summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/class/audio/audio_host.c11
-rw-r--r--src/class/audio/audio_host.h4
-rw-r--r--test/unit-test/test/host/audio/test_audio_host.c16
3 files changed, 29 insertions, 2 deletions
diff --git a/src/class/audio/audio_host.c b/src/class/audio/audio_host.c
index ad2fd677f..c73e9f225 100644
--- a/src/class/audio/audio_host.c
+++ b/src/class/audio/audio_host.c
@@ -2592,7 +2592,16 @@ bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, int16_t volume, tuh_x
uintptr_t user_data) {
tuh_audio_volume_range_t range;
TU_VERIFY(tuh_audio_volume_range_get(idx, stream_idx, &range), false);
- TU_VERIFY(volume >= range.min && volume <= range.max, false);
+ if (volume != TUH_AUDIO_VOLUME_SILENCE) {
+ TU_VERIFY(volume >= range.min && volume <= range.max && range.res != 0, false);
+ const uint32_t offset = (uint32_t)((int32_t)volume - range.min);
+ const uint32_t steps = (offset + range.res / 2u) / range.res;
+ int32_t rounded = (int32_t)range.min + (int32_t)(steps * range.res);
+ if (rounded > range.max) {
+ rounded -= range.res;
+ }
+ volume = (int16_t)rounded;
+ }
return tuh_audio_feature_unit_set(idx, stream_idx, AUDIO10_FU_CTRL_VOLUME, 0, (uint16_t)volume, complete_cb,
user_data);
}
diff --git a/src/class/audio/audio_host.h b/src/class/audio/audio_host.h
index 42cf4b4e5..4807d376a 100644
--- a/src/class/audio/audio_host.h
+++ b/src/class/audio/audio_host.h
@@ -241,7 +241,9 @@ bool tuh_audio_feature_unit_get(uint8_t idx, uint8_t stream_idx, uint8_t control
uint16_t *value, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
// Master mute and volume controls. Capability and range information is cached
-// before tuh_audio_mount_cb() is invoked.
+// before tuh_audio_mount_cb() is invoked. Volume SET accepts
+// TUH_AUDIO_VOLUME_SILENCE or a value within the cached range; finite values
+// are rounded to the nearest resolution step measured from the range minimum.
bool tuh_audio_mute_set(uint8_t idx, uint8_t stream_idx, bool mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool tuh_audio_mute_get(uint8_t idx, uint8_t stream_idx, bool *mute, tuh_xfer_cb_t complete_cb, uintptr_t user_data);
bool tuh_audio_volume_set(uint8_t idx, uint8_t stream_idx, int16_t volume, tuh_xfer_cb_t complete_cb,
diff --git a/test/unit-test/test/host/audio/test_audio_host.c b/test/unit-test/test/host/audio/test_audio_host.c
index 55b5e8ef6..aa2f70f9b 100644
--- a/test/unit-test/test/host/audio/test_audio_host.c
+++ b/test/unit-test/test/host/audio/test_audio_host.c
@@ -1536,6 +1536,22 @@ void test_audio_host_typed_mute_and_volume_controls(void) {
TEST_ASSERT_EQUAL_HEX32(0x4567, fu_cb_user_data);
}
+void test_audio_host_volume_set_accepts_silence_and_rounds_unaligned_values(void) {
+ tuh_audio_volume_range_t range;
+ mount_descriptors(playback_fu_before_terminal, sizeof(playback_fu_before_terminal));
+ TEST_ASSERT_TRUE(tuh_audio_volume_range_get(0, 0, &range));
+
+ TEST_ASSERT_TRUE(tuh_audio_volume_set(0, 0, (int16_t)(-6 * 256 + 100), feature_unit_complete, 0));
+ TEST_ASSERT_EQUAL_UINT8(1, control_xfer_count);
+ TEST_ASSERT_EQUAL_HEX8_ARRAY(((uint8_t[]){0x00, 0xFA}), control_buffer, 2);
+ complete_control_xfer(XFER_RESULT_SUCCESS);
+
+ TEST_ASSERT_TRUE(tuh_audio_volume_set(0, 0, TUH_AUDIO_VOLUME_SILENCE, feature_unit_complete, 0));
+ TEST_ASSERT_EQUAL_UINT8(2, control_xfer_count);
+ TEST_ASSERT_EQUAL_HEX8_ARRAY(((uint8_t[]){0x00, 0x80}), control_buffer, 2);
+ complete_control_xfer(XFER_RESULT_SUCCESS);
+}
+
void test_audio_host_schedules_44100_hz_fractional_packets_with_max_packets_only(void) {
uint8_t samples[441 * 4] = {0};
mount_descriptors(playback_44100_max_packets_only, sizeof(playback_44100_max_packets_only));