Skip to main content

Overview

The UAC speaker methods allow you to send audio data to USB audio devices connected to your ESP32.

Methods

uacWriteSpk()

Writes audio data to the speaker buffer, which will be sent to the USB device when ready.

Syntax

void uacWriteSpk(uint16_t *buffer, size_t data_bytes, size_t timeout_ms)

Parameters

  • buffer (uint16_t*) - Pointer to the audio data to be written
  • data_bytes (size_t) - Size of the data to be written in bytes
  • timeout_ms (size_t) - Timeout value for writing data in milliseconds

Example

// Prepare audio data (16-bit samples)
uint16_t audio_samples[1024];

// Generate a simple tone or load audio data
for (int i = 0; i < 1024; i++) {
    audio_samples[i] = (uint16_t)(sin(2 * PI * 440 * i / 48000) * 32767);
}

// Write to speaker with 1 second timeout
usb->uacWriteSpk(audio_samples, sizeof(audio_samples), 1000);

uacSpkSuspend()

Suspends USB speaker streaming.

Syntax

void uacSpkSuspend(void *ctrl_value)

Parameters

  • ctrl_value (void*) - Control value (typically NULL)

Example

usb->uacSpkSuspend(NULL);

uacSpkResume()

Resumes USB speaker streaming.

Syntax

void uacSpkResume(void *ctrl_value)

Parameters

  • ctrl_value (void*) - Control value (typically NULL)

Example

usb->uacSpkResume(NULL);

uacSpkMute()

Mutes the USB speaker.

Syntax

void uacSpkMute(void *ctrl_value)

Parameters

  • ctrl_value (void*) - Mute control value (0 = unmute, non-zero = mute)

Example

// Mute speaker
usb->uacSpkMute((void *)1);

// Unmute speaker
usb->uacSpkMute((void *)0);

uacSpkVolume()

Controls the speaker volume.

Syntax

void uacSpkVolume(void *ctrl_value)

Parameters

  • ctrl_value (void*) - Volume level value (device-specific range)

Example

// Set speaker volume to 80
usb->uacSpkVolume((void *)80);

uacSpkGetFrameSize()

Gets the frame size list of the currently connected speaker.

Syntax

uac_frame_size_t* uacSpkGetFrameSize(uac_frame_size_t *frame_size)

Parameters

  • frame_size (uac_frame_size_t*) - Pointer to store the frame size information

Returns

Pointer to the frame size structure, or NULL on error.

uac_frame_size_t Structure

typedef struct {
    uint8_t ch_num;                 // Channel numbers
    uint16_t bit_resolution;        // Bit resolution in buffer
    uint32_t samples_frequence;     // Sample frequency
    uint32_t samples_frequence_min; // Minimum sample frequency
    uint32_t samples_frequence_max; // Maximum sample frequency
} uac_frame_size_t;

Example

uac_frame_size_t frame_info;
if (usb->uacSpkGetFrameSize(&frame_info) != NULL) {
    Serial.printf("Speaker format: %d ch, %d bits, %u Hz\n",
        frame_info.ch_num,
        frame_info.bit_resolution,
        frame_info.samples_frequence);
}

uacSpkGetFrameListSize()

Gets the frame list size of the currently connected speaker.

Syntax

void uacSpkGetFrameListSize(size_t *frame_size, size_t *frame_index)

Parameters

  • frame_size (size_t*) - Pointer to store the frame list size
  • frame_index (size_t*) - Pointer to store the current frame index

Example

size_t list_size = 0;
size_t current_index = 0;

usb->uacSpkGetFrameListSize(&list_size, &current_index);
Serial.printf("Frame list size: %d, current index: %d\n", list_size, current_index);

uacSpkFrameReset()

Resets speaker audio channel number, bit resolution, and sample frequency. Call this when streaming is in suspend state. The new configuration becomes effective after streaming resumes.

Syntax

void uacSpkFrameReset(uint8_t ch_num, uint16_t bit_resolution, uint32_t samples_frequency)

Parameters

  • ch_num (uint8_t) - Audio channel numbers (1-2)
  • bit_resolution (uint16_t) - Audio bit resolution (8-24)
  • samples_frequency (uint32_t) - Audio sample frequency in Hz (1000-48000)

Example

// Suspend speaker first
usb->uacSpkSuspend(NULL);

// Reset to mono, 16-bit, 44.1kHz
usb->uacSpkFrameReset(1, 16, 44100);

// Resume with new settings
usb->uacSpkResume(NULL);

Complete Speaker Example

#include <Arduino.h>
#include "USB_STREAM.h"

// Generate a sine wave tone
void generateTone(uint16_t *buffer, size_t samples, float frequency, float sample_rate) {
    for (size_t i = 0; i < samples; i++) {
        float value = sin(2.0 * PI * frequency * i / sample_rate);
        buffer[i] = (uint16_t)((value + 1.0) * 32767.5);
    }
}

