arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioConfig.h
Go to the documentation of this file.
1#pragma once
2#include <stdint.h>
3
4#include <cstring>
5#include <vector>
6
8
9namespace audio_tools {
10
11// ── Platform-specific default USB endpoint numbers ──────────────────────────
12// Some MCUs' USB peripherals restrict isochronous transfers to one hardwired
13// endpoint number instead of allowing the stack to allocate any free one.
14//
15// nRF52833/nRF52840 caveats:
16// - The USBD peripheral has exactly one isochronous endpoint pair, hardwired
17// to endpoint number 8 (tinyusb dcd_nrf5x.c: EP_ISO_NUM == 8). Opening an
18// isochronous endpoint at any other number hits
19// TU_ASSERT(epnum == EP_ISO_NUM) and the device stops responding -- this
20// is why ep_in/ep_out/ep_fb are pinned here instead of using
21// TinyUSBDevice.allocEndpoint() like on RP2040/SAMD/STM32.
22// - ep_in and ep_fb both resolve to 0x88 (the same physical IN endpoint),
23// which is safe because they're mutually exclusive: enableFeedbackEp()
24// already requires enable_ep_in == false (see USBAudio2DescriptorBuilder),
25// matching the hardware's single ISO IN + single ISO OUT pair exactly.
26// - ep_int (the AC status/change interrupt endpoint) is NOT restricted --
27// endpoints 1-7 (control/bulk/interrupt) are freely assignable on this
28// chip -- so it keeps the generic -1 default below and is still allocated
29// dynamically even on nRF52.
30// - Because the fixed 0x88/0x08 numbers bypass allocEndpoint() entirely,
31// TinyUSBDevice's internal endpoint counters don't know endpoint 8 is
32// taken. Audio + a single CDC Serial (the common composite case) is safe
33// since CDC only needs a handful of low-numbered endpoints, but stacking
34// many other USB classes alongside Audio could in theory let one of them
35// also land on endpoint 8 and collide -- an edge case, not a concern for
36// a simple Audio+CDC composite device.
37#if defined(NRF52840_XXAA) || defined(NRF52833_XXAA)
38#ifndef USB_AUDIO_EP_IN
39#define USB_AUDIO_EP_IN 0x88
40#endif
41#ifndef USB_AUDIO_EP_OUT
42#define USB_AUDIO_EP_OUT 0x08
43#endif
44#ifndef USB_AUDIO_EP_FB
45#define USB_AUDIO_EP_FB 0x88
46#endif
47#endif
48
49// USBAudioDeviceESP32 (arduino-esp32) previously pinned 0x83/0x03/0x84/0x85
50// here under the assumption that CDC uses 0x81/0x82/0x02. That assumption is
51// wrong: arduino-esp32's own USBCDC::load_cdc_descriptor() (USBCDC.cpp)
52// hardcodes notification=0x85, OUT=0x03, IN=0x84 -- i.e. exactly the "safe"
53// defaults this block used to define -- so a composite CDC + Audio device
54// silently collided on three endpoints (confirmed independently: Adafruit's
55// own TinyUSBDevice::allocEndpoint() carries a matching ESP32 workaround
56// that explicitly steers around 0x03/0x84/0x85 for the same reason). Rather
57// than guess another set of "safe" fixed numbers, ESP32 now falls through to
58// the generic -1 ("allocate dynamically") default below: USBAudioDeviceESP32
59// resolves it via arduino-esp32's own tinyusb_get_free_in_endpoint() /
60// tinyusb_get_free_out_endpoint(), which already know about CDC's reserved
61// endpoints. See https://github.com/pschatzmann/arduino-audio-tools/issues/2396.
62
63// Arduino UNO R4 / Nano R4 (Renesas RA4M1, arduino:renesas_uno core) caveats:
64// - This core's USB.cpp builds one fixed composite descriptor (CDC + HID +
65// MSD) at runtime and has no dynamic endpoint allocator API (no
66// TinyUSBDevice.allocEndpoint(), no tinyusb_get_free_in_endpoint()), so
67// endpoint numbers must be fixed constants here, same as nRF52 above.
68// - Values must avoid the ones the core's own USB.cpp hardcodes for its
69// built-in interfaces: CDC uses 0x81 (notification), 0x02/0x82 (data);
70// HID uses 0x83; MSD uses 0x04/0x84 (see __USBGetCustomInterfaceDescriptor
71// in USB.h for the authoritative list). EP5-EP7 are free on every
72// variant since none of the built-ins use them.
73// - The RA4M1's RUSB2 USB peripheral (dcd_rusb2.c) only has 2 isochronous-
74// capable pipes, so at most 2 ISO endpoints can be open at once. This
75// matches USBAudioConfig's own constraint that feedback
76// (enable_feedback_ep) is only active in RX-only mode (no IN endpoint) --
77// TX-only, RX-only-with-feedback, and RXTX-without-feedback each need
78// exactly 2 or fewer ISO endpoints, so this hardware limit is never hit
79// through this library's own configuration surface.
80#if defined(ARDUINO_ARCH_RENESAS)
81#ifndef USB_AUDIO_EP_IN
82#define USB_AUDIO_EP_IN 0x85
83#endif
84#ifndef USB_AUDIO_EP_OUT
85#define USB_AUDIO_EP_OUT 0x05
86#endif
87#ifndef USB_AUDIO_EP_FB
88#define USB_AUDIO_EP_FB 0x86
89#endif
90#ifndef USB_AUDIO_EP_INT
91#define USB_AUDIO_EP_INT 0x87
92#endif
93#endif
94
95// Everywhere else (generic TinyUSB: RP2040, SAMD, STM32, and now ESP32) -1
96// tells the platform subclass to allocate the endpoint dynamically instead
97// of using a fixed number. Define any of these yourself (e.g. via a build
98// flag) to support additional boards/cores, or to pin a fixed address,
99// without touching the struct or driver.
100#ifndef USB_AUDIO_EP_IN
101#define USB_AUDIO_EP_IN -1
102#endif
103#ifndef USB_AUDIO_EP_OUT
104#define USB_AUDIO_EP_OUT -1
105#endif
106#ifndef USB_AUDIO_EP_FB
107#define USB_AUDIO_EP_FB -1
108#endif
109#ifndef USB_AUDIO_EP_INT
110#define USB_AUDIO_EP_INT -1
111#endif
112
117struct USBAudioConfig : public AudioInfo {
118
119 // ── Direction ─────────────────────────────────────────────────────────────
120 bool enable_ep_in = true;
121 bool enable_ep_out = true;
122
123 // ── USB endpoint addresses ────────────────────────────────────────────────
136
137 // ── Interface numbering ───────────────────────────────────────────────────
140 uint8_t itf_num_ac = 0;
141
142 // ── Buffering ─────────────────────────────────────────────────────────────
148 uint8_t fifo_packets = 32;
149
150 // ── Device identity ───────────────────────────────────────────────────────
151 uint16_t vid = 0xCafe;
152 uint16_t pid = 0x4002;
153 const char* manufacturer = "Audio Tools";
154 const char* product = "USB Audio";
155 const char* serial = "000001";
156 bool self_powered = true;
157 uint8_t max_power_ma = 100;
158
159 // ── Advanced ──────────────────────────────────────────────────────────────
162
171
184 std::vector<uint32_t> supported_sample_rates = {8000, 11025, 16000, 22050,
185 24000, 32000, 44100, 48000};
186
191
198
199#if defined(FREERTOS_H) || defined(INC_FREERTOS_H)
200 // ── FreeRTOS USB task (RP2040 + FreeRTOS only) ───────────────────────────
206 bool enable_usb_task = true;
209 int usb_task_stack_size = 1024;
212 int usb_task_priority = configMAX_PRIORITIES - 1;
216 int usb_task_write_wait_ms = 5;
217
218#endif
219 // ── Volume Handling ────────────────────────────────────────────────────────
223 bool volume_active = false;
224
225 // ── ESP32-specific ────────────────────────────────────────────────────────
229 bool begin_usb = false;
230
231 // ── Zephyr-specific ───────────────────────────────────────────────────────
236 uint8_t terminal_id = 1;
237
238 bool operator==(const USBAudioConfig& other) {
239 return sample_rate == other.sample_rate && channels == other.channels &&
241
242 enable_ep_in == other.enable_ep_in &&
243 enable_ep_out == other.enable_ep_out &&
244
245 ep_in == other.ep_in && ep_out == other.ep_out &&
246 ep_fb == other.ep_fb &&
247
248 itf_num_ac == other.itf_num_ac &&
249 fifo_packets == other.fifo_packets &&
250
251 vid == other.vid && pid == other.pid &&
252
253 std::strcmp(manufacturer ? manufacturer : "",
254 other.manufacturer ? other.manufacturer : "") == 0 &&
255
256 std::strcmp(product ? product : "",
257 other.product ? other.product : "") == 0 &&
258
259 std::strcmp(serial ? serial : "",
260 other.serial ? other.serial : "") == 0 &&
261
262 self_powered == other.self_powered &&
263 max_power_ma == other.max_power_ma &&
264
267
268 begin_usb == other.begin_usb && terminal_id == other.terminal_id;
269 }
270
271 bool operator!=(const USBAudioConfig& other) { return !(*this == other); }
272};
273
278inline uint32_t highestSupportedSampleRate(const USBAudioConfig& cfg) {
279 return !cfg.enable_multi_sample_rate || cfg.supported_sample_rates.empty() ? (uint32_t)cfg.sample_rate
280 : cfg.supported_sample_rates.back();
281}
282
283} // namespace audio_tools
#define USB_AUDIO_EP_IN
Definition USBAudioConfig.h:82
#define USB_AUDIO_EP_FB
Definition USBAudioConfig.h:88
#define USB_AUDIO_EP_INT
Definition USBAudioConfig.h:91
#define USB_AUDIO_EP_OUT
Definition USBAudioConfig.h:85
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
uint32_t highestSupportedSampleRate(const USBAudioConfig &cfg)
Definition USBAudioConfig.h:278
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:51
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:117
bool enable_interrupt_ep
Definition USBAudioConfig.h:190
bool operator==(const USBAudioConfig &other)
Definition USBAudioConfig.h:238
int16_t ep_fb
ISO IN (explicit feedback, RX-only mode)
Definition USBAudioConfig.h:134
bool self_powered
Definition USBAudioConfig.h:156
const char * serial
Definition USBAudioConfig.h:155
int16_t ep_in
ISO IN (device → host, capture/microphone)
Definition USBAudioConfig.h:132
bool enable_multi_sample_rate
Definition USBAudioConfig.h:170
std::vector< uint32_t > supported_sample_rates
Definition USBAudioConfig.h:184
const char * product
Definition USBAudioConfig.h:154
int16_t ep_int
INT IN (AC status/change notifications)
Definition USBAudioConfig.h:135
uint8_t max_power_ma
Definition USBAudioConfig.h:157
bool enable_feedback_ep
Enable isochronous feedback endpoint so the host can adjust its clock.
Definition USBAudioConfig.h:161
const char * manufacturer
Definition USBAudioConfig.h:153
uint16_t pid
Definition USBAudioConfig.h:152
uint16_t vid
Definition USBAudioConfig.h:151
int16_t ep_out
ISO OUT (host → device, playback/speaker)
Definition USBAudioConfig.h:133
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:120
uint8_t itf_num_ac
Definition USBAudioConfig.h:140
uint8_t fifo_packets
Definition USBAudioConfig.h:148
uint8_t terminal_id
Definition USBAudioConfig.h:236
bool enable_ep_in_flow_control
Definition USBAudioConfig.h:197
bool volume_active
Definition USBAudioConfig.h:223
bool operator!=(const USBAudioConfig &other)
Definition USBAudioConfig.h:271
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:121
bool begin_usb
Definition USBAudioConfig.h:229