Skip to content

Instantly share code, notes, and snippets.

@pschatzmann
Last active June 21, 2026 17:00
Show Gist options
  • Select an option

  • Save pschatzmann/806584b4af17b08f43b25a4da6bec1a6 to your computer and use it in GitHub Desktop.

Select an option

Save pschatzmann/806584b4af17b08f43b25a4da6bec1a6 to your computer and use it in GitHub Desktop.
USB Audio Testing Plan

USB Audio Testing Plan

Platform × Direction (core tests)

# Platform Mode Key settings Test
1 ESP32 TX defaults (enable_multi_sample_rate=false) Record 5s with arecord, play back, verify clean sine
2 ESP32 RX defaults Play test.wav with aplay, verify rxXfer > 0, MeasuringStream output
3 ESP32 RXTX enable_ep_in=true, enable_ep_out=true Record AND play simultaneously
4 RP2040 TX defaults Record with arecord, verify clean sine
5 RP2040 RX defaults Play with aplay, verify data received
6 RP2040 RXTX both directions Simultaneous record + play

Audio format variations

# Setting Values to test Verify
7 sample_rate 44100, 48000 Correct playback speed, frame matches rate
8 channels 1 (mono), 2 (stereo) lsusb shows correct bNrChannels, arecord -c N works
9 bits_per_sample 16, 24 lsusb shows correct bSubslotSize, arecord -f S16/S24_3LE

Feature flags

# Flag Test with true Test with false Verify
10 enable_feedback_ep TX only (no effect), RX with feedback RX without feedback lsusb shows/hides feedback EP, aplay works
11 enable_multi_sample_rate Host can change rate, 14 rates in GET_RANGE Fixed rate, single rate in GET_RANGE lsusb clock type (fixed vs programmable), no dmesg errors
12 enable_interrupt_ep AC interface has interrupt EP No interrupt EP lsusb shows/hides interrupt EP, device enumerates
13 enable_ep_in_flow_control Frame sizes vary (176/180 for 44100) Fixed frame size frame diagnostic matches expectation
14 volume_active processVolume scales audio Volume/mute info only, no processing Set volume to 50% via amixer, verify level change (true) or no change (false)

Composite device (CDC + Audio)

# Configuration Verify
15 ESP32: USBCDC USBSerial + Audio TX Both CDC serial and audio work, no EP conflicts
16 ESP32: USBCDC USBSerial + Audio RX Both CDC serial and audio work
17 RP2040: Audio only (no CDC) Enumeration, TX/RX work

Buffering

# Setting Values Verify
18 fifo_packets 8, 16, 32 Audio quality (smaller = more glitch-prone), RAM usage
19 ESP32 SynchronizedNBufferRTOS default (5ms write wait) No write failures, clean audio
20 RP2040 RingBuffer default Write retry loop works, clean audio

Volume / Mute controls

# Test Verify
21 amixer -c Audio shows Mic/Speaker controls Volume range -100dB to 0dB, mute on/off
22 Host changes volume via amixer setVolumeCallback fires, getVolume() returns new value
23 Host changes mute via amixer setMuteCallback fires, isMute() returns new value
24 Device changes volume via setVolume(0.5) Host sees updated value on next GET_CUR

Edge cases

# Test Verify
25 USB disconnect/reconnect during streaming No crash, streaming resumes after reconnect
26 Start/stop streaming repeatedly (open/close Audacity 5×) No memory leak, active toggles correctly
27 begin() called twice No crash, second call is no-op
28 aplay with wrong format (-f S32_LE) Clean error, no crash

Host-side test commands

# Record (TX mode test)
arecord -D hw:Audio -f S16_LE -r 44100 -c 2 -d 5 test.wav && aplay test.wav

# Play (RX mode test)
aplay -D hw:Audio -f S16_LE -r 44100 -c 2 test.wav

# Check descriptor
lsusb -v -d cafe:4002

# Check mixer controls
amixer -c Audio

# Check for USB errors
dmesg | tail -20

# List audio devices
aplay -l | grep Audio
arecord -l | grep Audio

Sketch-side diagnostics

// TX diagnostics
MySerial.print("xfer="); MySerial.print(out.getTxXferCount());
MySerial.print(" rd="); MySerial.print(out.getTxFifoReadTotal());
MySerial.print(" avail="); MySerial.print(out.availableForWrite());
MySerial.print(" frame="); MySerial.print(out.getTxFrameBytesLast());
MySerial.print(" bps="); MySerial.println(out.getTxBytesPerSample());

// RX diagnostics
MySerial.print("active="); MySerial.print(in.isStreamingActiveRx());
MySerial.print(" rxXfer="); MySerial.print(in.getRxXferCount());
MySerial.print(" rxBytes="); MySerial.print(in.getRxTotalBytes());
MySerial.print(" bufRx="); MySerial.print(in.bufferRx().available());
MySerial.print(" avail="); MySerial.println(in.available());

Priority order

  1. Core tests (1, 2, 4, 5) — TX/RX on both platforms
  2. Format variations (7–9) — sample rate, channels, bit depth
  3. Feature flags (10–14) — feedback, multi-rate, interrupt EP, flow control, volume
  4. Composite (15–17) — CDC + Audio
  5. Buffering (18–20) — FIFO depth, buffer types
  6. Volume/Mute (21–24) — control requests
  7. Edge cases (25–28) — robustness
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment