UAC Speaker Output Example
This example demonstrates how to output audio to a USB speaker or USB audio device using the USB Audio Class (UAC) protocol. It shows audio format configuration, writing audio data, and controlling speaker settings.What This Example Demonstrates
- Initializing USB audio output (speaker)
- Configuring audio format (sample rate, bit depth, channels)
- Writing audio data to the speaker
- Controlling speaker volume and mute
- Managing audio buffers for continuous playback
Hardware Setup
Required Components:- ESP32-S2 or ESP32-S3 development board
- USB speaker or USB audio interface (UAC-compatible)
- USB OTG cable or adapter
- Connect USB speaker to ESP32’s USB port
- Connect ESP32 to computer via serial for monitoring
- Ensure adequate power supply (some speakers need external power)
Complete Code
Code Explanation
1. Speaker Callback Function
static void onSpeakerFrameCallback(spk_frame_t *frame, void *ptr)- Called when speaker needs audio dataframe->bit_resolution- Audio bit depth (8, 16, 24, or 32 bits)frame->samples_frequence- Sample rate in Hz (e.g., 16000, 44100, 48000)frame->data_bytes- Number of bytes the buffer can holdframe->data- Pointer where you write audio samples
frame->data. For microphones, you READ from it.
2. Generating Audio Data
(int16_t *)frame->data- Cast buffer to 16-bit signed integersnumSamples = data_bytes / 2- Calculate how many samples to generatesin(phase) * 10000- Generate sine wave with amplitude 10000 (out of 32767 max)phase += ...- Advance phase by one sample period at target frequencyif (phase >= 2π)- Wrap phase to prevent overflow
- Max 16-bit value: 32767
- Amplitude 10000 ≈ 30% volume
- Increase for louder sound (but avoid clipping above 32767)
3. UAC Configuration
UAC_CH_ANY- Accept any channel count (mono/stereo)UAC_BITS_ANY- Accept any bit depth (typically 16-bit)UAC_FREQUENCY_ANY- Accept any sample rate (negotiated with device)6400- Output buffer size in bytes
- 6400 bytes = 3200 samples (16-bit)
- At 16kHz: 3200 samples = 200ms of audio
- At 48kHz: 6400 bytes = ~66ms of audio
- Larger buffers = more latency but smoother playback
4. Callback Registration
&onSpeakerFrameCallback- Function pointer to your audio generatorNULL- Optional user data (can pass state variables)
5. Volume and Mute Control
- Range: 0-100 (percentage)
- Applied by USB audio device hardware
- Independent of amplitude in your generated audio
- Not all USB speakers support this command
1= muted (callback still runs, but output silenced)0= unmuted (normal audio output)- Useful for temporary silence without stopping generation
6. Suspend and Resume
- Power saving when audio not needed
- Switching between audio sources
- Temporary pause without reconfiguration
- Callbacks stop during suspend, resume when resumed
Expected Serial Output
Audio Generation Examples
1. Play Tone at Specific Frequency
2. Play Audio from SD Card
3. Play Audio from Memory (Flash)
4. Generate White Noise
5. Play Multiple Tones (Chord)
6. Receive Audio Over WiFi and Play
Stereo Audio Output
For stereo speakers, interleave left and right channels:Performance Considerations
Callback Timing:- Callback must complete before buffer deadline
- Keep processing lightweight
- Pre-compute data when possible
- Avoid memory allocation in callback
- Occur when callback doesn’t fill buffer in time
- Causes audio glitches/clicks
- Solution: Increase buffer size or simplify callback
- Don’t allocate memory in callback
- Use ring buffers for streaming data
- Pre-allocate all buffers in setup()
Troubleshooting
No sound output:- Check USB speaker connection
- Verify speaker is UAC-compatible
- Check speaker power and volume knob
- Ensure callback is writing data to frame->data
- Try different USB cable/port
- Reduce amplitude (avoid clipping above 32767)
- Increase buffer size
- Simplify callback processing
- Check for buffer underruns
- Verify
uacSpkRegisterCb()was called - Check
connectWait()succeeded - Ensure stream not suspended
- Try restarting ESP32
- Not all USB speakers support UAC volume control
- Some speakers have hardware volume only
- Try controlling amplitude in code instead
Related Examples
UAC Microphone
Capture audio from USB microphones
Combined Streaming
Use speaker, microphone, and camera together