void setup() {
    Serial.begin(115200);
    
    // Create USB_STREAM instance
    USB_STREAM *usb = new USB_STREAM();
    
    // Configure speaker (accept any format, 6400 byte buffer)
    usb->uacConfiguration(
        UAC_CH_ANY,          // No microphone
        UAC_BITS_ANY,
        UAC_FREQUENCY_ANY,
        0,                   // No mic buffer
        UAC_CH_ANY,          // Accept any speaker channels
        UAC_BITS_ANY,        // Accept any speaker bit resolution
        UAC_FREQUENCY_ANY,   // Accept any speaker frequency
        6400                 // 6400 byte speaker buffer
    );
    
    // Start USB streaming
    usb->start();
    
    // Wait for device connection
    usb->connectWait(1000);
    delay(1000);
    
    // Get speaker frame information
    uac_frame_size_t frame_info;
    if (usb->uacSpkGetFrameSize(&frame_info) != NULL) {
        Serial.printf("Speaker format: %d channels, %d bits, %u Hz\n",
            frame_info.ch_num,
            frame_info.bit_resolution,
            frame_info.samples_frequence);
    }
    
    // Unmute speaker
    usb->uacSpkMute((void *)0);
    
    // Set volume to 80
    usb->uacSpkVolume((void *)80);
    
    Serial.println("Speaker ready!");
}

void loop() {
    USB_STREAM *usb = USB_STREAM::getInstance(); // Get instance
    
    // Prepare audio buffer
    const size_t buffer_size = 1024;
    uint16_t audio_buffer[buffer_size];
    
    // Generate 440 Hz tone (A4 note) at 48kHz sample rate
    generateTone(audio_buffer, buffer_size, 440.0, 48000.0);
    
    // Write to speaker with 1 second timeout
    usb->uacWriteSpk(audio_buffer, sizeof(audio_buffer), 1000);
    
    delay(10); // Small delay between writes
}

Advanced Speaker Example

#include <Arduino.h>
#include "USB_STREAM.h"

USB_STREAM *usb;
bool speaker_running = true;

void setup() {
    Serial.begin(115200);
    
    usb = new USB_STREAM();
    
    // Configure for stereo, 16-bit, 48kHz speaker
    usb->uacConfiguration(
        UAC_CH_ANY, UAC_BITS_ANY, UAC_FREQUENCY_ANY, 0,  // No mic
        2, 16, 48000, 8192                                // Stereo speaker
    );
    
    usb->start();
    usb->connectWait(1000);
    
    // Get actual speaker configuration
    size_t list_size = 0;
    size_t current_index = 0;
    usb->uacSpkGetFrameListSize(&list_size, &current_index);
    Serial.printf("Speaker frame list size: %d, index: %d\n", list_size, current_index);
    
    uac_frame_size_t frame_info;
    if (usb->uacSpkGetFrameSize(&frame_info) != NULL) {
        Serial.printf("Active format: %d ch, %d bits, %u Hz (range: %u-%u Hz)\n",
            frame_info.ch_num,
            frame_info.bit_resolution,
            frame_info.samples_frequence,
            frame_info.samples_frequence_min,
            frame_info.samples_frequence_max);
    }
    
    // Unmute and set volume
    usb->uacSpkMute((void *)0);
    usb->uacSpkVolume((void *)75);
}

void loop() {
    if (speaker_running) {
        // Create stereo audio (left and right channels)
        const size_t samples_per_channel = 512;
        uint16_t stereo_buffer[samples_per_channel * 2];
        
        for (size_t i = 0; i < samples_per_channel; i++) {
            // Left channel: 440 Hz (A4)
            float left = sin(2.0 * PI * 440.0 * i / 48000.0);
            stereo_buffer[i * 2] = (uint16_t)((left + 1.0) * 32767.5);
            
            // Right channel: 554.37 Hz (C#5)
            float right = sin(2.0 * PI * 554.37 * i / 48000.0);
            stereo_buffer[i * 2 + 1] = (uint16_t)((right + 1.0) * 32767.5);
        }
        
        // Write stereo audio
        usb->uacWriteSpk(stereo_buffer, sizeof(stereo_buffer), 500);
    }
    
    // Check for serial commands
    if (Serial.available()) {
        char cmd = Serial.read();
        
        switch (cmd) {
            case 'p': // Pause
                usb->uacSpkSuspend(NULL);
                speaker_running = false;
                Serial.println("Speaker paused");
                break;
                
            case 'r': // Resume
                usb->uacSpkResume(NULL);
                speaker_running = true;
                Serial.println("Speaker resumed");
                break;
                
            case 'm': // Mute
                usb->uacSpkMute((void *)1);
                Serial.println("Speaker muted");
                break;
                
            case 'u': // Unmute
                usb->uacSpkMute((void *)0);
                Serial.println("Speaker unmuted");
                break;
                
            case 's': // Switch to mono 44.1kHz
                usb->uacSpkSuspend(NULL);
                usb->uacSpkFrameReset(1, 16, 44100);
                usb->uacSpkResume(NULL);
                Serial.println("Switched to mono 44.1kHz");
                break;
        }
    }
    
    delay(10);
}

Notes

  • The speaker buffer must contain 16-bit audio samples (uint16_t*).
  • For stereo audio, interleave left and right channel samples (L, R, L, R, …).
  • Ensure the data_bytes parameter matches the actual buffer size in bytes.
  • Use appropriate timeout values to prevent blocking if the USB device is slow.
  • Frame reset must be performed while streaming is suspended.
  • The volume and mute control values may be device-specific. Check your USB audio device documentation.
  • Buffer underruns may cause audio glitches. Adjust buffer sizes and write frequency accordingly.

Build docs developers (and LLMs) love