summaryrefslogtreecommitdiff
path: root/examples/host
diff options
context:
space:
mode:
authorHiFiPHile <[email protected]>2026-08-27 06:21:18 +0200
committerHiFiPHile <[email protected]>2026-08-27 10:34:34 +0200
commitb23e33a1ce07297df2f37219861d78d2a4cc9809 (patch)
tree0e4d46110d8e82ef28e9836e3a18e5c622b15430 /examples/host
parentfa6283249362278b887364ae51f9d866fcb96ad3 (diff)
audio: expose generic Audio Control requests
Remove the narrow public Feature Unit request API while retaining managed mute and volume helpers. Expose validated Audio Control descriptors during enumeration and provide raw asynchronous and synchronous entity requests for advanced controls. Signed-off-by: HiFiPHile <[email protected]>
Diffstat (limited to 'examples/host')
-rw-r--r--examples/host/audio_host/README.md15
-rw-r--r--examples/host/audio_host/src/audio_app.c24
2 files changed, 20 insertions, 19 deletions
diff --git a/examples/host/audio_host/README.md b/examples/host/audio_host/README.md
index 326b469e8..e7140bf67 100644
--- a/examples/host/audio_host/README.md
+++ b/examples/host/audio_host/README.md
@@ -68,7 +68,7 @@ make BOARD=<your_board> flash
2. Connect a USB Audio device (UAC 1.0 or 2.0) to the USB host port
3. Open a serial terminal to view output
4. The example will:
- - Print each stream's Feature Unit ID, master mute/volume capabilities, cached volume range, and supported configurations when mounted
+ - Print each stream's master mute/volume capabilities, cached volume range, and supported configurations when mounted
- Look for an S16_LE capture configuration at a preferred sample rate (48 kHz first, 44.1 kHz fallback; stereo preferred, mono accepted) and configure it
- Echo captured audio to an S16_LE playback configuration at the same sample rate (same channel count preferred, converted otherwise)
- Read/unmute the microphone and speaker Feature Units and set supported master volumes near -6 dB
@@ -81,25 +81,25 @@ make BOARD=<your_board> flash
TinyUSB Host USB Audio Example
Connect a USB Audio Device (UAC 1.0 or 2.0) to test
Audio device mounted: idx=0 addr=1
- capture stream 1 Feature Unit ID: 5, configurations: 2
+ capture stream 1, configurations: 2
master mute supported
master volume range: min=-23040 max=1536 res=256 (1/256 dB)
[0] format=1 rate=44100 channels=2
[1] format=1 rate=48000 channels=2
- playback stream 0 Feature Unit ID: 2, configurations: 2
+ playback stream 0, configurations: 2
master mute supported
master volume range: min=-23040 max=1536 res=256 (1/256 dB)
[0] format=1 rate=44100 channels=2
[1] format=1 rate=48000 channels=2
Configuring 48 kHz S16_LE capture (2 channels)
Microphone configured
- Microphone Feature Unit 5 master mute: off
- Microphone Feature Unit 5 master volume: 0 (1/256 dB)
+ Microphone master mute: off
+ Microphone master volume: 0 (1/256 dB)
Microphone master volume set: -1536 (1/256 dB)
Configuring 48 kHz S16_LE playback (2 channels)
Speaker configured
- Speaker Feature Unit 2 master mute: off
- Speaker Feature Unit 2 master volume: 0 (1/256 dB)
+ Speaker master mute: off
+ Speaker master volume: 0 (1/256 dB)
Speaker master volume set: -1536 (1/256 dB)
```
@@ -115,6 +115,7 @@ Edit `src/tusb_config.h` to modify:
## Notes
+- `tuh_audio_descriptor_cb()` exposes the validated Audio Control descriptor block during enumeration. Applications that need raw entity controls must copy the required entity IDs or descriptor fields before the callback returns, then use `tuh_audio_control_xfer()` after the device mounts.
- While a stream is running, the driver keeps one isochronous transfer in flight and re-submits on completion, so transfers follow the endpoint's `bInterval`. `tuh_audio_capture_cb()` / `tuh_audio_playback_cb()` report each completed transfer; `tuh_audio_err_cb()` reports failures. The example restarts the failed stream automatically 100 ms after the error callback.
- Capture and playback streams running concurrently in the same Audio Control instance must use the same sample rate.
- `tuh_audio_read()` / `tuh_audio_write()` are non-blocking FIFO operations: they return the number of whole frames actually queued/read (0 when the FIFO is empty/full or the stream is not running), and `tuh_audio_read_available()` / `tuh_audio_write_available()` report the FIFO occupancy in frames. `tuh_audio_write()` only queues data; the playback transfer-completion chain sends it, or sends silence when the FIFO does not contain a complete polling interval without consuming the partial data.
diff --git a/examples/host/audio_host/src/audio_app.c b/examples/host/audio_host/src/audio_app.c
index 4623dff5a..8852ca58c 100644
--- a/examples/host/audio_host/src/audio_app.c
+++ b/examples/host/audio_host/src/audio_app.c
@@ -360,11 +360,9 @@ void tuh_audio_err_cb(uint8_t idx, uint8_t stream_idx, uint16_t xferred_bytes) {
// Print all supported stream configurations
static void print_stream_configs(uint8_t idx, uint8_t stream_idx) {
- const tuh_audio_direction_t dir = tuh_audio_stream_direction(idx, stream_idx);
- const char *dir_name = (dir == TUH_AUDIO_STREAM_CAPTURE) ? "capture" : "playback";
- const uint8_t feature_unit_id = tuh_audio_get_feature_unit_id(idx, stream_idx);
- printf(" %s stream %u Feature Unit ID: %u, configurations: %u\r\n", dir_name, stream_idx, feature_unit_id,
- tuh_audio_config_count(idx, stream_idx));
+ const tuh_audio_direction_t dir = tuh_audio_stream_direction(idx, stream_idx);
+ const char *dir_name = (dir == TUH_AUDIO_STREAM_CAPTURE) ? "capture" : "playback";
+ printf(" %s stream %u, configurations: %u\r\n", dir_name, stream_idx, tuh_audio_config_count(idx, stream_idx));
tuh_audio_volume_range_t range;
if (tuh_audio_mute_supported(idx, stream_idx)) {
printf(" master mute supported\r\n");
@@ -383,17 +381,14 @@ static void print_stream_configs(uint8_t idx, uint8_t stream_idx) {
}
static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const char *stream_name) {
- const uint8_t feature_unit_id = tuh_audio_get_feature_unit_id(idx, stream_idx);
- if (feature_unit_id == 0) {
- printf(" %s stream has no master mute/volume Feature Unit\r\n", stream_name);
- return;
- }
+ bool has_control = false;
if (tuh_audio_mute_supported(idx, stream_idx)) {
+ has_control = true;
bool mute;
tusb_xfer_result_t result = tuh_audio_mute_get_sync(idx, stream_idx, &mute);
if (result == XFER_RESULT_SUCCESS) {
- printf(" %s Feature Unit %u master mute: %s\r\n", stream_name, feature_unit_id, mute ? "on" : "off");
+ printf(" %s master mute: %s\r\n", stream_name, mute ? "on" : "off");
result = tuh_audio_mute_set_sync(idx, stream_idx, false);
}
if (result != XFER_RESULT_SUCCESS) {
@@ -403,10 +398,11 @@ static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const cha
tuh_audio_volume_range_t range;
if (tuh_audio_volume_range_get(idx, stream_idx, &range)) {
+ has_control = true;
int16_t volume;
tusb_xfer_result_t result = tuh_audio_volume_get_sync(idx, stream_idx, &volume);
if (result == XFER_RESULT_SUCCESS) {
- printf(" %s Feature Unit %u master volume: %d (1/256 dB)\r\n", stream_name, feature_unit_id, volume);
+ printf(" %s master volume: %d (1/256 dB)\r\n", stream_name, volume);
int32_t target = FEATURE_UNIT_VOLUME_DB;
target = TU_MAX(target, range.min);
target = TU_MIN(target, range.max);
@@ -421,6 +417,10 @@ static void configure_stream_controls(uint8_t idx, uint8_t stream_idx, const cha
printf(" Accessing %s master volume failed: result=%u\r\n", stream_name, result);
}
}
+
+ if (!has_control) {
+ printf(" %s stream has no master mute/volume control\r\n", stream_name);
+ }
}
// Invoked when device with Audio interface is un-mounted