Overview
This guide covers USB Audio Class (UAC) configuration for microphone input and speaker output. The library supports simultaneous operation of microphone and speaker streams with independent control of volume, mute, and audio parameters.
Audio Configuration Parameters
UAC streams are configured with three primary parameters:
Channel Number : Mono (1) or stereo (2)
Bit Resolution : 8, 16, 24, or 32 bits per sample
Sample Frequency : Common rates include 8000, 16000, 44100, 48000 Hz
Using Auto-Detection
Use wildcard values to auto-detect device capabilities:
UAC_CH_ANY // Accept any channel number (0)
UAC_BITS_ANY // Accept any bit resolution (__UINT16_MAX__)
UAC_FREQUENCY_ANY // Accept any sample frequency (__UINT32_MAX__)
Microphone Configuration
Configure Microphone Parameters
USB_STREAM * usb = new USB_STREAM ();
usb -> uacConfiguration (
1 , // mic_ch_num - mono
16 , // mic_bit_resolution - 16 bits
16000 , // mic_samples_frequency - 16 kHz
6400 , // mic_buf_size - buffer size in bytes
0 , // spk_ch_num - 0 to disable speaker
0 , // spk_bit_resolution
0 , // spk_samples_frequency
0 // spk_buf_size
);
Mic buffer size should accommodate at least 100-200ms of audio data. Calculate as:
buffer_size = (sample_rate / 1000) * (bit_resolution / 8) * channels * duration_ms
Register Microphone Callback (Optional)
For real-time processing, register a callback: void mic_callback ( mic_frame_t * frame , void * user_ptr ) {
ESP_LOGV (TAG, "Mic data: %u bytes, %u Hz, %u bits" ,
frame -> data_bytes ,
frame -> samples_frequence ,
frame -> bit_resolution );
// Process audio in frame->data
// WARNING: Do NOT block in this callback!
}
usb -> uacMicRegisterCb ( & mic_callback, NULL );
The callback runs in the USB task context. Never block! For heavy processing, queue data to another task.
Start and Resume Microphone
usb -> start ();
usb -> connectWait ( 10000 );
// If using FLAG_UAC_MIC_SUSPEND_AFTER_START, resume manually:
usb -> uacMicResume ( NULL );
Reading Microphone Data
If not using a callback, read from the internal buffer:
#define BUFFER_SIZE (96 * 20) // Adjust based on sample rate
uint8_t * audio_buffer = ( uint8_t * ) malloc (BUFFER_SIZE);
size_t bytes_read = 0 ;
// Read with timeout
usb -> uacReadMic (audio_buffer, BUFFER_SIZE, & bytes_read, 1000 );
if (bytes_read > 0 ) {
ESP_LOGI (TAG, "Read %u bytes from microphone" , bytes_read);
// Process audio_buffer
}
free (audio_buffer);
The read operation blocks until data is available or timeout occurs. Use portMAX_DELAY to wait indefinitely.
Speaker Configuration
Configure Speaker Parameters
USB_STREAM * usb = new USB_STREAM ();
usb -> uacConfiguration (
0 , // mic_ch_num - 0 to disable mic
0 , // mic_bit_resolution
0 , // mic_samples_frequency
0 , // mic_buf_size
1 , // spk_ch_num - mono
16 , // spk_bit_resolution - 16 bits
16000 , // spk_samples_frequency - 16 kHz
6400 // spk_buf_size - buffer size in bytes
);
Speaker buffer size should be a multiple of the endpoint max packet size (MPS). Larger buffers reduce underruns.
Start and Resume Speaker
usb -> start ();
usb -> connectWait ( 10000 );
// Resume if suspended
usb -> uacSpkResume ( NULL );
Write Audio Data
// Audio data buffer (16-bit samples)
uint16_t * audio_samples = get_audio_data ();
size_t data_bytes = 320 ; // Number of bytes to write
usb -> uacWriteSpk (audio_samples, data_bytes, portMAX_DELAY);
Ensure continuous data supply to avoid audio stuttering. Use a buffer or queue to manage playback.
Simultaneous Microphone and Speaker
Configure Both Streams
USB_STREAM * usb = new USB_STREAM ();
usb -> uacConfiguration (
1 , // mic_ch_num
16 , // mic_bit_resolution
16000 , // mic_samples_frequency
3200 , // mic_buf_size
1 , // spk_ch_num
16 , // spk_bit_resolution
16000 , // spk_samples_frequency
6400 // spk_buf_size
);
Register Mic Callback for Audio Loop
Create an audio loop by forwarding mic data to speaker: void mic_to_speaker_callback ( mic_frame_t * frame , void * user_ptr ) {
// Forward mic input directly to speaker
uac_spk_streaming_write ( frame -> data , frame -> data_bytes , 0 );
}
usb -> uacMicRegisterCb ( & mic_to_speaker_callback, ( void * ) 1 );
Start Both Streams
usb -> start ();
usb -> connectWait ( 10000 );
// Both streams are now active
Volume and Mute Control
Volume Control
Set volume level (0-100):
// Microphone volume
uint32_t mic_volume = 50 ; // 50%
usb -> uacMicVolume ( & mic_volume);
// Speaker volume
uint32_t spk_volume = 75 ; // 75%
usb -> uacSpkVolume ( & spk_volume);
Volume control depends on device support. Some devices may not support this feature.
Mute Control
Mute or unmute audio streams:
// Mute microphone
uint32_t mute = 1 ; // 1 = mute, 0 = unmute
usb -> uacMicMute ( & mute);
// Unmute speaker
uint32_t unmute = 0 ;
usb -> uacSpkMute ( & unmute);
Suspend and Resume
Suspend Individual Streams
// Suspend microphone
usb -> uacMicSuspend ( NULL );
// Suspend speaker
usb -> uacSpkSuspend ( NULL );
Change Configuration (Optional)
While suspended, modify audio parameters: // Change microphone to 44.1 kHz, stereo, 24-bit
usb -> uacMicFrameReset ( 2 , 24 , 44100 );
// Change speaker to 48 kHz, mono, 16-bit
usb -> uacSpkFrameReset ( 1 , 16 , 48000 );
Resume Streams
// Resume microphone
usb -> uacMicResume ( NULL );
// Resume speaker
usb -> uacSpkResume ( NULL );
New configurations take effect after resume.
Get supported audio formats from connected device:
// Get microphone format list
size_t mic_frame_size = 0 ;
size_t mic_frame_index = 0 ;
usb -> uacMicGetFrameListSize ( & mic_frame_size, & mic_frame_index);
uac_frame_size_t * mic_formats =
( uac_frame_size_t * ) malloc (mic_frame_size * sizeof ( uac_frame_size_t ));
usb -> uacMicGetFrameSize (mic_formats);
for ( size_t i = 0 ; i < mic_frame_size; i ++ ) {
ESP_LOGI (TAG, "Mic[ %u ]: %u ch, %u bits, %u Hz (range: %u - %u )" ,
i,
mic_formats [i]. ch_num ,
mic_formats [i]. bit_resolution ,
mic_formats [i]. samples_frequence ,
mic_formats [i]. samples_frequence_min ,
mic_formats [i]. samples_frequence_max );
}
free (mic_formats);
// Similar for speaker
size_t spk_frame_size = 0 ;
size_t spk_frame_index = 0 ;
usb -> uacSpkGetFrameListSize ( & spk_frame_size, & spk_frame_index);
uac_frame_size_t * spk_formats =
( uac_frame_size_t * ) malloc (spk_frame_size * sizeof ( uac_frame_size_t ));
usb -> uacSpkGetFrameSize (spk_formats);
// Process speaker formats...
free (spk_formats);
Complete Example: Audio Echo
#include "USB_STREAM.h"
static const char * TAG = "UAC_Echo" ;
USB_STREAM * usb_stream = nullptr ;
// Echo callback: forward mic to speaker
void echo_callback ( mic_frame_t * frame , void * user_ptr ) {
// Write mic data to speaker
uac_spk_streaming_write ( frame -> data , frame -> data_bytes , 0 );
}
void state_callback ( usb_stream_state_t event , void * arg ) {
if (event == STREAM_CONNECTED) {
ESP_LOGI (TAG, "Audio device connected" );
} else {
ESP_LOGI (TAG, "Audio device disconnected" );
}
}
void setup () {
Serial . begin ( 115200 );
usb_stream = new USB_STREAM ();
// Configure mic and speaker with auto-detection
usb_stream -> uacConfiguration (
UAC_CH_ANY, // mic channels
UAC_BITS_ANY, // mic bit resolution
UAC_FREQUENCY_ANY, // mic frequency
6400 , // mic buffer size
UAC_CH_ANY, // speaker channels
UAC_BITS_ANY, // speaker bit resolution
UAC_FREQUENCY_ANY, // speaker frequency
16000 // speaker buffer size
);
// Register callbacks
usb_stream -> uacMicRegisterCb ( & echo_callback, ( void * ) 1 );
usb_stream -> registerState ( & state_callback, NULL );
// Start streaming
usb_stream -> start ();
usb_stream -> connectWait ( 10000 );
// Set volume
uint32_t volume = 60 ;
usb_stream -> uacMicVolume ( & volume);
usb_stream -> uacSpkVolume ( & volume);
ESP_LOGI (TAG, "Audio echo started" );
}
void loop () {
delay ( 1000 );
}
Audio Buffer Size Recommendations
Sample Rate Bit Depth Channels Buffer Size (100ms) Buffer Size (200ms) 8000 Hz 16-bit 1 1,600 bytes 3,200 bytes 16000 Hz 16-bit 1 3,200 bytes 6,400 bytes 44100 Hz 16-bit 2 17,640 bytes 35,280 bytes 48000 Hz 16-bit 2 19,200 bytes 38,400 bytes
Formula : buffer_size = (sample_rate / 1000) * (bits / 8) * channels * duration_ms
Microphone vs Speaker Buffer Sizing
Microphone : Smaller buffers (100-200ms) for lower latency
Speaker : Larger buffers (200-500ms) to prevent underruns
Speaker buffer should be a multiple of endpoint MPS
Suspend After Start
Automatically suspend streams on startup:
src/original/include/usb_stream.h
// Using C API
uac_config_t uac_config = {
.mic_ch_num = UAC_CH_ANY,
.mic_bit_resolution = UAC_BITS_ANY,
.mic_samples_frequence = UAC_FREQUENCY_ANY,
.mic_buf_size = 6400 ,
// ... other config ...
.flags = FLAG_UAC_MIC_SUSPEND_AFTER_START | FLAG_UAC_SPK_SUSPEND_AFTER_START
};
Next Steps