USB Audio Host Example¶
Example source:
examples/host/audio_host
This example demonstrates how to use TinyUSB’s USB Audio Host driver (TUH_AUDIO) to capture audio from a UAC 1.0 or UAC 2.0 USB microphone and echo it back to the speaker, using a WASAPI/ALSA-like high-level API. The application never touches USB interfaces, alternate settings, or endpoint addresses — it only selects supported {format, sample_rate, channels} configurations by stream index.
Features¶
Enumerates and mounts USB Audio Class 1.0 and 2.0 devices
Discovers the device’s logical streams (capture/playback) and their supported configurations (discrete tuples only)
Reports each stream’s mute/volume capabilities and cached volume range
Configures and starts an S16_LE capture stream (44.1 kHz preferred, 48 kHz fallback; stereo preferred, mono accepted)
Echoes captured audio to an S16_LE playback stream at the same sample rate (same channel count preferred, mono/stereo conversion otherwise)
Frame-based FIFO API: the main loop reads capture when its FIFO is half full and fills playback when its FIFO is half drained; USB transfer callbacks are not used for FIFO servicing
Cycles the streams through three phases (5 s each): mic-only (capture, data dropped), spk-only (sine test tone), and echo (capture looped back to playback)
Supported Devices¶
This example supports UAC1 devices whose Type I Format descriptor lists discrete sampling frequencies (bSamFreqType > 0) and UAC2 devices using a directly connected Clock Source, such as:
USB microphones
USB headsets (mono microphone + speaker)
USB audio interfaces
The echo needs a matching S16_LE playback stream at the capture sample rate; devices without one run capture-only. The sample rate and channel preferences are configured by the SAMPLE_RATES / AUDIO_MAX_CHANNELS macros in src/audio_app.c (44.1 kHz stereo by default). Non-PCM formats are rejected by the driver.
Limitations and trade-offs¶
Explicit feedback endpoints are supported with both 10.14 and 16.16 feedback values. An implicit-feedback IN endpoint is treated as an ordinary audio-data endpoint and is not used to pace playback.
UAC1 Type I Format descriptors with
bSamFreqType == 0are unsupported; the driver requires a list of discrete sampling frequencies.UAC2 supports direct Clock Sources. Clock Selectors, Clock Multipliers, Sampling Rate Converters, Clock Validity, and Valid Alternate Settings controls are not handled.
UAC2 sampling-frequency RANGE responses are expanded into at most
CFG_TUH_AUDIO_MAX_SAM_FREQdiscrete configurations. A read-only Clock Source exposes only its current frequency.Master mute and volume controls are discovered before the mount callback. A Feature Unit with volume only on its logical channels is also supported: the range is read from the first controlled channel, and a stream-volume SET writes every logical channel when no writable master control exists. The typed API assumes all logical channels share one range; applications needing different per-channel ranges can use the raw control API. UAC2 volume discovery supports the common RANGE response containing one subrange.
The
MaxPacketsOnlyendpoint attribute is not supported. OUT transfers are not padded towMaxPacketSize, and padding in IN transfers is not removed from the reported audio data.
Building¶
Using CMake (recommended)¶
cd examples/host/audio_host
mkdir -p build && cd build
cmake -DBOARD=<your_board> -G Ninja ..
cmake --build .
Replace <your_board> with your target board name (e.g., raspberry_pi_pico, stm32f407disco, etc.)
Using Make¶
cd examples/host/audio_host
make BOARD=<your_board> all
Flashing¶
# Using CMake: list the board-specific flash targets, then select one
ninja -t targets
ninja audio_host-jlink # example for a board with J-Link support
# Using Make
make BOARD=<your_board> flash
Usage¶
Build and flash the example to your board
Connect a USB Audio device (UAC 1.0 or 2.0) to the USB host port
Open a serial terminal to view output
The example will:
Print each stream’s mute/volume capabilities, cached volume range, and supported configurations when mounted
Look for an S16_LE capture configuration at a preferred sample rate (44.1 kHz first, 48 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 stream volumes near -6 dB; a channel-only Feature Unit is updated one logical channel at a time
Service both FIFOs from
audio_app_task()at their half-full/half-drained watermarks; a sine test tone plays on the playback stream when no capture stream is echoingCycle through the three phases (mic-only / spk-only / echo, 5 s each) with
tuh_audio_start()/tuh_audio_stop(); their asynchronous results are printed fromtuh_audio_event_cb(), and a failed stream is restarted automatically after 100 ms
Serial Output Example¶
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, configurations: 2
master mute supported
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, configurations: 2
master mute supported
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 44100 S16_LE capture (2 channels)
Microphone configured
Microphone master mute: off
Microphone master volume: 0 (1/256 dB)
Microphone volume set: -1536 (1/256 dB)
Configuring 44100 S16_LE playback (2 channels)
Speaker configured
Speaker master mute: off
Speaker master volume: 0 (1/256 dB)
Speaker volume set: -1536 (1/256 dB)
Configuration¶
Edit src/tusb_config.h to modify:
CFG_TUH_AUDIO_MAX: Maximum number of audio devices supportedCFG_TUH_AUDIO_PROTOCOLS: Bitmask selecting UAC1 and/or UAC2 support; the example enables bothCFG_TUH_AUDIO_MAX_SAM_FREQ: Maximum number of discrete frequencies retained per alternate setting or UAC2 Clock SourceCFG_TUH_AUDIO_EPIN_BUFSIZE: Maximum size of one capture transfer the driver submits (configurations needing a larger per-poll-interval packet are rejected)CFG_TUH_AUDIO_EPOUT_BUFSIZE: Maximum size of one playback transfer the driver submitsCFG_TUH_AUDIO_STREAM_BUFSIZE: Per-stream FIFO depth in bytes (default 1024, i.e. four 256 B packets); capture overwrites the oldest frames when full
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 usetuh_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()only count completed transfers;audio_app_task()services the FIFOs independently from the main loop.tuh_audio_event_cb()reports asynchronous start/stop results and unrecoverable transfer failures. The example restarts a failed stream automatically 100 ms later.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 read/queued.tuh_audio_read_available()reports captured frames ready to read;tuh_audio_write_available()reports free playback capacity.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.Isochronous transfers require the host to poll
tuh_task()continuously; the capture FIFO absorbs short scheduling gaps and overwrites the oldest frames when full.