From eb544ea0880a5a9d92634875cbce1341de3342e2 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 09:03:28 -0500 Subject: [PATCH 01/11] Allow `channel_count=2` --- shared-bindings/usb_audio/__init__.c | 2 +- shared-module/usb_audio/usb_audio_descriptors.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 49eaa9716f2..7ac882e982b 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -93,7 +93,7 @@ //| This function may only be used from ``boot.py``. //| //| :param int sample_rate: Samples per second of the streamed audio. -//| :param int channel_count: Number of channels. Only mono (1) is supported initially. +//| :param int channel_count: Number of channels. Either mono (1) or stereo (2) is supported. //| :param int bits_per_sample: Bits per signed PCM sample. Only 16 is supported initially. //| :param bool microphone: Present a microphone (audio flows board -> host). Enabled by default. //| :param bool speaker: Present a speaker (audio flows host -> board). diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index 1313e198959..8b297c3218f 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -30,7 +30,7 @@ size_t usb_audio_descriptor_length(void); // 16-bit signed LE PCM, mono. #define USB_AUDIO_N_BYTES_PER_SAMPLE (2) -#define USB_AUDIO_N_CHANNELS (1) +#define USB_AUDIO_N_CHANNELS (2) #define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) // Endpoint number for the single isochronous audio data endpoint. Most device From 2285442c5901a3e91b5445c11d3ec655d34e159f Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 09:09:51 -0500 Subject: [PATCH 02/11] Add error to disallow stereo microphone and speaker --- shared-bindings/usb_audio/__init__.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 7ac882e982b..108c8ee2bdf 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -124,6 +124,10 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_raise_ValueError(MP_ERROR_TEXT("At least one of microphone and speaker must be enabled")); } + if (microphone && speaker && channel_count == 2) { + mp_raise_ValueError(MP_ERROR_TEXT("Only mono operation is supported when microphone and speaker are both enabled")); + } + if (!shared_module_usb_audio_enable(sample_rate, channel_count, bits_per_sample, microphone, speaker)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot change USB devices now")); } From 478762cd1d0338f3d885595f70f1d7d71436db74 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 16:46:31 -0500 Subject: [PATCH 03/11] Support stereo audio device descriptors --- shared-bindings/usb_audio/__init__.c | 16 +- shared-module/usb_audio/USBMicrophone.c | 4 +- shared-module/usb_audio/USBSpeaker.c | 52 ++-- shared-module/usb_audio/USBSpeaker.h | 20 +- shared-module/usb_audio/__init__.c | 281 ++++++++++++++---- .../usb_audio/usb_audio_descriptors.h | 64 +++- supervisor/shared/usb/tusb_config.h | 8 +- 7 files changed, 330 insertions(+), 115 deletions(-) diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 108c8ee2bdf..77c3201fcb5 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -19,9 +19,9 @@ //| Audio Class (UAC2) microphone: the board is the audio *source* and streams //| samples to the host over a USB isochronous IN endpoint. //| -//| This mode requires 1 IN endpoint and 2 interfaces. Generally, microcontrollers -//| have a limit on the number of endpoints. If you exceed the number of endpoints, -//| CircuitPython will automatically enter Safe Mode. Even in this case, you may be +//| This mode requires 1 (mono) or 2 (stereo / bidirectional) IN endpoint and 2 interfaces. +//| Generally, microcontrollers have a limit on the number of endpoints. If you exceed the number +//| of endpoints, CircuitPython will automatically enter Safe Mode. Even in this case, you may be //| able to enable USB audio by also disabling other USB functions, such as //| `usb_hid` or `usb_midi`. //| @@ -61,9 +61,11 @@ //| pass //| mic.stop() //| -//| The ``sample_rate`` and ``channel_count`` of the sample played must match the -//| values passed to `enable`, and the sample must be 16-bit signed; otherwise -//| ``play`` raises a ``ValueError``. +//| The ``sample_rate`` and ``channel_count`` of the sample played must match the values passed to `enable`, +//| and the sample must be 16-bit signed; otherwise ``play`` raises a ``ValueError``. +//| +//| If both speaker and headphone features are enabled (bidirectional), only mono operation is +//| permitted. //| //| This interface is experimental and may change without notice even in stable //| versions of CircuitPython.""" @@ -115,7 +117,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_int_t sample_rate = mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, USB_AUDIO_MAX_SAMPLE_RATE, MP_QSTR_sample_rate); - mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_N_CHANNELS, MP_QSTR_channel_count); + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_MAX_CHANNELS, MP_QSTR_channel_count); mp_int_t bits_per_sample = mp_arg_validate_int(args[ARG_bits_per_sample].u_int, USB_AUDIO_BITS_PER_SAMPLE, MP_QSTR_bits_per_sample); bool microphone = args[ARG_microphone].u_bool; bool speaker = args[ARG_speaker].u_bool; diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index fd5309abeb9..c9245b2edb2 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -107,9 +107,9 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { return 0; } - // The negotiated USB format is 16-bit signed mono PCM. For this step the + // The negotiated USB format is 16-bit signed PCM. For this step the // bound sample is assumed to already be in that format (e.g. a 16-bit signed - // mono audiocore.RawSample), so its bytes are copied straight through. + // audiocore.RawSample), so its bytes are copied straight through. size_t filled = 0; while (filled < max_bytes) { if (self->buffer_length == 0) { diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index 12921430bc3..63c75dbf5b5 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -15,11 +15,6 @@ #include "tusb.h" -// The ring is sized independently of the TinyUSB headers (see USBSpeaker.h); -// check it still matches the OUT endpoint's software FIFO so the push side can be -// reasoned about against the USB plumbing. -MP_STATIC_ASSERT(USB_AUDIO_SPEAKER_RING_SIZE == CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ); - // Only one speaker can be fed by the single USB OUT endpoint at a time. This // points at the most recently constructed USBSpeaker, or NULL when none exists, // mirroring active_microphone in USBMicrophone.c. The USB background task pushes @@ -36,7 +31,30 @@ void common_hal_usb_audio_usbspeaker_construct(usb_audio_usbspeaker_obj_t *self) self->base.channel_count = usb_audio_channel_count; self->base.samples_signed = true; self->base.single_buffer = false; - self->base.max_buffer_length = USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE; + + // Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches + // audiocore.RawSample's convention where base.max_buffer_length is the whole + // buffer and get_buffer() returns half of it. + self->base.max_buffer_length = (2 * USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER * USB_AUDIO_N_BYTES_PER_SAMPLE * usb_audio_channel_count); + self->output_buffer = m_malloc_without_collect(self->base.max_buffer_length); + if (self->output_buffer == NULL) { + common_hal_usb_audio_usbspeaker_deinit(self); + m_malloc_fail(self->base.max_buffer_length); + } + memset(self->output_buffer, 0, self->base.max_buffer_length); + + // The ring is sized independently of the TinyUSB headers; check it still + // matches the OUT endpoint's software FIFO so the push side can be reasoned + // about against the USB plumbing. + self->ring_len = 16 * ((usb_audio_sample_rate / 1000 + 1) * (USB_AUDIO_N_BYTES_PER_SAMPLE * usb_audio_channel_count)); + assert(self->ring_len == CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ); + + self->ring = m_malloc_without_collect(self->ring_len); + if (self->ring == NULL) { + common_hal_usb_audio_usbspeaker_deinit(self); + m_malloc_fail(self->ring_len); + } + memset(self->ring, 0, self->ring_len); self->ring_head = 0; self->ring_tail = 0; @@ -104,32 +122,32 @@ void usb_audio_usbspeaker_background_drain(const uint8_t *in, size_t n) { if (self == NULL || n == 0) { return; } - if (n >= USB_AUDIO_SPEAKER_RING_SIZE) { + if (n >= self->ring_len) { // A single chunk larger than the whole ring can only contribute its // newest tail. (Cannot happen with USB packets << ring size, but keep // the copies provably in-bounds.) - in += n - USB_AUDIO_SPEAKER_RING_SIZE; - n = USB_AUDIO_SPEAKER_RING_SIZE; + in += n - self->ring_len; + n = self->ring_len; } common_hal_mcu_disable_interrupts(); - size_t free_space = USB_AUDIO_SPEAKER_RING_SIZE - self->ring_count; + size_t free_space = self->ring_len - self->ring_count; if (n > free_space) { // Overrun: advance the read cursor past the oldest bytes we're about to // overwrite, keeping latency bounded and following the newest host audio. size_t drop = n - free_space; - self->ring_tail = (self->ring_tail + drop) % USB_AUDIO_SPEAKER_RING_SIZE; + self->ring_tail = (self->ring_tail + drop) % self->ring_len; self->ring_count -= drop; } // Copy in one or two segments, wrapping at the end of the ring. - size_t first = MIN(n, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_head); + size_t first = MIN(n, self->ring_len - self->ring_head); memcpy(&self->ring[self->ring_head], in, first); if (n > first) { memcpy(&self->ring[0], in + first, n - first); } - self->ring_head = (self->ring_head + n) % USB_AUDIO_SPEAKER_RING_SIZE; + self->ring_head = (self->ring_head + n) % self->ring_len; self->ring_count += n; common_hal_mcu_enable_interrupts(); @@ -157,12 +175,12 @@ uint32_t common_hal_usb_audio_usbspeaker_read(usb_audio_usbspeaker_obj_t *self, // but stay defensive so the returned count is always a whole number). to_copy -= to_copy % bytes_per_frame; - size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); + size_t first = MIN(to_copy, self->ring_len - self->ring_tail); memcpy(buffer, &self->ring[self->ring_tail], first); if (to_copy > first) { memcpy((uint8_t *)buffer + first, &self->ring[0], to_copy - first); } - self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; + self->ring_tail = (self->ring_tail + to_copy) % self->ring_len; self->ring_count -= to_copy; common_hal_mcu_enable_interrupts(); @@ -199,12 +217,12 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker // It is never preempted by the producer, so no interrupt guard is required. size_t to_copy = MIN(self->ring_count, (size_t)half); - size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); + size_t first = MIN(to_copy, self->ring_len - self->ring_tail); memcpy(out, &self->ring[self->ring_tail], first); if (to_copy > first) { memcpy(out + first, &self->ring[0], to_copy - first); } - self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; + self->ring_tail = (self->ring_tail + to_copy) % self->ring_len; self->ring_count -= to_copy; if (to_copy < half) { diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index 8c06cff519d..1483ff89085 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -22,21 +22,6 @@ // comfortably inside the ring. #define USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER (128) -// Bytes per audio frame in the negotiated UAC2 format (mono 16-bit for v1). -#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) - -// Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches -// audiocore.RawSample's convention where base.max_buffer_length is the whole -// buffer and get_buffer() returns half of it. -#define USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE (2 * USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER * USB_AUDIO_SPEAKER_BYTES_PER_FRAME) - -// Host -> board receive ring size. Mirrors CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ -// (16 * the full-speed OUT wMaxPacketSize) but is spelled out from the format -// constants so this struct definition stays free of the TinyUSB headers. A -// MP_STATIC_ASSERT in USBSpeaker.c checks the two stay equal. -#define USB_AUDIO_SPEAKER_OUT_PACKET_SIZE ((USB_AUDIO_MAX_SAMPLE_RATE / 1000 + 1) * USB_AUDIO_SPEAKER_BYTES_PER_FRAME) -#define USB_AUDIO_SPEAKER_RING_SIZE (16 * USB_AUDIO_SPEAKER_OUT_PACKET_SIZE) - typedef struct usb_audio_usbspeaker_obj { // First member so the object can be used directly as an audiosample source. audiosample_base_t base; @@ -45,13 +30,14 @@ typedef struct usb_audio_usbspeaker_obj { // background task via usb_audio_usbspeaker_background_drain() and drained by // get_buffer(). Single producer (task), single consumer (output DMA ISR); // see the concurrency notes in USBSpeaker.c. - uint8_t ring[USB_AUDIO_SPEAKER_RING_SIZE]; + uint8_t *ring; + size_t ring_len; size_t ring_head; // next write offset size_t ring_tail; // next read offset size_t ring_count; // valid bytes currently in the ring // Owned double-buffer returned to the output backend by get_buffer(). - uint8_t output_buffer[USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE]; + uint8_t *output_buffer; uint8_t output_index; // 0 or 1: which half get_buffer() fills next } usb_audio_usbspeaker_obj_t; diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index 5964b8acf78..113f503ee66 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -20,6 +20,9 @@ #include "supervisor/shared/tick.h" #include "tusb.h" +int16_t *usb_audio_mic_samples; +size_t usb_audio_mic_samples_len; + static bool usb_audio_is_enabled = false; // The host opens each AudioStreaming interface independently (alt 0 = idle, alt 1 @@ -41,14 +44,24 @@ bool usb_audio_microphone_enabled; bool usb_audio_speaker_enabled; // Audio control state surfaced to the host. One extra entry for the master channel 0. -static int8_t usb_audio_mute[USB_AUDIO_N_CHANNELS + 1]; -static int16_t usb_audio_volume[USB_AUDIO_N_CHANNELS + 1]; +static int8_t usb_audio_mute[USB_AUDIO_MAX_CHANNELS + 1]; +static int16_t usb_audio_volume[USB_AUDIO_MAX_CHANNELS + 1]; bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, mp_int_t bits_per_sample, bool microphone, bool speaker) { if (tud_connected()) { return false; } + // One scratch chunk (1 ms at the sample rate); we loop until the FIFO reaches + // the setpoint. + usb_audio_mic_samples_len = sample_rate / 1000 * channel_count * USB_AUDIO_N_BYTES_PER_SAMPLE; + usb_audio_mic_samples = (int16_t *)m_malloc_without_collect(usb_audio_mic_samples_len); + if (usb_audio_mic_samples == NULL) { + m_malloc_fail(usb_audio_mic_samples_len); + return false; + } + memset(usb_audio_mic_samples, 0, usb_audio_mic_samples_len); + usb_audio_sample_rate = sample_rate; usb_audio_channel_count = channel_count; usb_audio_bits_per_sample = bits_per_sample; @@ -126,25 +139,25 @@ void usb_audio_setup_singletons(void) { speaker; } -// Hand-rolled UAC2 mono speaker (host -> board) descriptor WITHOUT an async -// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO_SPEAKER_MONO_FB_DESCRIPTOR +// Hand-rolled UAC2 speaker (host -> board) descriptor WITHOUT an async +// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO_SPEAKER_STEREO_FB_DESCRIPTOR // (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so // the streaming alt-setting declares a single OUT endpoint (_nEPs = 0x01). The // entity IDs match the mic descriptor (see usb_audio_descriptors.h); only the // terminal roles reverse: the input terminal is the USB-streaming side and the // output terminal is the desktop speaker, and the AS interface links the input // terminal (0x01). Async feedback for true clock matching is a later step. -#define USB_AUDIO_SPEAKER_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ +#define USB_AUDIO_SPEAKER_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ /* Standard Interface Association Descriptor (IAD) */ \ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ /* Standard AC Interface Descriptor(4.7.1) */ \ TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -154,7 +167,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -162,10 +175,103 @@ void usb_audio_setup_singletons(void) { /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) +#define USB_AUDIO_SPEAKER_TWO_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ + /* Feature Unit Descriptor(4.7.2.8) */ \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) -- alt 0, zero bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ + /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +// Hand-rolled UAC2 microphone (board -> host) descriptor WITHOUT an async +// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR +// (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so +// the streaming alt-setting declares a single IN endpoint (_nEPs = 0x00). Async +// feedback for true clock matching is a later step. +#define USB_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ + /* Feature Unit Descriptor(4.7.2.8) */ \ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + +#define USB_AUDIO_MIC_TWO_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ + /* Standard Interface Association Descriptor (IAD) */ \ + TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + /* Standard AC Interface Descriptor(4.7.1) */ \ + TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + /* Clock Source Descriptor(4.7.2.1) */ \ + TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ + /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ + TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ + /* Feature Unit Descriptor(4.7.2.8) */ \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + /* Standard AS Interface Descriptor(4.9.1) */ \ + /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ + TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + /* Class-Specific AS Interface Descriptor(4.9.2) */ \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ + TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ + TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ + /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ + TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + // Hand-rolled UAC2 mono headset (microphone + speaker both enabled): one audio function // presenting both a speaker (host -> board OUT) and a microphone (board -> host // IN) at once. This combines USB_AUDIO_SPEAKER_DESCRIPTOR's speaker chain with -// TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR's mic chain under a single IAD. The two chains +// USB_AUDIO_MIC_DESCRIPTOR's mic chain under a single IAD. The two chains // must use distinct entity IDs (USB_AUDIO_HS_ENTITY_*; see usb_audio_descriptors.h) // because they live in the same AudioControl interface, and they share one clock // source. The function spans three interfaces: AudioControl (_itfnum), the @@ -183,14 +289,14 @@ void usb_audio_setup_singletons(void) { TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* --- Speaker chain (host -> board) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Mic chain (board -> host) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- generic microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming out to the host */ \ @@ -201,7 +307,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the speaker input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -214,7 +320,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one IN endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the mic output terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -238,9 +344,17 @@ size_t usb_audio_descriptor_length(void) { return USB_AUDIO_HEADSET_DESC_LEN; } if (usb_audio_direction_is_output()) { - return USB_AUDIO_SPEAKER_DESC_LEN; + if (usb_audio_channel_count == 1) { + return USB_AUDIO_SPEAKER_ONE_CH_DESC_LEN; + } else { + return USB_AUDIO_SPEAKER_TWO_CH_DESC_LEN; + } + } + if (usb_audio_channel_count == 1) { + return USB_AUDIO_MIC_ONE_CH_DESC_LEN; + } else { + return USB_AUDIO_MIC_TWO_CH_DESC_LEN; } - return TUD_AUDIO_MIC_ONE_CH_DESC_LEN; } size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) { @@ -268,6 +382,8 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de const uint8_t ep_in = descriptor_counts->current_endpoint + 1; usb_add_interface_string(*current_interface_string, "CircuitPython Headset"); + (*current_interface_string)++; + const uint8_t usb_audio_descriptor[] = { USB_AUDIO_HEADSET_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, @@ -283,7 +399,6 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; usb_audio_mic_as_itf = descriptor_counts->current_interface + 2; - (*current_interface_string)++; // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus // one OUT and one IN endpoint. descriptor_counts->current_interface += 3; @@ -298,57 +413,105 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de if (usb_audio_direction_is_output()) { usb_add_interface_string(*current_interface_string, "CircuitPython Speaker"); + (*current_interface_string)++; + + // The AudioStreaming interface follows the AudioControl interface. + usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; + + if (usb_audio_channel_count == 1) { + const uint8_t usb_audio_descriptor[] = { + USB_AUDIO_SPEAKER_ONE_CH_DESCRIPTOR( + /*_itfnum*/ descriptor_counts->current_interface, + /*_stridx*/ *current_interface_string, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, + /*_epout*/ iso_ep_num, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) + }; + + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. + descriptor_counts->current_interface += 2; + if (!forced_iso_ep) { + descriptor_counts->num_out_endpoints++; + descriptor_counts->current_endpoint++; + } + + memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); + + return sizeof(usb_audio_descriptor); + } else { + const uint8_t usb_audio_descriptor[] = { + USB_AUDIO_SPEAKER_TWO_CH_DESCRIPTOR( + /*_itfnum*/ descriptor_counts->current_interface, + /*_stridx*/ *current_interface_string, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, + /*_epout*/ iso_ep_num, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) + }; + + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. + descriptor_counts->current_interface += 3; + if (!forced_iso_ep) { + descriptor_counts->num_out_endpoints += 2; + descriptor_counts->current_endpoint += 2; + } + + memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); + + return sizeof(usb_audio_descriptor); + } + } + + usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); + (*current_interface_string)++; + + // The AudioStreaming interface follows the AudioControl interface. + usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; + + if (usb_audio_channel_count == 2) { const uint8_t usb_audio_descriptor[] = { - USB_AUDIO_SPEAKER_DESCRIPTOR( + USB_AUDIO_MIC_ONE_CH_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epout*/ iso_ep_num, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) + /*_epin*/ iso_ep_num | 0x80, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) }; - // The AudioStreaming interface follows the AudioControl interface. - usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; - - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints++; + descriptor_counts->num_in_endpoints++; descriptor_counts->current_endpoint++; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); return sizeof(usb_audio_descriptor); - } + } else { + const uint8_t usb_audio_descriptor[] = { + USB_AUDIO_MIC_TWO_CH_DESCRIPTOR( + /*_itfnum*/ descriptor_counts->current_interface, + /*_stridx*/ *current_interface_string, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, + /*_epin*/ iso_ep_num | 0x80, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) + }; - usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); - const uint8_t usb_audio_descriptor[] = { - TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR( - /*_itfnum*/ descriptor_counts->current_interface, - /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, - /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epin*/ iso_ep_num | 0x80, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) - }; + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. + descriptor_counts->current_interface += 3; + if (!forced_iso_ep) { + descriptor_counts->num_in_endpoints += 2; + descriptor_counts->current_endpoint += 2; + } - // The AudioStreaming interface follows the AudioControl interface. - usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; + memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. - descriptor_counts->current_interface += 2; - if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints++; - descriptor_counts->current_endpoint++; + return sizeof(usb_audio_descriptor); } - - memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); - - return sizeof(usb_audio_descriptor); } // --------------------------------------------------------------------+ @@ -397,24 +560,20 @@ static void usb_audio_microphone_task(void) { tu_fifo_t *ep_in_ff = tud_audio_get_ep_in_ff(); uint16_t const target = tu_fifo_depth(ep_in_ff) / 2; - // One scratch chunk (1 ms at the max rate); we loop until the FIFO reaches - // the setpoint. Sized for the highest rate enable() accepts. - static int16_t samples[USB_AUDIO_MAX_SAMPLE_RATE / 1000 * USB_AUDIO_N_CHANNELS]; - uint16_t count; while ((count = tu_fifo_count(ep_in_ff)) < target) { size_t want = target - count; - if (want > sizeof(samples)) { - want = sizeof(samples); + if (want > usb_audio_mic_samples_len) { + want = usb_audio_mic_samples_len; } // Pull the next chunk from whichever USBMicrophone is playing. bool underran = false; - size_t filled = usb_audio_usbmicrophone_background_fill((uint8_t *)samples, want); + size_t filled = usb_audio_usbmicrophone_background_fill((uint8_t *)usb_audio_mic_samples, want); if (filled == 0) { // No source attached, paused, or fully drained: keep the endpoint // alive with silence so the host never sees a starved stream. - memset((uint8_t *)samples, 0, want); + memset((uint8_t *)usb_audio_mic_samples, 0, want); } else if (filled < want) { // The source momentarily underran. Send just what it produced and // let the FIFO cushion ride until it catches up next pass, rather @@ -423,7 +582,7 @@ static void usb_audio_microphone_task(void) { underran = true; } - if (tud_audio_write((uint8_t *)samples, (uint16_t)want) == 0) { + if (tud_audio_write((uint8_t *)usb_audio_mic_samples, (uint16_t)want) == 0) { break; // FIFO unexpectedly full / host not ready } if (underran) { @@ -496,10 +655,10 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // A headset exposes a feature unit per direction; the speaker's id matches the // single-direction USB_AUDIO_ENTITY_FEATURE_UNIT, the mic adds a second one. - // Mute/volume state is shared across them (mono, cosmetic for now). + // Mute/volume state is shared across them. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_N_CHANNELS) { + if (channelNum > USB_AUDIO_MAX_CHANNELS) { return false; } switch (ctrlSel) { @@ -537,7 +696,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p switch (ctrlSel) { case AUDIO_TE_CTRL_CONNECTOR: { audio_desc_channel_cluster_t ret; - ret.bNrChannels = USB_AUDIO_N_CHANNELS; + ret.bNrChannels = usb_audio_channel_count; ret.bmChannelConfig = (audio_channel_config_t)0; ret.iChannelNames = 0; return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &ret, sizeof(ret)); @@ -550,7 +709,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Feature unit (mute/volume) for either the speaker or mic chain. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_N_CHANNELS) { + if (channelNum > USB_AUDIO_MAX_CHANNELS) { return false; } switch (ctrlSel) { diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index 8b297c3218f..d94c8acabdf 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -27,10 +27,10 @@ size_t usb_audio_descriptor_length(void); // The isochronous IN endpoint's wMaxPacketSize in the USB descriptor is computed // for this rate, so it is the highest rate usb_audio.enable() will accept. #define USB_AUDIO_MAX_SAMPLE_RATE (48000) +#define USB_AUDIO_MAX_CHANNELS (2) -// 16-bit signed LE PCM, mono. +// 16-bit signed LE PCM. #define USB_AUDIO_N_BYTES_PER_SAMPLE (2) -#define USB_AUDIO_N_CHANNELS (2) #define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) // Endpoint number for the single isochronous audio data endpoint. Most device @@ -45,7 +45,7 @@ size_t usb_audio_descriptor_length(void); #define USB_AUDIO_ISO_EP_NUM (0) #endif -// Fixed UAC2 entity IDs baked into TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR and the +// Fixed UAC2 entity IDs baked into USB_AUDIO_MIC_DESCRIPTOR and the // hand-rolled speaker descriptor (USB_AUDIO_SPEAKER_DESCRIPTOR in __init__.c). // The speaker reuses the same IDs as the mic; only the terminal roles reverse // (input terminal = USB streaming, output terminal = desktop speaker). @@ -68,21 +68,71 @@ size_t usb_audio_descriptor_length(void); #define USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT (0x06) #define USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL (0x07) // USB streaming out to host -// Length of the no-feedback mono speaker descriptor. It uses the same set of -// sub-descriptors as TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR (one isochronous data +// Length of the no-feedback speaker descriptor. It uses the same set of +// sub-descriptors as USB_AUDIO_MIC_DESCRIPTOR (except one isochronous data // endpoint, no feedback endpoint), so this is identical to -// TUD_AUDIO_MIC_ONE_CH_DESC_LEN -- but spell it out independently so the two +// USB_AUDIO_MIC_DESC_LEN -- but spell it out independently so the two // can diverge later (e.g. stereo) without silently mis-sizing the descriptor. // These TUD_AUDIO_DESC_*_LEN macros come from TinyUSB's usbd.h; this expression // is only expanded where that header is already included (never at the point // tusb_config.h includes us), so the header stays dependency-free. -#define USB_AUDIO_SPEAKER_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ +#define USB_AUDIO_SPEAKER_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + TUD_AUDIO_DESC_STD_AC_LEN \ + TUD_AUDIO_DESC_CS_AC_LEN \ + TUD_AUDIO_DESC_CLK_SRC_LEN \ + /* speaker chain: USB-streaming input terminal -> feature unit -> speaker */ \ + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ + /* speaker AudioStreaming interface (alt 0 + alt 1 with OUT endpoint) */ \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define USB_AUDIO_SPEAKER_TWO_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + /* speaker chain: USB-streaming input terminal -> feature unit -> speaker */ \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + /* speaker AudioStreaming interface (alt 0 + alt 1 with OUT endpoint) */ \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define USB_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ + /* mic AudioStreaming interface (alt 0 + alt 1 with IN endpoint) */ \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + + TUD_AUDIO_DESC_CS_AS_INT_LEN \ + + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ + + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) + +#define USB_AUDIO_MIC_TWO_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + + TUD_AUDIO_DESC_STD_AC_LEN \ + + TUD_AUDIO_DESC_CS_AC_LEN \ + + TUD_AUDIO_DESC_CLK_SRC_LEN \ + /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ + + TUD_AUDIO_DESC_INPUT_TERM_LEN \ + + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + /* mic AudioStreaming interface (alt 0 + alt 1 with IN endpoint) */ \ + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + TUD_AUDIO_DESC_STD_AS_INT_LEN \ + TUD_AUDIO_DESC_CS_AS_INT_LEN \ diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index a10cd712226..12f32988bc2 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -142,9 +142,9 @@ extern "C" { #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_N_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_MAX_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) // Deep software FIFO so the 1 ms refill keeps clear of the underrun floor. #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -154,9 +154,9 @@ extern "C" { // IN sizing above. #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_N_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_MAX_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) // Deep software FIFO so the 1 ms drain keeps clear of the overrun ceiling. #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) #endif From ff4b108b746a68122ea1bf6d1447c448ffb11b7a Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 18:09:29 -0500 Subject: [PATCH 04/11] Fix descriptor declaration --- shared-module/usb_audio/__init__.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index 113f503ee66..add1ec1c972 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -382,7 +382,6 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de const uint8_t ep_in = descriptor_counts->current_endpoint + 1; usb_add_interface_string(*current_interface_string, "CircuitPython Headset"); - (*current_interface_string)++; const uint8_t usb_audio_descriptor[] = { USB_AUDIO_HEADSET_DESCRIPTOR( @@ -399,6 +398,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; usb_audio_mic_as_itf = descriptor_counts->current_interface + 2; + (*current_interface_string)++; // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus // one OUT and one IN endpoint. descriptor_counts->current_interface += 3; @@ -413,7 +413,6 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de if (usb_audio_direction_is_output()) { usb_add_interface_string(*current_interface_string, "CircuitPython Speaker"); - (*current_interface_string)++; // The AudioStreaming interface follows the AudioControl interface. usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; @@ -429,6 +428,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) }; + (*current_interface_string)++; // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { @@ -450,8 +450,9 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) }; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. - descriptor_counts->current_interface += 3; + (*current_interface_string)++; + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two OUT endpoints. + descriptor_counts->current_interface += 2; if (!forced_iso_ep) { descriptor_counts->num_out_endpoints += 2; descriptor_counts->current_endpoint += 2; @@ -464,12 +465,11 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de } usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); - (*current_interface_string)++; // The AudioStreaming interface follows the AudioControl interface. usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; - if (usb_audio_channel_count == 2) { + if (usb_audio_channel_count == 1) { const uint8_t usb_audio_descriptor[] = { USB_AUDIO_MIC_ONE_CH_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, @@ -480,6 +480,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) }; + (*current_interface_string)++; // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { @@ -501,8 +502,9 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) }; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. - descriptor_counts->current_interface += 3; + (*current_interface_string)++; + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two IN endpoints. + descriptor_counts->current_interface += 2; if (!forced_iso_ep) { descriptor_counts->num_in_endpoints += 2; descriptor_counts->current_endpoint += 2; From a0a6b79701179cf5f0f3a7321826e98a6d4797ad Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 18:17:29 -0500 Subject: [PATCH 05/11] Revert dynamic ring buffer --- shared-module/usb_audio/USBSpeaker.c | 18 +++++------------- shared-module/usb_audio/USBSpeaker.h | 13 +++++++++++-- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index 63c75dbf5b5..58601c69ed2 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -15,6 +15,11 @@ #include "tusb.h" +// The ring is sized independently of the TinyUSB headers (see USBSpeaker.h); +// check it still matches the OUT endpoint's software FIFO so the push side can be +// reasoned about against the USB plumbing. +MP_STATIC_ASSERT(USB_AUDIO_SPEAKER_RING_SIZE == CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ); + // Only one speaker can be fed by the single USB OUT endpoint at a time. This // points at the most recently constructed USBSpeaker, or NULL when none exists, // mirroring active_microphone in USBMicrophone.c. The USB background task pushes @@ -43,19 +48,6 @@ void common_hal_usb_audio_usbspeaker_construct(usb_audio_usbspeaker_obj_t *self) } memset(self->output_buffer, 0, self->base.max_buffer_length); - // The ring is sized independently of the TinyUSB headers; check it still - // matches the OUT endpoint's software FIFO so the push side can be reasoned - // about against the USB plumbing. - self->ring_len = 16 * ((usb_audio_sample_rate / 1000 + 1) * (USB_AUDIO_N_BYTES_PER_SAMPLE * usb_audio_channel_count)); - assert(self->ring_len == CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ); - - self->ring = m_malloc_without_collect(self->ring_len); - if (self->ring == NULL) { - common_hal_usb_audio_usbspeaker_deinit(self); - m_malloc_fail(self->ring_len); - } - memset(self->ring, 0, self->ring_len); - self->ring_head = 0; self->ring_tail = 0; self->ring_count = 0; diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index 1483ff89085..d1694cd4745 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -22,6 +22,16 @@ // comfortably inside the ring. #define USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER (128) +// Bytes per audio frame in the negotiated UAC2 format (mono 16-bit for v1). +#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) + +// Host -> board receive ring size. Mirrors CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ +// (16 * the full-speed OUT wMaxPacketSize) but is spelled out from the format +// constants so this struct definition stays free of the TinyUSB headers. A +// MP_STATIC_ASSERT in USBSpeaker.c checks the two stay equal. +#define USB_AUDIO_SPEAKER_OUT_PACKET_SIZE ((USB_AUDIO_MAX_SAMPLE_RATE / 1000 + 1) * USB_AUDIO_SPEAKER_BYTES_PER_FRAME) +#define USB_AUDIO_SPEAKER_RING_SIZE (16 * USB_AUDIO_SPEAKER_OUT_PACKET_SIZE) + typedef struct usb_audio_usbspeaker_obj { // First member so the object can be used directly as an audiosample source. audiosample_base_t base; @@ -30,8 +40,7 @@ typedef struct usb_audio_usbspeaker_obj { // background task via usb_audio_usbspeaker_background_drain() and drained by // get_buffer(). Single producer (task), single consumer (output DMA ISR); // see the concurrency notes in USBSpeaker.c. - uint8_t *ring; - size_t ring_len; + uint8_t ring[USB_AUDIO_SPEAKER_RING_SIZE]; size_t ring_head; // next write offset size_t ring_tail; // next read offset size_t ring_count; // valid bytes currently in the ring From 2d9d805ab734b5125919262ac607fcd035826127 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 22:41:10 -0500 Subject: [PATCH 06/11] Assume stereo operation --- shared-bindings/usb_audio/USBMicrophone.c | 1 + shared-bindings/usb_audio/__init__.c | 20 +- shared-module/usb_audio/USBMicrophone.c | 17 +- shared-module/usb_audio/USBSpeaker.c | 71 +++--- shared-module/usb_audio/USBSpeaker.h | 7 +- shared-module/usb_audio/__init__.c | 226 +++++------------- shared-module/usb_audio/__init__.h | 3 +- .../usb_audio/usb_audio_descriptors.h | 44 +--- supervisor/shared/usb/tusb_config.h | 8 +- 9 files changed, 133 insertions(+), 264 deletions(-) diff --git a/shared-bindings/usb_audio/USBMicrophone.c b/shared-bindings/usb_audio/USBMicrophone.c index ff53d812e49..c66c78942dd 100644 --- a/shared-bindings/usb_audio/USBMicrophone.c +++ b/shared-bindings/usb_audio/USBMicrophone.c @@ -12,6 +12,7 @@ #include "shared-bindings/usb_audio/USBMicrophone.h" #include "shared-bindings/util.h" #include "shared-module/usb_audio/__init__.h" +#include "shared-module/usb_audio/usb_audio_descriptors.h" //| class USBMicrophone: //| """Streams an audio sample to the host computer as a USB Audio Class microphone. diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 77c3201fcb5..00ec82f10ae 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -19,7 +19,7 @@ //| Audio Class (UAC2) microphone: the board is the audio *source* and streams //| samples to the host over a USB isochronous IN endpoint. //| -//| This mode requires 1 (mono) or 2 (stereo / bidirectional) IN endpoint and 2 interfaces. +//| This mode requires 2 IN endpoints and 2 interfaces. //| Generally, microcontrollers have a limit on the number of endpoints. If you exceed the number //| of endpoints, CircuitPython will automatically enter Safe Mode. Even in this case, you may be //| able to enable USB audio by also disabling other USB functions, such as @@ -32,7 +32,7 @@ //| //| # boot.py //| import usb_audio -//| usb_audio.enable(sample_rate=16000, channel_count=1, bits_per_sample=16) +//| usb_audio.enable(sample_rate=16000, bits_per_sample=16) //| //| .. code-block:: py //| @@ -64,9 +64,6 @@ //| The ``sample_rate`` and ``channel_count`` of the sample played must match the values passed to `enable`, //| and the sample must be 16-bit signed; otherwise ``play`` raises a ``ValueError``. //| -//| If both speaker and headphone features are enabled (bidirectional), only mono operation is -//| permitted. -//| //| This interface is experimental and may change without notice even in stable //| versions of CircuitPython.""" //| @@ -86,7 +83,6 @@ //| def enable( //| sample_rate: int = 16000, //| channel_count: int = 1, -//| bits_per_sample: int = 16, //| microphone: bool = True, //| speaker: bool = False, //| ) -> None: @@ -105,11 +101,10 @@ //| //| static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) { - enum { ARG_sample_rate, ARG_channel_count, ARG_bits_per_sample, ARG_microphone, ARG_speaker }; + enum { ARG_sample_rate, ARG_channel_count, ARG_microphone, ARG_speaker }; static const mp_arg_t allowed_args[] = { { MP_QSTR_sample_rate, MP_ARG_INT, { .u_int = 16000 } }, { MP_QSTR_channel_count, MP_ARG_INT, { .u_int = 1 } }, - { MP_QSTR_bits_per_sample, MP_ARG_INT, { .u_int = 16 } }, { MP_QSTR_microphone, MP_ARG_BOOL, { .u_bool = true } }, { MP_QSTR_speaker, MP_ARG_BOOL, { .u_bool = false } }, }; @@ -117,8 +112,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_int_t sample_rate = mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, USB_AUDIO_MAX_SAMPLE_RATE, MP_QSTR_sample_rate); - mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_MAX_CHANNELS, MP_QSTR_channel_count); - mp_int_t bits_per_sample = mp_arg_validate_int(args[ARG_bits_per_sample].u_int, USB_AUDIO_BITS_PER_SAMPLE, MP_QSTR_bits_per_sample); + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_N_CHANNELS, MP_QSTR_channel_count); bool microphone = args[ARG_microphone].u_bool; bool speaker = args[ARG_speaker].u_bool; @@ -126,11 +120,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_raise_ValueError(MP_ERROR_TEXT("At least one of microphone and speaker must be enabled")); } - if (microphone && speaker && channel_count == 2) { - mp_raise_ValueError(MP_ERROR_TEXT("Only mono operation is supported when microphone and speaker are both enabled")); - } - - if (!shared_module_usb_audio_enable(sample_rate, channel_count, bits_per_sample, microphone, speaker)) { + if (!shared_module_usb_audio_enable(sample_rate, channel_count, microphone, speaker)) { mp_raise_RuntimeError(MP_ERROR_TEXT("Cannot change USB devices now")); } diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index c9245b2edb2..a1228ad1c64 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -11,6 +11,7 @@ #include "shared-bindings/usb_audio/USBMicrophone.h" #include "shared-module/usb_audio/__init__.h" +#include "shared-module/usb_audio/usb_audio_descriptors.h" #include "shared-module/audiocore/__init__.h" // Only one microphone may feed the single USB IN endpoint at a time. This points @@ -53,7 +54,7 @@ void common_hal_usb_audio_usbmicrophone_play(usb_audio_usbmicrophone_obj_t *self if (audiosample_get_channel_count(sample_base) != usb_audio_channel_count) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_channel_count); } - if (audiosample_get_bits_per_sample(sample_base) != usb_audio_bits_per_sample) { + if (audiosample_get_bits_per_sample(sample_base) != USB_AUDIO_BITS_PER_SAMPLE) { mp_raise_ValueError_varg(MP_ERROR_TEXT("The sample's %q does not match"), MP_QSTR_bits_per_sample); } if (!sample_base->samples_signed) { @@ -138,8 +139,18 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { } } - size_t n = MIN(self->buffer_length, max_bytes - filled); - memcpy(out + filled, self->buffer, n); + size_t n; + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + n = MIN(self->buffer_length, max_bytes - filled); + memcpy(out + filled, self->buffer, n); + } else { + n = MIN(self->buffer_length << 1, max_bytes - filled); + int16_t *word_buffer = (int16_t *)self->buffer; + int16_t *word_out = (int16_t *)(out + filled); + for (size_t i = 0; i < n / USB_AUDIO_N_BYTES_PER_SAMPLE; i++) { + word_out[i] = word_buffer[i >> 1]; + } + } self->buffer += n; self->buffer_length -= n; filled += n; diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index 58601c69ed2..310eb3cd7d2 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -32,22 +32,12 @@ void common_hal_usb_audio_usbspeaker_construct(usb_audio_usbspeaker_obj_t *self) // format we present is 16-bit signed LE PCM, which is exactly what the // CircuitPython audio pipeline carries, so no conversion is needed. self->base.sample_rate = usb_audio_sample_rate; - self->base.bits_per_sample = usb_audio_bits_per_sample; + self->base.bits_per_sample = USB_AUDIO_BITS_PER_SAMPLE; self->base.channel_count = usb_audio_channel_count; self->base.samples_signed = true; self->base.single_buffer = false; + self->base.max_buffer_length = USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE; - // Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches - // audiocore.RawSample's convention where base.max_buffer_length is the whole - // buffer and get_buffer() returns half of it. - self->base.max_buffer_length = (2 * USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER * USB_AUDIO_N_BYTES_PER_SAMPLE * usb_audio_channel_count); - self->output_buffer = m_malloc_without_collect(self->base.max_buffer_length); - if (self->output_buffer == NULL) { - common_hal_usb_audio_usbspeaker_deinit(self); - m_malloc_fail(self->base.max_buffer_length); - } - memset(self->output_buffer, 0, self->base.max_buffer_length); - self->ring_head = 0; self->ring_tail = 0; self->ring_count = 0; @@ -114,32 +104,32 @@ void usb_audio_usbspeaker_background_drain(const uint8_t *in, size_t n) { if (self == NULL || n == 0) { return; } - if (n >= self->ring_len) { + if (n >= USB_AUDIO_SPEAKER_RING_SIZE) { // A single chunk larger than the whole ring can only contribute its // newest tail. (Cannot happen with USB packets << ring size, but keep // the copies provably in-bounds.) - in += n - self->ring_len; - n = self->ring_len; + in += n - USB_AUDIO_SPEAKER_RING_SIZE; + n = USB_AUDIO_SPEAKER_RING_SIZE; } common_hal_mcu_disable_interrupts(); - size_t free_space = self->ring_len - self->ring_count; + size_t free_space = USB_AUDIO_SPEAKER_RING_SIZE - self->ring_count; if (n > free_space) { // Overrun: advance the read cursor past the oldest bytes we're about to // overwrite, keeping latency bounded and following the newest host audio. size_t drop = n - free_space; - self->ring_tail = (self->ring_tail + drop) % self->ring_len; + self->ring_tail = (self->ring_tail + drop) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count -= drop; } // Copy in one or two segments, wrapping at the end of the ring. - size_t first = MIN(n, self->ring_len - self->ring_head); + size_t first = MIN(n, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_head); memcpy(&self->ring[self->ring_head], in, first); if (n > first) { memcpy(&self->ring[0], in + first, n - first); } - self->ring_head = (self->ring_head + n) % self->ring_len; + self->ring_head = (self->ring_head + n) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count += n; common_hal_mcu_enable_interrupts(); @@ -167,12 +157,12 @@ uint32_t common_hal_usb_audio_usbspeaker_read(usb_audio_usbspeaker_obj_t *self, // but stay defensive so the returned count is always a whole number). to_copy -= to_copy % bytes_per_frame; - size_t first = MIN(to_copy, self->ring_len - self->ring_tail); + size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); memcpy(buffer, &self->ring[self->ring_tail], first); if (to_copy > first) { memcpy((uint8_t *)buffer + first, &self->ring[0], to_copy - first); } - self->ring_tail = (self->ring_tail + to_copy) % self->ring_len; + self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count -= to_copy; common_hal_mcu_enable_interrupts(); @@ -203,35 +193,56 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker uint32_t half = self->base.max_buffer_length / 2; uint8_t *out = self->output_buffer + half * self->output_index; + int16_t *word_out = (int16_t *)out; + int16_t *word_ring = (int16_t *)self->ring; self->output_index = 1 - self->output_index; // Consumer side of the SPSC ring (runs in the output backend's refill ISR). // It is never preempted by the producer, so no interrupt guard is required. size_t to_copy = MIN(self->ring_count, (size_t)half); - size_t first = MIN(to_copy, self->ring_len - self->ring_tail); - memcpy(out, &self->ring[self->ring_tail], first); - if (to_copy > first) { - memcpy(out + first, &self->ring[0], to_copy - first); + size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + memcpy(out, &self->ring[self->ring_tail], first); + if (to_copy > first) { + memcpy(out + first, &self->ring[0], to_copy - first); + } + } else { + for (size_t i = self->ring_tail / USB_AUDIO_N_BYTES_PER_SAMPLE; i < first / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { + word_out[i >> 1] = word_ring[i]; + } + if (to_copy > first) { + for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { + word_out[(first / USB_AUDIO_N_BYTES_PER_SAMPLE) + (i >> 1)] = word_ring[i]; + } + } } - self->ring_tail = (self->ring_tail + to_copy) % self->ring_len; + self->ring_tail = (self->ring_tail + to_copy) % USB_AUDIO_SPEAKER_RING_SIZE; self->ring_count -= to_copy; if (to_copy < half) { // Underrun: pad the remainder with silence. Samples are signed, so // silence is 0. This is the consume-side of the pacing failure mode // tracked in the usb-audio-artifact-pacing memory: we never spin. - memset(out + to_copy, 0, half - to_copy); + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + memset(out + to_copy, 0, half - to_copy); + } else { + memset(out + (to_copy >> 1), 0, (half - to_copy) >> 1); + } } - // Mono only for v1, so the single-channel offset is always 0; computed the - // same way as audiocore.RawSample so stereo (interleaved ring) can extend it. + // Computed the same way as audiocore.RawSample so stereo (interleaved ring) + // can extend it. if (single_channel_output) { out += (channel % self->base.channel_count) * (self->base.bits_per_sample / 8); } *buffer = out; - *buffer_length = half; + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + *buffer_length = half; + } else { + *buffer_length = half >> 1; + } // A live USB stream is infinite; never report DONE or the backend would stop. return GET_BUFFER_MORE_DATA; } diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index d1694cd4745..8c06cff519d 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -25,6 +25,11 @@ // Bytes per audio frame in the negotiated UAC2 format (mono 16-bit for v1). #define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) +// Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches +// audiocore.RawSample's convention where base.max_buffer_length is the whole +// buffer and get_buffer() returns half of it. +#define USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE (2 * USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER * USB_AUDIO_SPEAKER_BYTES_PER_FRAME) + // Host -> board receive ring size. Mirrors CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ // (16 * the full-speed OUT wMaxPacketSize) but is spelled out from the format // constants so this struct definition stays free of the TinyUSB headers. A @@ -46,7 +51,7 @@ typedef struct usb_audio_usbspeaker_obj { size_t ring_count; // valid bytes currently in the ring // Owned double-buffer returned to the output backend by get_buffer(). - uint8_t *output_buffer; + uint8_t output_buffer[USB_AUDIO_SPEAKER_OUTPUT_BUFFER_SIZE]; uint8_t output_index; // 0 or 1: which half get_buffer() fills next } usb_audio_usbspeaker_obj_t; diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index add1ec1c972..9d3a244af8e 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -39,15 +39,14 @@ static uint8_t usb_audio_spk_as_itf = 0xff; uint32_t usb_audio_sample_rate; uint8_t usb_audio_channel_count; -uint8_t usb_audio_bits_per_sample; bool usb_audio_microphone_enabled; bool usb_audio_speaker_enabled; // Audio control state surfaced to the host. One extra entry for the master channel 0. -static int8_t usb_audio_mute[USB_AUDIO_MAX_CHANNELS + 1]; -static int16_t usb_audio_volume[USB_AUDIO_MAX_CHANNELS + 1]; +static int8_t usb_audio_mute[USB_AUDIO_N_CHANNELS + 1]; +static int16_t usb_audio_volume[USB_AUDIO_N_CHANNELS + 1]; -bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, mp_int_t bits_per_sample, bool microphone, bool speaker) { +bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker) { if (tud_connected()) { return false; } @@ -64,7 +63,6 @@ bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count usb_audio_sample_rate = sample_rate; usb_audio_channel_count = channel_count; - usb_audio_bits_per_sample = bits_per_sample; usb_audio_microphone_enabled = microphone; usb_audio_speaker_enabled = speaker; usb_audio_is_enabled = true; @@ -147,7 +145,7 @@ void usb_audio_setup_singletons(void) { // terminal roles reverse: the input terminal is the USB-streaming side and the // output terminal is the desktop speaker, and the AS interface links the input // terminal (0x01). Async feedback for true clock matching is a later step. -#define USB_AUDIO_SPEAKER_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ +#define USB_AUDIO_SPEAKER_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ /* Standard Interface Association Descriptor (IAD) */ \ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ /* Standard AC Interface Descriptor(4.7.1) */ \ @@ -157,35 +155,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ - /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ - /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ - /* Standard AS Interface Descriptor(4.9.1) -- alt 0, zero bandwidth */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00), \ - /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ - /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ - TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (uint8_t)((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ - TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) - -#define USB_AUDIO_SPEAKER_TWO_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epout, _epsize) \ - /* Standard Interface Association Descriptor (IAD) */ \ - TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ - /* Standard AC Interface Descriptor(4.7.1) */ \ - TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ - /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_DESKTOP_SPEAKER, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ - /* Clock Source Descriptor(4.7.2.1) */ \ - TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ - /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -195,7 +165,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -208,37 +178,7 @@ void usb_audio_setup_singletons(void) { // (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so // the streaming alt-setting declares a single IN endpoint (_nEPs = 0x00). Async // feedback for true clock matching is a later step. -#define USB_AUDIO_MIC_ONE_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ - /* Standard Interface Association Descriptor (IAD) */ \ - TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ - /* Standard AC Interface Descriptor(4.7.1) */ \ - TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ - /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ - /* Clock Source Descriptor(4.7.2.1) */ \ - TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ - /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ - /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ - /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ - /* Standard AS Interface Descriptor(4.9.1) */ \ - /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ - /* Standard AS Interface Descriptor(4.9.1) */ \ - /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ - /* Class-Specific AS Interface Descriptor(4.9.2) */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ - TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ - /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ - TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) - -#define USB_AUDIO_MIC_TWO_CH_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ +#define USB_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ /* Standard Interface Association Descriptor (IAD) */ \ TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ /* Standard AC Interface Descriptor(4.7.1) */ \ @@ -248,7 +188,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -260,7 +200,7 @@ void usb_audio_setup_singletons(void) { /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ /* Class-Specific AS Interface Descriptor(4.9.2) */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x02, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -268,7 +208,7 @@ void usb_audio_setup_singletons(void) { /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) -// Hand-rolled UAC2 mono headset (microphone + speaker both enabled): one audio function +// Hand-rolled UAC2 headset (microphone + speaker both enabled): one audio function // presenting both a speaker (host -> board OUT) and a microphone (board -> host // IN) at once. This combines USB_AUDIO_SPEAKER_DESCRIPTOR's speaker chain with // USB_AUDIO_MIC_DESCRIPTOR's mic chain under a single IAD. The two chains @@ -284,21 +224,21 @@ void usb_audio_setup_singletons(void) { /* Standard AC Interface Descriptor(4.7.1) */ \ TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ /* Class-Specific AC Interface Header Descriptor(4.7.2) -- clock + both chains */ \ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + 2 * (TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN), /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + 2 * (TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN), /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ /* Clock Source Descriptor(4.7.2.1) -- shared by both chains */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* --- Speaker chain (host -> board) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Mic chain (board -> host) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- generic microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming out to the host */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Speaker AudioStreaming interface (_itfnum + 1) --- */ \ @@ -307,7 +247,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the speaker input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -320,7 +260,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one IN endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the mic output terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ 0x01, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -344,17 +284,9 @@ size_t usb_audio_descriptor_length(void) { return USB_AUDIO_HEADSET_DESC_LEN; } if (usb_audio_direction_is_output()) { - if (usb_audio_channel_count == 1) { - return USB_AUDIO_SPEAKER_ONE_CH_DESC_LEN; - } else { - return USB_AUDIO_SPEAKER_TWO_CH_DESC_LEN; - } - } - if (usb_audio_channel_count == 1) { - return USB_AUDIO_MIC_ONE_CH_DESC_LEN; - } else { - return USB_AUDIO_MIC_TWO_CH_DESC_LEN; + return USB_AUDIO_SPEAKER_DESC_LEN; } + return USB_AUDIO_MIC_DESC_LEN; } size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *descriptor_counts, uint8_t *current_interface_string) { @@ -400,11 +332,11 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de (*current_interface_string)++; // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus - // one OUT and one IN endpoint. + // two OUT and two IN endpoints. descriptor_counts->current_interface += 3; - descriptor_counts->num_out_endpoints++; - descriptor_counts->num_in_endpoints++; - descriptor_counts->current_endpoint += 2; + descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS * 2; memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -417,103 +349,55 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // The AudioStreaming interface follows the AudioControl interface. usb_audio_spk_as_itf = descriptor_counts->current_interface + 1; - if (usb_audio_channel_count == 1) { - const uint8_t usb_audio_descriptor[] = { - USB_AUDIO_SPEAKER_ONE_CH_DESCRIPTOR( - /*_itfnum*/ descriptor_counts->current_interface, - /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, - /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epout*/ iso_ep_num, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) - }; - - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one OUT endpoint. - descriptor_counts->current_interface += 2; - if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints++; - descriptor_counts->current_endpoint++; - } - - memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); - - return sizeof(usb_audio_descriptor); - } else { - const uint8_t usb_audio_descriptor[] = { - USB_AUDIO_SPEAKER_TWO_CH_DESCRIPTOR( - /*_itfnum*/ descriptor_counts->current_interface, - /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, - /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epout*/ iso_ep_num, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) - }; - - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two OUT endpoints. - descriptor_counts->current_interface += 2; - if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints += 2; - descriptor_counts->current_endpoint += 2; - } - - memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); - - return sizeof(usb_audio_descriptor); - } - } - - usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); - - // The AudioStreaming interface follows the AudioControl interface. - usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; - - if (usb_audio_channel_count == 1) { const uint8_t usb_audio_descriptor[] = { - USB_AUDIO_MIC_ONE_CH_DESCRIPTOR( + USB_AUDIO_SPEAKER_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epin*/ iso_ep_num | 0x80, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) + /*_epout*/ iso_ep_num, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) }; (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus one IN endpoint. + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two OUT endpoints. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints++; - descriptor_counts->current_endpoint++; + descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); return sizeof(usb_audio_descriptor); - } else { - const uint8_t usb_audio_descriptor[] = { - USB_AUDIO_MIC_TWO_CH_DESCRIPTOR( - /*_itfnum*/ descriptor_counts->current_interface, - /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, - /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, - /*_epin*/ iso_ep_num | 0x80, - /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) - }; + } - (*current_interface_string)++; - // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two IN endpoints. - descriptor_counts->current_interface += 2; - if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints += 2; - descriptor_counts->current_endpoint += 2; - } + usb_add_interface_string(*current_interface_string, "CircuitPython Microphone"); - memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); + // The AudioStreaming interface follows the AudioControl interface. + usb_audio_mic_as_itf = descriptor_counts->current_interface + 1; - return sizeof(usb_audio_descriptor); + const uint8_t usb_audio_descriptor[] = { + USB_AUDIO_MIC_DESCRIPTOR( + /*_itfnum*/ descriptor_counts->current_interface, + /*_stridx*/ *current_interface_string, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, + /*_epin*/ iso_ep_num | 0x80, + /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) + }; + + (*current_interface_string)++; + // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two IN endpoints. + descriptor_counts->current_interface += 2; + if (!forced_iso_ep) { + descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; } + + memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); + + return sizeof(usb_audio_descriptor); } // --------------------------------------------------------------------+ @@ -660,7 +544,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Mute/volume state is shared across them. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_MAX_CHANNELS) { + if (channelNum > USB_AUDIO_N_CHANNELS) { return false; } switch (ctrlSel) { @@ -711,7 +595,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Feature unit (mute/volume) for either the speaker or mic chain. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_MAX_CHANNELS) { + if (channelNum > USB_AUDIO_N_CHANNELS) { return false; } switch (ctrlSel) { diff --git a/shared-module/usb_audio/__init__.h b/shared-module/usb_audio/__init__.h index 8ec13f90853..5730e6ef60b 100644 --- a/shared-module/usb_audio/__init__.h +++ b/shared-module/usb_audio/__init__.h @@ -17,7 +17,7 @@ // called before USB is connected (i.e. from boot.py); they return false // otherwise. At least one of microphone/speaker must be true; enabling both // presents a combined headset. -bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, mp_int_t bits_per_sample, bool microphone, bool speaker); +bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker); bool shared_module_usb_audio_disable(void); // True once enable() has been called successfully. @@ -36,7 +36,6 @@ bool usb_audio_speaker_streaming(void); // Negotiated audio format, valid when usb_audio_enabled() is true. extern uint32_t usb_audio_sample_rate; extern uint8_t usb_audio_channel_count; -extern uint8_t usb_audio_bits_per_sample; // Which streams were requested in enable(), valid when usb_audio_enabled() is // true. Both true presents a combined headset. diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index d94c8acabdf..597a78ab82c 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -27,10 +27,10 @@ size_t usb_audio_descriptor_length(void); // The isochronous IN endpoint's wMaxPacketSize in the USB descriptor is computed // for this rate, so it is the highest rate usb_audio.enable() will accept. #define USB_AUDIO_MAX_SAMPLE_RATE (48000) -#define USB_AUDIO_MAX_CHANNELS (2) -// 16-bit signed LE PCM. +// 16-bit signed LE stereo PCM. #define USB_AUDIO_N_BYTES_PER_SAMPLE (2) +#define USB_AUDIO_N_CHANNELS (2) #define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) // Endpoint number for the single isochronous audio data endpoint. Most device @@ -76,23 +76,7 @@ size_t usb_audio_descriptor_length(void); // These TUD_AUDIO_DESC_*_LEN macros come from TinyUSB's usbd.h; this expression // is only expanded where that header is already included (never at the point // tusb_config.h includes us), so the header stays dependency-free. -#define USB_AUDIO_SPEAKER_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ - + TUD_AUDIO_DESC_STD_AC_LEN \ - + TUD_AUDIO_DESC_CS_AC_LEN \ - + TUD_AUDIO_DESC_CLK_SRC_LEN \ - /* speaker chain: USB-streaming input terminal -> feature unit -> speaker */ \ - + TUD_AUDIO_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ - + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ - /* speaker AudioStreaming interface (alt 0 + alt 1 with OUT endpoint) */ \ - + TUD_AUDIO_DESC_STD_AS_INT_LEN \ - + TUD_AUDIO_DESC_STD_AS_INT_LEN \ - + TUD_AUDIO_DESC_CS_AS_INT_LEN \ - + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ - + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ - + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) - -#define USB_AUDIO_SPEAKER_TWO_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ +#define USB_AUDIO_SPEAKER_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + TUD_AUDIO_DESC_STD_AC_LEN \ + TUD_AUDIO_DESC_CS_AC_LEN \ + TUD_AUDIO_DESC_CLK_SRC_LEN \ @@ -108,23 +92,7 @@ size_t usb_audio_descriptor_length(void); + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) -#define USB_AUDIO_MIC_ONE_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ - + TUD_AUDIO_DESC_STD_AC_LEN \ - + TUD_AUDIO_DESC_CS_AC_LEN \ - + TUD_AUDIO_DESC_CLK_SRC_LEN \ - /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ - + TUD_AUDIO_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ - + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ - /* mic AudioStreaming interface (alt 0 + alt 1 with IN endpoint) */ \ - + TUD_AUDIO_DESC_STD_AS_INT_LEN \ - + TUD_AUDIO_DESC_STD_AS_INT_LEN \ - + TUD_AUDIO_DESC_CS_AS_INT_LEN \ - + TUD_AUDIO_DESC_TYPE_I_FORMAT_LEN \ - + TUD_AUDIO_DESC_STD_AS_ISO_EP_LEN \ - + TUD_AUDIO_DESC_CS_AS_ISO_EP_LEN) - -#define USB_AUDIO_MIC_TWO_CH_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ +#define USB_AUDIO_MIC_DESC_LEN (TUD_AUDIO_DESC_IAD_LEN \ + TUD_AUDIO_DESC_STD_AC_LEN \ + TUD_AUDIO_DESC_CS_AC_LEN \ + TUD_AUDIO_DESC_CLK_SRC_LEN \ @@ -155,11 +123,11 @@ size_t usb_audio_descriptor_length(void); + TUD_AUDIO_DESC_CLK_SRC_LEN \ /* speaker chain: USB-streaming input terminal -> feature unit -> speaker */ \ + TUD_AUDIO_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ /* mic chain: microphone input terminal -> feature unit -> USB-streaming out */ \ + TUD_AUDIO_DESC_INPUT_TERM_LEN \ - + TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN \ + + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN \ + TUD_AUDIO_DESC_OUTPUT_TERM_LEN \ /* speaker AudioStreaming interface (alt 0 + alt 1 with OUT endpoint) */ \ + TUD_AUDIO_DESC_STD_AS_INT_LEN \ diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index 12f32988bc2..a10cd712226 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -142,9 +142,9 @@ extern "C" { #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_MAX_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_N_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) // Deep software FIFO so the 1 ms refill keeps clear of the underrun floor. #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -154,9 +154,9 @@ extern "C" { // IN sizing above. #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 #define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_MAX_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_N_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) // Deep software FIFO so the 1 ms drain keeps clear of the overrun ceiling. #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) #endif From b26ef7d3ab67fcbd9978d077828ee6177a14dfd3 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Tue, 7 Jul 2026 22:43:29 -0500 Subject: [PATCH 07/11] Rename audio constants --- shared-bindings/usb_audio/__init__.c | 2 +- shared-module/usb_audio/USBMicrophone.c | 4 +- shared-module/usb_audio/USBSpeaker.c | 12 ++--- shared-module/usb_audio/USBSpeaker.h | 4 +- shared-module/usb_audio/__init__.c | 46 +++++++++---------- .../usb_audio/usb_audio_descriptors.h | 8 ++-- 6 files changed, 38 insertions(+), 38 deletions(-) diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 00ec82f10ae..7bf20807f3e 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -112,7 +112,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_int_t sample_rate = mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, USB_AUDIO_MAX_SAMPLE_RATE, MP_QSTR_sample_rate); - mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_N_CHANNELS, MP_QSTR_channel_count); + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_MAX_CHANNELS, MP_QSTR_channel_count); bool microphone = args[ARG_microphone].u_bool; bool speaker = args[ARG_speaker].u_bool; diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index a1228ad1c64..c10258477c2 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -140,14 +140,14 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { } size_t n; - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { n = MIN(self->buffer_length, max_bytes - filled); memcpy(out + filled, self->buffer, n); } else { n = MIN(self->buffer_length << 1, max_bytes - filled); int16_t *word_buffer = (int16_t *)self->buffer; int16_t *word_out = (int16_t *)(out + filled); - for (size_t i = 0; i < n / USB_AUDIO_N_BYTES_PER_SAMPLE; i++) { + for (size_t i = 0; i < n / USB_AUDIO_BYTES_PER_SAMPLE; i++) { word_out[i] = word_buffer[i >> 1]; } } diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index 310eb3cd7d2..e2f2bf4f525 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -202,18 +202,18 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker size_t to_copy = MIN(self->ring_count, (size_t)half); size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { memcpy(out, &self->ring[self->ring_tail], first); if (to_copy > first) { memcpy(out + first, &self->ring[0], to_copy - first); } } else { - for (size_t i = self->ring_tail / USB_AUDIO_N_BYTES_PER_SAMPLE; i < first / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { + for (size_t i = self->ring_tail / USB_AUDIO_BYTES_PER_SAMPLE; i < first / USB_AUDIO_BYTES_PER_SAMPLE; i += 2) { word_out[i >> 1] = word_ring[i]; } if (to_copy > first) { - for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { - word_out[(first / USB_AUDIO_N_BYTES_PER_SAMPLE) + (i >> 1)] = word_ring[i]; + for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_BYTES_PER_SAMPLE; i += 2) { + word_out[(first / USB_AUDIO_BYTES_PER_SAMPLE) + (i >> 1)] = word_ring[i]; } } } @@ -224,7 +224,7 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker // Underrun: pad the remainder with silence. Samples are signed, so // silence is 0. This is the consume-side of the pacing failure mode // tracked in the usb-audio-artifact-pacing memory: we never spin. - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { memset(out + to_copy, 0, half - to_copy); } else { memset(out + (to_copy >> 1), 0, (half - to_copy) >> 1); @@ -238,7 +238,7 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker } *buffer = out; - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { *buffer_length = half; } else { *buffer_length = half >> 1; diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index 8c06cff519d..27007ad275d 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -22,8 +22,8 @@ // comfortably inside the ring. #define USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER (128) -// Bytes per audio frame in the negotiated UAC2 format (mono 16-bit for v1). -#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) +// Bytes per audio frame in the negotiated UAC2 format (stereo 16-bit). +#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_BYTES_PER_SAMPLE * USB_AUDIO_MAX_CHANNELS) // Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches // audiocore.RawSample's convention where base.max_buffer_length is the whole diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index 9d3a244af8e..10bc368616a 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -43,8 +43,8 @@ bool usb_audio_microphone_enabled; bool usb_audio_speaker_enabled; // Audio control state surfaced to the host. One extra entry for the master channel 0. -static int8_t usb_audio_mute[USB_AUDIO_N_CHANNELS + 1]; -static int16_t usb_audio_volume[USB_AUDIO_N_CHANNELS + 1]; +static int8_t usb_audio_mute[USB_AUDIO_MAX_CHANNELS + 1]; +static int16_t usb_audio_volume[USB_AUDIO_MAX_CHANNELS + 1]; bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker) { if (tud_connected()) { @@ -53,7 +53,7 @@ bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count // One scratch chunk (1 ms at the sample rate); we loop until the FIFO reaches // the setpoint. - usb_audio_mic_samples_len = sample_rate / 1000 * channel_count * USB_AUDIO_N_BYTES_PER_SAMPLE; + usb_audio_mic_samples_len = sample_rate / 1000 * channel_count * USB_AUDIO_BYTES_PER_SAMPLE; usb_audio_mic_samples = (int16_t *)m_malloc_without_collect(usb_audio_mic_samples_len); if (usb_audio_mic_samples == NULL) { m_malloc_fail(usb_audio_mic_samples_len); @@ -155,7 +155,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -165,7 +165,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -188,7 +188,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -200,7 +200,7 @@ void usb_audio_setup_singletons(void) { /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ /* Class-Specific AS Interface Descriptor(4.9.2) */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -229,14 +229,14 @@ void usb_audio_setup_singletons(void) { TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* --- Speaker chain (host -> board) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Mic chain (board -> host) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- generic microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming out to the host */ \ @@ -247,7 +247,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the speaker input terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -260,7 +260,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one IN endpoint */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the mic output terminal */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -319,7 +319,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_HEADSET_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epout*/ ep_out, /*_epin*/ ep_in | 0x80, @@ -334,9 +334,9 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus // two OUT and two IN endpoints. descriptor_counts->current_interface += 3; - descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; - descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS * 2; + descriptor_counts->num_out_endpoints += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->num_in_endpoints += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS * 2; memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -353,7 +353,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_SPEAKER_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epout*/ iso_ep_num, /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) @@ -363,8 +363,8 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two OUT endpoints. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; + descriptor_counts->num_out_endpoints += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -381,7 +381,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_MIC_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epin*/ iso_ep_num | 0x80, /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -391,8 +391,8 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two IN endpoints. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; + descriptor_counts->num_in_endpoints += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -544,7 +544,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Mute/volume state is shared across them. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_N_CHANNELS) { + if (channelNum > USB_AUDIO_MAX_CHANNELS) { return false; } switch (ctrlSel) { @@ -595,7 +595,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Feature unit (mute/volume) for either the speaker or mic chain. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_N_CHANNELS) { + if (channelNum > USB_AUDIO_MAX_CHANNELS) { return false; } switch (ctrlSel) { diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index 597a78ab82c..a6622c8f8eb 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -27,11 +27,11 @@ size_t usb_audio_descriptor_length(void); // The isochronous IN endpoint's wMaxPacketSize in the USB descriptor is computed // for this rate, so it is the highest rate usb_audio.enable() will accept. #define USB_AUDIO_MAX_SAMPLE_RATE (48000) +#define USB_AUDIO_MAX_CHANNELS (2) -// 16-bit signed LE stereo PCM. -#define USB_AUDIO_N_BYTES_PER_SAMPLE (2) -#define USB_AUDIO_N_CHANNELS (2) -#define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) +// 16-bit signed LE PCM. +#define USB_AUDIO_BYTES_PER_SAMPLE (2) +#define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_BYTES_PER_SAMPLE * 8) // Endpoint number for the single isochronous audio data endpoint. Most device // controllers accept an ISO endpoint on any number, so the descriptor builder From 3a2934f42b9a368ac742c6dd17bbd07983d91063 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 8 Jul 2026 08:36:02 -0500 Subject: [PATCH 08/11] Fix CFG_TUD_AUDIO constants --- supervisor/shared/usb/tusb_config.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index a10cd712226..5530f751d1c 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -141,10 +141,10 @@ extern "C" { #define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 -#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_N_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_TX USB_AUDIO_BYTES_PER_SAMPLE +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX USB_AUDIO_MAX_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) // Deep software FIFO so the 1 ms refill keeps clear of the underrun floor. #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -153,10 +153,10 @@ extern "C" { // descriptor (still mic-only until the speaker descriptor lands). Mirrors the // IN sizing above. #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 -#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX USB_AUDIO_N_BYTES_PER_SAMPLE -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_N_CHANNELS +#define CFG_TUD_AUDIO_FUNC_1_N_BYTES_PER_SAMPLE_RX USB_AUDIO_BYTES_PER_SAMPLE +#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX USB_AUDIO_MAX_CHANNELS // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) // Deep software FIFO so the 1 ms drain keeps clear of the overrun ceiling. #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) #endif From f17c5d9ab8a4f5291dd25dd8404f5d5903ac6c84 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Wed, 8 Jul 2026 09:37:41 -0500 Subject: [PATCH 09/11] Improve mono to stereo conversion on USBMicrophone --- shared-module/usb_audio/USBMicrophone.c | 28 +++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index c10258477c2..172ef5465e3 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -102,6 +102,24 @@ bool common_hal_usb_audio_usbmicrophone_get_paused(usb_audio_usbmicrophone_obj_t return self->playing && self->paused; } +static inline uint32_t copy16lsb(uint32_t val) { + #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) + return __PKHBT(val, val, 16); + #else + val &= 0x0000ffff; + return val | (val << 16); + #endif +} + +static inline uint32_t copy16msb(uint32_t val) { + #if (defined(__ARM_ARCH_7EM__) && (__ARM_ARCH_7EM__ == 1)) + return __PKHTB(val, val, 16); + #else + val &= 0xffff0000; + return val | (val >> 16); + #endif +} + size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { usb_audio_usbmicrophone_obj_t *self = active_microphone; if (self == NULL || !self->playing || self->paused || self->sample == MP_OBJ_NULL) { @@ -145,10 +163,12 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { memcpy(out + filled, self->buffer, n); } else { n = MIN(self->buffer_length << 1, max_bytes - filled); - int16_t *word_buffer = (int16_t *)self->buffer; - int16_t *word_out = (int16_t *)(out + filled); - for (size_t i = 0; i < n / USB_AUDIO_BYTES_PER_SAMPLE; i++) { - word_out[i] = word_buffer[i >> 1]; + uint32_t *word_buffer = (uint32_t *)self->buffer; + uint32_t *word_out = (uint32_t *)(out + filled); + for (size_t i = 0; i < n / sizeof(uint32_t); i += 2) { + uint32_t v = word_buffer[i >> 1]; + word_out[i] = copy16lsb(v); + word_out[i + 1] = copy16msb(v); } } self->buffer += n; From 234461b8ce9ec1364e9bdf91d606bee87f9d7b82 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Mon, 13 Jul 2026 11:18:30 -0500 Subject: [PATCH 10/11] Revert rename audio constants --- shared-bindings/usb_audio/__init__.c | 2 +- shared-module/usb_audio/USBMicrophone.c | 2 +- shared-module/usb_audio/USBSpeaker.c | 12 ++--- shared-module/usb_audio/USBSpeaker.h | 2 +- shared-module/usb_audio/__init__.c | 48 +++++++++---------- .../usb_audio/usb_audio_descriptors.h | 6 +-- supervisor/shared/usb/tusb_config.h | 4 +- 7 files changed, 38 insertions(+), 38 deletions(-) diff --git a/shared-bindings/usb_audio/__init__.c b/shared-bindings/usb_audio/__init__.c index 7bf20807f3e..00ec82f10ae 100644 --- a/shared-bindings/usb_audio/__init__.c +++ b/shared-bindings/usb_audio/__init__.c @@ -112,7 +112,7 @@ static mp_obj_t usb_audio_enable(size_t n_args, const mp_obj_t *pos_args, mp_map mp_arg_parse_all(n_args, pos_args, kw_args, MP_ARRAY_SIZE(allowed_args), allowed_args, args); mp_int_t sample_rate = mp_arg_validate_int_range(args[ARG_sample_rate].u_int, 1, USB_AUDIO_MAX_SAMPLE_RATE, MP_QSTR_sample_rate); - mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_MAX_CHANNELS, MP_QSTR_channel_count); + mp_int_t channel_count = mp_arg_validate_int_range(args[ARG_channel_count].u_int, 1, USB_AUDIO_N_CHANNELS, MP_QSTR_channel_count); bool microphone = args[ARG_microphone].u_bool; bool speaker = args[ARG_speaker].u_bool; diff --git a/shared-module/usb_audio/USBMicrophone.c b/shared-module/usb_audio/USBMicrophone.c index 172ef5465e3..9d2a46b7985 100644 --- a/shared-module/usb_audio/USBMicrophone.c +++ b/shared-module/usb_audio/USBMicrophone.c @@ -158,7 +158,7 @@ size_t usb_audio_usbmicrophone_background_fill(uint8_t *out, size_t max_bytes) { } size_t n; - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { n = MIN(self->buffer_length, max_bytes - filled); memcpy(out + filled, self->buffer, n); } else { diff --git a/shared-module/usb_audio/USBSpeaker.c b/shared-module/usb_audio/USBSpeaker.c index e2f2bf4f525..310eb3cd7d2 100644 --- a/shared-module/usb_audio/USBSpeaker.c +++ b/shared-module/usb_audio/USBSpeaker.c @@ -202,18 +202,18 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker size_t to_copy = MIN(self->ring_count, (size_t)half); size_t first = MIN(to_copy, USB_AUDIO_SPEAKER_RING_SIZE - self->ring_tail); - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { memcpy(out, &self->ring[self->ring_tail], first); if (to_copy > first) { memcpy(out + first, &self->ring[0], to_copy - first); } } else { - for (size_t i = self->ring_tail / USB_AUDIO_BYTES_PER_SAMPLE; i < first / USB_AUDIO_BYTES_PER_SAMPLE; i += 2) { + for (size_t i = self->ring_tail / USB_AUDIO_N_BYTES_PER_SAMPLE; i < first / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { word_out[i >> 1] = word_ring[i]; } if (to_copy > first) { - for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_BYTES_PER_SAMPLE; i += 2) { - word_out[(first / USB_AUDIO_BYTES_PER_SAMPLE) + (i >> 1)] = word_ring[i]; + for (size_t i = 0; i < (to_copy - first) / USB_AUDIO_N_BYTES_PER_SAMPLE; i += 2) { + word_out[(first / USB_AUDIO_N_BYTES_PER_SAMPLE) + (i >> 1)] = word_ring[i]; } } } @@ -224,7 +224,7 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker // Underrun: pad the remainder with silence. Samples are signed, so // silence is 0. This is the consume-side of the pacing failure mode // tracked in the usb-audio-artifact-pacing memory: we never spin. - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { memset(out + to_copy, 0, half - to_copy); } else { memset(out + (to_copy >> 1), 0, (half - to_copy) >> 1); @@ -238,7 +238,7 @@ audioio_get_buffer_result_t usb_audio_usbspeaker_get_buffer(usb_audio_usbspeaker } *buffer = out; - if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_MAX_CHANNELS)) { + if (MP_LIKELY(usb_audio_channel_count == USB_AUDIO_N_CHANNELS)) { *buffer_length = half; } else { *buffer_length = half >> 1; diff --git a/shared-module/usb_audio/USBSpeaker.h b/shared-module/usb_audio/USBSpeaker.h index 27007ad275d..0760676b53a 100644 --- a/shared-module/usb_audio/USBSpeaker.h +++ b/shared-module/usb_audio/USBSpeaker.h @@ -23,7 +23,7 @@ #define USB_AUDIO_SPEAKER_FRAMES_PER_BUFFER (128) // Bytes per audio frame in the negotiated UAC2 format (stereo 16-bit). -#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_BYTES_PER_SAMPLE * USB_AUDIO_MAX_CHANNELS) +#define USB_AUDIO_SPEAKER_BYTES_PER_FRAME (USB_AUDIO_N_BYTES_PER_SAMPLE * USB_AUDIO_N_CHANNELS) // Full owned double-buffer: two halves of FRAMES_PER_BUFFER frames each. Matches // audiocore.RawSample's convention where base.max_buffer_length is the whole diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index 516bcd53112..5840c861e7e 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -43,8 +43,8 @@ bool usb_audio_microphone_enabled; bool usb_audio_speaker_enabled; // Audio control state surfaced to the host. One extra entry for the master channel 0. -static int8_t usb_audio_mute[USB_AUDIO_MAX_CHANNELS + 1]; -static int16_t usb_audio_volume[USB_AUDIO_MAX_CHANNELS + 1]; +static int8_t usb_audio_mute[USB_AUDIO_N_CHANNELS + 1]; +static int16_t usb_audio_volume[USB_AUDIO_N_CHANNELS + 1]; bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count, bool microphone, bool speaker) { if (tud_connected()) { @@ -53,7 +53,7 @@ bool shared_module_usb_audio_enable(mp_int_t sample_rate, mp_int_t channel_count // One scratch chunk (1 ms at the sample rate); we loop until the FIFO reaches // the setpoint. - usb_audio_mic_samples_len = sample_rate / 1000 * channel_count * USB_AUDIO_BYTES_PER_SAMPLE; + usb_audio_mic_samples_len = sample_rate / 1000 * channel_count * USB_AUDIO_N_BYTES_PER_SAMPLE; usb_audio_mic_samples = (int16_t *)m_malloc_without_collect(usb_audio_mic_samples_len); if (usb_audio_mic_samples == NULL) { m_malloc_fail(usb_audio_mic_samples_len); @@ -155,7 +155,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -165,7 +165,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the input terminal */ \ - TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -188,7 +188,7 @@ void usb_audio_setup_singletons(void) { /* Clock Source Descriptor(4.7.2.1) */ \ TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ @@ -200,7 +200,7 @@ void usb_audio_setup_singletons(void) { /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ /* Class-Specific AS Interface Descriptor(4.9.2) */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -229,14 +229,14 @@ void usb_audio_setup_singletons(void) { TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ /* --- Speaker chain (host -> board) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- USB streaming in from the host */ \ - TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Output Terminal Descriptor(4.7.2.5) -- desktop speaker */ \ TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_DESKTOP_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ USB_AUDIO_HS_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* --- Mic chain (board -> host) --- */ \ /* Input Terminal Descriptor(4.7.2.4) -- generic microphone */ \ - TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ USB_AUDIO_HS_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_HS_ENTITY_MIC_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming out to the host */ \ @@ -247,7 +247,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one OUT endpoint */ \ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the speaker input terminal */ \ - TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -260,7 +260,7 @@ void usb_audio_setup_singletons(void) { /* Standard AS Interface Descriptor(4.9.1) -- alt 1, one IN endpoint */ \ TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 2), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00), \ /* Class-Specific AS Interface Descriptor(4.9.2) -- linked to the mic output terminal */ \ - TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_MAX_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_HS_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00), \ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ @@ -319,7 +319,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_HEADSET_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epout*/ ep_out, /*_epin*/ ep_in | 0x80, @@ -334,9 +334,9 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + two AudioStreaming interfaces, plus // two OUT and two IN endpoints. descriptor_counts->current_interface += 3; - descriptor_counts->num_out_endpoints += USB_AUDIO_MAX_CHANNELS; - descriptor_counts->num_in_endpoints += USB_AUDIO_MAX_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS * 2; + descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS * 2; memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -353,7 +353,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_SPEAKER_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epout*/ iso_ep_num, /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) @@ -363,8 +363,8 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two OUT endpoints. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_out_endpoints += USB_AUDIO_MAX_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->num_out_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -381,7 +381,7 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de USB_AUDIO_MIC_DESCRIPTOR( /*_itfnum*/ descriptor_counts->current_interface, /*_stridx*/ *current_interface_string, - /*_nBytesPerSample*/ USB_AUDIO_BYTES_PER_SAMPLE, + /*_nBytesPerSample*/ USB_AUDIO_N_BYTES_PER_SAMPLE, /*_nBitsUsedPerSample*/ USB_AUDIO_BITS_PER_SAMPLE, /*_epin*/ iso_ep_num | 0x80, /*_epsize*/ CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -391,8 +391,8 @@ size_t usb_audio_add_descriptor(uint8_t *descriptor_buf, descriptor_counts_t *de // One IAD wrapping an AudioControl + an AudioStreaming interface, plus two IN endpoints. descriptor_counts->current_interface += 2; if (!forced_iso_ep) { - descriptor_counts->num_in_endpoints += USB_AUDIO_MAX_CHANNELS; - descriptor_counts->current_endpoint += USB_AUDIO_MAX_CHANNELS; + descriptor_counts->num_in_endpoints += USB_AUDIO_N_CHANNELS; + descriptor_counts->current_endpoint += USB_AUDIO_N_CHANNELS; } memcpy(descriptor_buf, usb_audio_descriptor, sizeof(usb_audio_descriptor)); @@ -544,7 +544,7 @@ bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Mute/volume state is shared across them. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_MAX_CHANNELS) { + if (channelNum > USB_AUDIO_N_CHANNELS) { return false; } switch (ctrlSel) { @@ -582,7 +582,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p switch (ctrlSel) { case AUDIO20_TE_CTRL_CONNECTOR: { audio20_desc_channel_cluster_t ret; - ret.bNrChannels = USB_AUDIO_MAX_CHANNELS; + ret.bNrChannels = USB_AUDIO_N_CHANNELS; ret.bmChannelConfig = (audio20_channel_config_t)0; ret.iChannelNames = 0; return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &ret, sizeof(ret)); @@ -595,7 +595,7 @@ bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p // Feature unit (mute/volume) for either the speaker or mic chain. if (entityID == USB_AUDIO_ENTITY_FEATURE_UNIT || entityID == USB_AUDIO_HS_ENTITY_MIC_FEATURE_UNIT) { - if (channelNum > USB_AUDIO_MAX_CHANNELS) { + if (channelNum > USB_AUDIO_N_CHANNELS) { return false; } switch (ctrlSel) { diff --git a/shared-module/usb_audio/usb_audio_descriptors.h b/shared-module/usb_audio/usb_audio_descriptors.h index a1ec26b26ca..40691a17529 100644 --- a/shared-module/usb_audio/usb_audio_descriptors.h +++ b/shared-module/usb_audio/usb_audio_descriptors.h @@ -26,11 +26,11 @@ size_t usb_audio_descriptor_length(void); // The isochronous IN endpoint's wMaxPacketSize in the USB descriptor is computed // for this rate, so it is the highest rate usb_audio.enable() will accept. #define USB_AUDIO_MAX_SAMPLE_RATE (48000) -#define USB_AUDIO_MAX_CHANNELS (2) +#define USB_AUDIO_N_CHANNELS (2) // 16-bit signed LE PCM. -#define USB_AUDIO_BYTES_PER_SAMPLE (2) -#define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_BYTES_PER_SAMPLE * 8) +#define USB_AUDIO_N_BYTES_PER_SAMPLE (2) +#define USB_AUDIO_BITS_PER_SAMPLE (USB_AUDIO_N_BYTES_PER_SAMPLE * 8) // Endpoint number for the single isochronous audio data endpoint. Most device // controllers accept an ISO endpoint on any number, so the descriptor builder diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h index 9421dd03e0d..ac63542b8e4 100644 --- a/supervisor/shared/usb/tusb_config.h +++ b/supervisor/shared/usb/tusb_config.h @@ -127,7 +127,7 @@ extern "C" { #define CFG_TUD_AUDIO_ENABLE_EP_IN 1 // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) // Deep software FIFO so the 1 ms refill keeps clear of the underrun floor. #define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX) @@ -137,7 +137,7 @@ extern "C" { // IN sizing above. #define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 // wMaxPacketSize, sized for the highest supported sample rate. -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_BYTES_PER_SAMPLE, USB_AUDIO_MAX_CHANNELS) +#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX TUD_AUDIO_EP_SIZE(TUD_OPT_HIGH_SPEED, USB_AUDIO_MAX_SAMPLE_RATE, USB_AUDIO_N_BYTES_PER_SAMPLE, USB_AUDIO_N_CHANNELS) // Deep software FIFO so the 1 ms drain keeps clear of the overrun ceiling. #define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (16 * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX) #endif From 6f29d4529540ccf74495648586bb2f3bbd6f1616 Mon Sep 17 00:00:00 2001 From: Cooper Dalrymple Date: Mon, 13 Jul 2026 11:34:33 -0500 Subject: [PATCH 11/11] Update stereo mic descriptor --- shared-module/usb_audio/__init__.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/shared-module/usb_audio/__init__.c b/shared-module/usb_audio/__init__.c index 5840c861e7e..2a5948bb74f 100644 --- a/shared-module/usb_audio/__init__.c +++ b/shared-module/usb_audio/__init__.c @@ -174,39 +174,39 @@ void usb_audio_setup_singletons(void) { TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) // Hand-rolled UAC2 microphone (board -> host) descriptor WITHOUT an async -// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO_MIC_ONE_CH_DESCRIPTOR +// feedback endpoint. This mirrors TinyUSB's TUD_AUDIO20_MIC_ONE_CH_DESCRIPTOR // (lib/tinyusb/src/device/usbd.h) but drops the trailing feedback endpoint, so // the streaming alt-setting declares a single IN endpoint (_nEPs = 0x00). Async // feedback for true clock matching is a later step. #define USB_AUDIO_MIC_DESCRIPTOR(_itfnum, _stridx, _nBytesPerSample, _nBitsUsedPerSample, _epin, _epsize) \ /* Standard Interface Association Descriptor (IAD) */ \ - TUD_AUDIO_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_IAD(/*_firstitf*/ _itfnum, /*_nitfs*/ 0x02, /*_stridx*/ 0x00), \ /* Standard AC Interface Descriptor(4.7.1) */ \ - TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ + TUD_AUDIO20_DESC_STD_AC(/*_itfnum*/ _itfnum, /*_nEPs*/ 0x00, /*_stridx*/ _stridx), \ /* Class-Specific AC Interface Header Descriptor(4.7.2) */ \ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN + TUD_AUDIO_DESC_INPUT_TERM_LEN + TUD_AUDIO_DESC_OUTPUT_TERM_LEN + TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ + TUD_AUDIO20_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO20_FUNC_MICROPHONE, /*_totallen*/ TUD_AUDIO20_DESC_CLK_SRC_LEN + TUD_AUDIO20_DESC_INPUT_TERM_LEN + TUD_AUDIO20_DESC_OUTPUT_TERM_LEN + TUD_AUDIO20_DESC_FEATURE_UNIT_LEN(2), /*_ctrl*/ AUDIO20_CS_AS_INTERFACE_CTRL_LATENCY_POS), \ /* Clock Source Descriptor(4.7.2.1) */ \ - TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO_CTRL_R << AUDIO_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_CLK_SRC(/*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_attr*/ AUDIO20_CLOCK_SOURCE_ATT_INT_FIX_CLK, /*_ctrl*/ (AUDIO20_CTRL_R << AUDIO20_CLOCK_SOURCE_CTRL_CLK_FRQ_POS), /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00), \ /* Input Terminal Descriptor(4.7.2.4) -- microphone */ \ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_INPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_nchannelslogical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_idxchannelnames*/ 0x00, /*_ctrl*/ AUDIO20_CTRL_R << AUDIO20_IN_TERM_CTRL_CONNECTOR_POS, /*_stridx*/ 0x00), \ /* Output Terminal Descriptor(4.7.2.5) -- USB streaming */ \ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_OUTPUT_TERM(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_srcid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_clkid*/ USB_AUDIO_ENTITY_CLOCK_SOURCE, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00), \ /* Feature Unit Descriptor(4.7.2.8) */ \ - TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_ctrlch0master*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO_CTRL_RW << AUDIO_FEATURE_UNIT_CTRL_VOLUME_POS, /*_stridx*/ 0x00), \ + TUD_AUDIO20_DESC_FEATURE_UNIT(/*_unitid*/ USB_AUDIO_ENTITY_FEATURE_UNIT, /*_srcid*/ USB_AUDIO_ENTITY_INPUT_TERMINAL, /*_stridx*/ 0x00, /*_ctrlch0master*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch1*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS, /*_ctrlch2*/ AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_MUTE_POS | AUDIO20_CTRL_RW << AUDIO20_FEATURE_UNIT_CTRL_VOLUME_POS), \ /* Standard AS Interface Descriptor(4.9.1) */ \ /* Interface 1, Alternate 0 - default alternate setting with 0 bandwidth */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ + TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x00),\ /* Standard AS Interface Descriptor(4.9.1) */ \ /* Interface 1, Alternate 1 - alternate interface for data streaming */ \ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ + TUD_AUDIO20_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)((_itfnum) + 1), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x00),\ /* Class-Specific AS Interface Descriptor(4.9.2) */ \ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ + TUD_AUDIO20_DESC_CS_AS_INT(/*_termid*/ USB_AUDIO_ENTITY_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_formattype*/ AUDIO20_FORMAT_TYPE_I, /*_formats*/ AUDIO20_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ USB_AUDIO_N_CHANNELS, /*_channelcfg*/ AUDIO20_CHANNEL_CONFIG_NON_PREDEFINED, /*_stridx*/ 0x00),\ /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */ \ - TUD_AUDIO_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ + TUD_AUDIO20_DESC_TYPE_I_FORMAT(_nBytesPerSample, _nBitsUsedPerSample), \ /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */ \ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ + TUD_AUDIO20_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (uint8_t) ((uint8_t)TUSB_XFER_ISOCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_ASYNCHRONOUS | (uint8_t)TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ _epsize, /*_interval*/ 0x01), \ /* Class-Specific AS Isochronous Audio Data Endpoint Descriptor(4.10.1.2) */ \ - TUD_AUDIO_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO_CTRL_NONE, /*_lockdelayunit*/ AUDIO_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) + TUD_AUDIO20_DESC_CS_AS_ISO_EP(/*_attr*/ AUDIO20_CS_AS_ISO_DATA_EP_ATT_NON_MAX_PACKETS_OK, /*_ctrl*/ AUDIO20_CTRL_NONE, /*_lockdelayunit*/ AUDIO20_CS_AS_ISO_DATA_EP_LOCK_DELAY_UNIT_UNDEFINED, /*_lockdelay*/ 0x0000) // Hand-rolled UAC2 headset (microphone + speaker both enabled): one audio function // presenting both a speaker (host -> board OUT) and a microphone (board -> host