Skip to content

feat(audio): add USB Audio Host (UAC 1.0) support #3774

Open
zjzhang-cn wants to merge 6 commits into
hathach:masterfrom
zjzhang-cn:feature/tuh-audio-uac-1.0
Open

feat(audio): add USB Audio Host (UAC 1.0) support #3774
zjzhang-cn wants to merge 6 commits into
hathach:masterfrom
zjzhang-cn:feature/tuh-audio-uac-1.0

Conversation

@zjzhang-cn

@zjzhang-cn zjzhang-cn commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Add TinyUSB Host Audio class driver supporting UAC 1.0 devices.

Features:

  • Support multiple Audio Streaming (AS) interfaces with independent format storage
  • Support both IN (Microphone) and OUT (Speaker) endpoints
  • Per-AS interface format info: channels, sample rate, bit resolution
  • Support Feature Unit volume control
  • Support sampling frequency get/set
  • Add host/audio_host example for STM32F407 discovery board
  • Support mono-to-stereo conversion for loopback

Changes:

  • Add src/class/audio/audio_host.c and audio_host.h
  • Register AUDIO driver in usbh.c
  • Add CFG_TUH_AUDIO macro in tusb_option.h
  • Add host/audio_host example with CMake and Makefile build support

Tested with Jabra USB headset (stereo speaker + mono microphone) on STM32F407 disco.

Zhang, Zhenjiang and others added 2 commits July 16, 2026 15:03
Add TinyUSB Host Audio class driver supporting UAC 1.0 devices.

Features:
- Support multiple Audio Streaming (AS) interfaces with independent format storage
- Support both IN (Microphone) and OUT (Speaker) endpoints
- Per-AS interface format info: channels, sample rate, bit resolution
- Support Feature Unit volume control
- Support sampling frequency get/set
- Add host/audio_host example for STM32F407 discovery board
- Support mono-to-stereo conversion for loopback

Changes:
- Add src/class/audio/audio_host.c and audio_host.h
- Register AUDIO driver in usbh.c
- Add CFG_TUH_AUDIO macro in tusb_option.h
- Add host/audio_host example with CMake and Makefile build support

Tested with Jabra USB headset (stereo speaker + mono microphone) on STM32F407 disco.
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copilot AI review requested due to automatic review settings July 17, 2026 02:02
@zjzhang-cn zjzhang-cn changed the title Feature/tuh audio uac 1.0 feat(audio): add USB Audio Host (UAC 1.0) support Jul 17, 2026

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces initial USB Audio Class (UAC 1.0) Host support to TinyUSB by adding a new host class driver (audio_host.c/.h), wiring it into the host driver registry, and providing a new host-side example application demonstrating enumeration and basic streaming/control requests.

Changes:

  • Add a new TUH Audio (UAC 1.0) host class driver and public host API header.
  • Integrate the new class driver into the host stack and build systems (CMake + Make).
  • Add a new examples/host/audio_host example + README.

Reviewed changes

Copilot reviewed 14 out of 14 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
src/tusb.h Expose TUH audio host API when CFG_TUH_AUDIO is enabled.
src/tusb_option.h Add default CFG_TUH_AUDIO option.
src/tinyusb.mk Compile new audio host class driver.
src/host/usbh.c Register audio host class driver in the host class driver table.
src/CMakeLists.txt Add class/audio/audio_host.c to TinyUSB core sources.
src/class/audio/audio_host.h New TUH audio host public API + callback types.
src/class/audio/audio_host.c New UAC1 host driver implementation (enumeration, set_config, transfers, control requests).
examples/host/audio_host/src/tusb_config.h New example configuration enabling TUH audio.
examples/host/audio_host/src/main.c New host example main loop.
examples/host/audio_host/src/audio_app.c New example “loopback” style audio logic and callbacks.
examples/host/audio_host/src/app.h Example app header.
examples/host/audio_host/README.md Example documentation.
examples/host/audio_host/Makefile Make-based build for the new example.
examples/host/audio_host/CMakeLists.txt CMake-based build for the new example.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/class/audio/audio_host.c Outdated
Comment on lines +281 to +286
while (tu_desc_in_bounds(p_desc, desc_end)) {
if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE) {
const tusb_desc_interface_t *itf = (const tusb_desc_interface_t *)p_desc;
if (itf->bInterfaceClass == TUSB_CLASS_AUDIO && itf->bInterfaceSubClass == AUDIO_SUBCLASS_STREAMING) {
// Found Audio Streaming Interface
TU_LOG_DRV(" Found AS Interface %u (alt = %u)\r\n", itf->bInterfaceNumber, itf->bAlternateSetting);
Comment on lines +536 to +545
// If not found, check if this is an AS interface that belongs to a known AC interface
if (idx >= CFG_TUH_AUDIO_MAX) {
for (uint8_t i = 0; i < CFG_TUH_AUDIO_MAX; i++) {
if (_audioh_itf[i].daddr == dev_addr && _audioh_itf[i].as_interface_num == itf_num) {
// AS interface, already handled by AC interface's set_config
return true;
}
}
return false;
}
Comment on lines +469 to +480
// Send SET_INTERFACE for next AS interface if any
p_audio->as_set_idx++;
if (p_audio->as_set_idx < p_audio->as_count) {
uint8_t as_idx = p_audio->as_set_idx;
uint8_t itf = p_audio->as_interfaces[as_idx];
uint8_t alt = p_audio->as_alt_settings[as_idx];
if (alt > 0) {
TU_LOG_DRV("AUDIO Set Interface %u Alt %u (addr = %u)\r\n", itf, alt, xfer->daddr);
tuh_interface_set(xfer->daddr, itf, alt, audioh_set_interface_complete, idx);
return;
}
}
Comment on lines +549 to +559
// Send SET_INTERFACE for all AS interfaces with alt_setting > 0
if (p_audio->as_count > 0) {
p_audio->as_set_idx = 0;
uint8_t itf = p_audio->as_interfaces[0];
uint8_t alt = p_audio->as_alt_settings[0];
if (alt > 0) {
TU_LOG_DRV("AUDIO Set Interface %u Alt %u (addr = %u)\r\n", itf, alt, dev_addr);
tuh_interface_set(dev_addr, itf, alt, audioh_set_interface_complete, idx);
return true;
}
}
Comment thread src/class/audio/audio_host.c Outdated
Comment on lines +136 to +142
typedef struct {
TUH_EPBUF_DEF(epin, CFG_TUH_AUDIO_EPIN_BUFSIZE);
TUH_EPBUF_DEF(epout, CFG_TUH_AUDIO_EPOUT_BUFSIZE);
} audioh_epbuf_t;

static audioh_interface_t _audioh_itf[CFG_TUH_AUDIO_MAX];

Comment thread src/class/audio/audio_host.c Outdated
Comment on lines +687 to +694
uint8_t val_buf[2] = { (uint8_t)(value & 0xFF), (uint8_t)((value >> 8) & 0xFF) };

tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = val_buf,
.complete_cb = complete_cb,
Comment on lines +645 to +647
bool tuh_audio_get_sampling_freq(uint8_t daddr, uint8_t ep_addr, uint32_t *sampling_freq,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
Comment on lines +211 to +213
uint16_t samples = xferred_bytes / 2;
mono_to_stereo(audio_rx_buffer, audio_tx_buffer, samples);
ok = tuh_audio_send(audio_dev_addr, audio_idx, audio_tx_buffer, xferred_bytes * 2);
Comment on lines +147 to +152
printf(" Sampling frequency set OK, ready for isochronous transfer\r\n");
// Set Feature Unit volume to un-mute
set_feature_unit_volume();
// Set OUT sampling frequency then send empty packet to kick-start device
tuh_audio_set_sampling_freq(audio_dev_addr, audio_ep_out, sampling_freq, NULL, 0);
audio_ready = true;
Comment thread examples/host/audio_host/README.md Outdated
Comment on lines +55 to +59
4. The example will:
- Print device information when mounted
- Set sampling frequency to 48kHz
- Receive audio samples from the device (IN endpoint)
- Send test sine wave audio to the device (OUT endpoint)
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

MemBrowse Memory Report

No memory changes detected across 2404 targets. View Project Dashboard →

…sync control transfer buffer

- Stop parsing at first non-Audio interface in audioh_open to avoid claiming unrelated interfaces
- Call usbh_driver_set_config_complete for AS and unknown interfaces to allow enumeration to continue
- Add global ctrl endpoint buffer to audioh_epbuf_t to fix use-after-return in feature_unit_set
- Add sampling_freq NULL check and initialize to 0 in tuh_audio_get_sampling_freq
- Change BOARD_TUH_RHPORT from 1 to 0 in audio_host example
- Add only.txt with supported MCU/family list for audio_host example

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 11 comments.

Comment on lines +690 to +693
.bRequest = AUDIO10_CS_REQ_SET_CUR,
.wValue = tu_u16(control_selector, channel),
.wIndex = tu_u16(itf_num, unit_id),
.wLength = 2
Comment on lines +725 to +728
.bRequest = AUDIO10_CS_REQ_GET_CUR,
.wValue = tu_u16(control_selector, channel),
.wIndex = tu_u16(itf_num, unit_id),
.wLength = len
Comment on lines +624 to +628
.bRequest = AUDIO10_CS_REQ_SET_CUR,
.wValue = tu_u16(AUDIO10_EP_CTRL_SAMPLING_FREQ, 0), // Control Selector = Sampling Freq, Channel = 0
.wIndex = tu_u16_low(ep_addr),
.wLength = 3
};
Comment on lines +662 to +666
.bRequest = AUDIO10_CS_REQ_GET_CUR,
.wValue = tu_u16(AUDIO10_EP_CTRL_SAMPLING_FREQ, 0), // Control Selector = Sampling Freq, Channel = 0
.wIndex = tu_u16_low(ep_addr),
.wLength = 3
};
Comment thread src/class/audio/audio_host.c Outdated
Comment on lines +617 to +618
static uint8_t freq_buf[3] = {0};
tusb_control_request_t const request = {
Comment on lines +136 to +140
typedef struct {
TUH_EPBUF_DEF(epin, CFG_TUH_AUDIO_EPIN_BUFSIZE);
TUH_EPBUF_DEF(epout, CFG_TUH_AUDIO_EPOUT_BUFSIZE);
TUH_EPBUF_DEF(ctrl, 8);
} audioh_epbuf_t;
Comment on lines +797 to +821
bool tuh_audio_set_interface(uint8_t daddr, uint8_t itf_num, uint8_t alt_setting,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_OUT
},
.bRequest = TUSB_REQ_SET_INTERFACE,
.wValue = alt_setting,
.wIndex = itf_num,
.wLength = 0
};

tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
};

return tuh_control_xfer(&xfer);
}
Comment thread examples/host/audio_host/README.md Outdated
Comment on lines +56 to +59
- Print device information when mounted
- Set sampling frequency to 48kHz
- Receive audio samples from the device (IN endpoint)
- Send test sine wave audio to the device (OUT endpoint)
Comment on lines +150 to +152
// Set OUT sampling frequency then send empty packet to kick-start device
tuh_audio_set_sampling_freq(audio_dev_addr, audio_ep_out, sampling_freq, NULL, 0);
audio_ready = true;
Comment on lines +210 to +214
// Mono microphone, convert to stereo and send to OUT endpoint
uint16_t samples = xferred_bytes / 2;
mono_to_stereo(audio_rx_buffer, audio_tx_buffer, samples);
ok = tuh_audio_send(audio_dev_addr, audio_idx, audio_tx_buffer, xferred_bytes * 2);
} else {
@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown

Hardware-in-the-loop (HIL) Test Report

hfp.json

✅ 39 passed · ❌ 13 failed · ⚪ 0 skipped · blank not run

Board cdc_dual_ports cdc_msc dfu cdc_msc_throughput audio_test_freertos dfu_runtime cdc_msc_freertos hid_boot_interface msc_dual_lun hid_generic_inout printer_to_cdc midi_test mtp
stm32l412nucleo ✅ CDC 637k/385k MSC 841k/818k
stm32f746disco ✅ CDC 13M/10.1M MSC 15.8M/30.9M
stm32f746disco-DMA ✅ CDC 13.2M/10M MSC 15.7M/30.3M
lpcxpresso43s67

tinyusb.json

✅ 316 passed · ❌ 23 failed · ⚪ 11 skipped · blank not run

Board cdc_dual_ports cdc_msc dfu cdc_msc_throughput audio_test_freertos dfu_runtime cdc_msc_freertos hid_boot_interface msc_dual_lun hid_generic_inout printer_to_cdc midi_test mtp host_info_to_device_cdc cdc_msc_hid msc_file_explorer msc_file_explorer_freertos device_info hid_composite_freertos
ek_tm4c123gxl ✅ CDC 507k/511k MSC 511k/511k
espressif_p4_function_ev rd 409KB/s
espressif_p4_function_ev-DMA rd 409KB/s
espressif_s3_devkitm rd 409KB/s
espressif_s3_devkitm-DMA
feather_nrf52840_express ✅ CDC 507k/511k MSC 589k/513k
max32666fthr ✅ CDC 7M/14.3M MSC 17.3M/21.8M
metro_m4_express ✅ CDC 514k/554k MSC 511k/512k
mimxrt1015_evk
mimxrt1064_evk ✅ CDC 22.4M/15M MSC 32.3M/28M rd 1368KB/s rd 1365KB/s
lpcxpresso11u37 ✅ CDC 500k/330k MSC 516k/585k
ra4m1_ek ✅ CDC 529k/413k MSC 523k/563k
raspberry_pi_pico ✅ CDC 508k/512k MSC 511k/568k rd 62KB/s rd 62KB/s
raspberry_pi_pico_w
raspberry_pi_pico2 rd 1110KB/s rd 1022KB/s
adafruit_fruit_jam ✅ CDC 508k/512k MSC 511k/561k rd 62KB/s rd 62KB/s
stm32f072disco ✅ CDC 508k/302k MSC 513k/485k
stm32f407disco ✅ CDC 579k/623k MSC 512k/538k
stm32f723disco ✅ CDC 507k/511k MSC 511k/511k rd 18078KB/s rd 4032KB/s
stm32f723disco-DMA ✅ CDC 507k/511k MSC 511k/511k rd 16912KB/s rd 4002KB/s
stm32h743nucleo ✅ CDC 522k/512k MSC 512k/518k
stm32h743nucleo-DMA ✅ CDC 649k/629k MSC 522k/511k
stm32g0b1nucleo ✅ CDC 507k/505k MSC 591k/515k
stm32l476disco ✅ CDC 508k/497k MSC 541k/540k
stm32u083nucleo ✅ CDC 508k/420k MSC 632k/543k
nanoch32v203-fsdev ✅ CDC 508k/513k MSC 511k/511k
nanoch32v203-usbfs
ch32v103r_r1_1v0 ✅ CDC 145k/428k MSC 648k/612k
ch582m_evt ✅ CDC 230k/206k MSC 464k/373k

…audio host

- Fix missing tu_htole16() conversions for wValue and wIndex in
  tuh_audio_set_sampling_freq, tuh_audio_get_sampling_freq,
  tuh_audio_feature_unit_set, and tuh_audio_feature_unit_get
- Fix incorrect wIndex parameter order in feature unit requests
  (unit_id and itf_num were swapped)
- Replace static freq_buf with per-endpoint ctrl buffer in
  tuh_audio_set_sampling_freq to avoid concurrency issues
- Update audio_host README to match actual example behavior

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 8 comments.

Comment on lines +799 to +823
bool tuh_audio_set_interface(uint8_t daddr, uint8_t itf_num, uint8_t alt_setting,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_OUT
},
.bRequest = TUSB_REQ_SET_INTERFACE,
.wValue = alt_setting,
.wIndex = itf_num,
.wLength = 0
};

tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
};

return tuh_control_xfer(&xfer);
}
Comment on lines +539 to +551
// If not found, check if this is an AS interface that belongs to a known AC interface
if (idx >= CFG_TUH_AUDIO_MAX) {
for (uint8_t i = 0; i < CFG_TUH_AUDIO_MAX; i++) {
if (_audioh_itf[i].daddr == dev_addr && _audioh_itf[i].as_interface_num == itf_num) {
// AS interface: configuration is driven by the AC interface, so just pass through
usbh_driver_set_config_complete(dev_addr, itf_num);
return true;
}
}
// Not an Audio interface we own; pass through so enumeration can continue
usbh_driver_set_config_complete(dev_addr, itf_num);
return true;
}
Comment on lines +136 to +143
typedef struct {
TUH_EPBUF_DEF(epin, CFG_TUH_AUDIO_EPIN_BUFSIZE);
TUH_EPBUF_DEF(epout, CFG_TUH_AUDIO_EPOUT_BUFSIZE);
TUH_EPBUF_DEF(ctrl, 8);
} audioh_epbuf_t;

static audioh_interface_t _audioh_itf[CFG_TUH_AUDIO_MAX];
static audioh_epbuf_t _audioh_epbuf[CFG_TUH_AUDIO_MAX];
Comment on lines +590 to +593
bool tuh_audio_itf_get_info(uint8_t idx, tuh_itf_info_t *info) {
audioh_interface_t *p_audio = &_audioh_itf[idx];
TU_VERIFY(p_audio && info);

Comment on lines +147 to +152
printf(" Sampling frequency set OK, ready for isochronous transfer\r\n");
// Set Feature Unit volume to un-mute
set_feature_unit_volume();
// Set OUT sampling frequency then send empty packet to kick-start device
tuh_audio_set_sampling_freq(audio_dev_addr, audio_ep_out, sampling_freq, NULL, 0);
audio_ready = true;
Comment on lines +209 to +214
if (audio_mic_channels == 1) {
// Mono microphone, convert to stereo and send to OUT endpoint
uint16_t samples = xferred_bytes / 2;
mono_to_stereo(audio_rx_buffer, audio_tx_buffer, samples);
ok = tuh_audio_send(audio_dev_addr, audio_idx, audio_tx_buffer, xferred_bytes * 2);
} else {
Comment on lines +96 to +98
- This example uses isochronous transfers which require precise timing
- For production applications, synchronize audio transfers with the device's audio clock
- The example sends a simple sine wave for testing; replace with actual audio data in real applications
Comment on lines +653 to +657
bool tuh_audio_get_sampling_freq(uint8_t daddr, uint8_t ep_addr, uint32_t *sampling_freq,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(sampling_freq, false);
*sampling_freq = 0;

Zhang, Zhenjiang added 2 commits July 17, 2026 14:41
…nify const style

Rename descriptor pointer variables to use desc_ prefix for consistency with audio_device.c:

- it -> desc_input_terminal

- ot -> desc_output_terminal

- fu -> desc_feature_unit

- itf -> desc_interface

- p_ep -> desc_endpoint

Unify const qualifier placement to type_t const * style.

Add file header comment describing UAC 1.0 host driver capabilities.

Switch license header to SPDX identifier.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 15 changed files in this pull request and generated 9 comments.

Comments suppressed due to low confidence (1)

examples/host/audio_host/README.md:99

  • The Notes section says the example sends a "simple sine wave", but the implementation actually loops back received microphone data to the speaker (with optional mono-to-stereo conversion). This is misleading for users trying to understand the example's behavior.
## Notes

- This example uses isochronous transfers which require precise timing
- For production applications, synchronize audio transfers with the device's audio clock
- The example sends a simple sine wave for testing; replace with actual audio data in real applications

Comment on lines +202 to +215
bool audioh_xfer_cb(uint8_t dev_addr, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes) {
(void) result;
const uint8_t idx = get_idx_by_ep_addr(dev_addr, ep_addr);
TU_VERIFY(idx < CFG_TUH_AUDIO_MAX);
audioh_interface_t *p_audio = &_audioh_itf[idx];

if (ep_addr == p_audio->ep_in) {
tuh_audio_rx_cb(idx, ep_addr, (uint16_t) xferred_bytes);
} else if (ep_addr == p_audio->ep_out) {
tuh_audio_tx_cb(idx, ep_addr, (uint16_t) xferred_bytes);
}

return true;
}
Comment on lines +389 to +439
case TUSB_DESC_ENDPOINT: {
const tusb_desc_endpoint_t *desc_endpoint = (const tusb_desc_endpoint_t *)p_desc;
if (desc_endpoint->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
TU_LOG_DRV(" Isochronous EP %02x\r\n", desc_endpoint->bEndpointAddress);
if (tu_edpt_dir(desc_endpoint->bEndpointAddress) == TUSB_DIR_IN) {
p_audio->ep_in = desc_endpoint->bEndpointAddress;
p_audio->ep_in_size = tu_edpt_packet_size(desc_endpoint);
p_audio->ep_in_interval = desc_endpoint->bInterval;
desc_cb.desc_ep_in = desc_endpoint;
// Save to per-AS storage
if (as_entry_idx < CFG_TUH_AUDIO_MAX_AS) {
audioh_as_t *as = &p_audio->as[as_entry_idx];
as->ep_addr = desc_endpoint->bEndpointAddress;
as->ep_size = tu_edpt_packet_size(desc_endpoint);
as->ep_dir = TUSB_DIR_IN;
as->format_type = tmp_format_type;
as->num_channels = tmp_num_channels;
as->sub_frame_size = tmp_sub_frame_size;
as->bit_resolution = tmp_bit_resolution;
as->sam_freq_type = tmp_sam_freq_type;
as->sam_freq_lower = tmp_sam_freq_lower;
as->sam_freq_upper = tmp_sam_freq_upper;
for (uint8_t i = 0; i < CFG_TUH_AUDIO_MAX_SAM_FREQ; i++) {
as->sam_freq[i] = tmp_sam_freq[i];
}
}
} else {
p_audio->ep_out = desc_endpoint->bEndpointAddress;
p_audio->ep_out_size = tu_edpt_packet_size(desc_endpoint);
p_audio->ep_out_interval = desc_endpoint->bInterval;
desc_cb.desc_ep_out = desc_endpoint;
// Save to per-AS storage
if (as_entry_idx < CFG_TUH_AUDIO_MAX_AS) {
audioh_as_t *as = &p_audio->as[as_entry_idx];
as->ep_addr = desc_endpoint->bEndpointAddress;
as->ep_size = tu_edpt_packet_size(desc_endpoint);
as->ep_dir = TUSB_DIR_OUT;
as->format_type = tmp_format_type;
as->num_channels = tmp_num_channels;
as->sub_frame_size = tmp_sub_frame_size;
as->bit_resolution = tmp_bit_resolution;
as->sam_freq_type = tmp_sam_freq_type;
as->sam_freq_lower = tmp_sam_freq_lower;
as->sam_freq_upper = tmp_sam_freq_upper;
for (uint8_t i = 0; i < CFG_TUH_AUDIO_MAX_SAM_FREQ; i++) {
as->sam_freq[i] = tmp_sam_freq[i];
}
}
}
TU_ASSERT(tuh_edpt_open(dev_addr, desc_endpoint), 0);
}
Comment on lines +615 to +630
bool tuh_audio_set_sampling_freq(uint8_t daddr, uint8_t ep_addr, uint32_t sampling_freq,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
uint8_t const idx = get_idx_by_ep_addr(daddr, ep_addr);
TU_VERIFY(idx < CFG_TUH_AUDIO_MAX, false);
uint8_t* freq_buf = _audioh_epbuf[idx].ctrl;
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_ENDPOINT,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
.bRequest = AUDIO10_CS_REQ_SET_CUR,
.wValue = tu_htole16(tu_u16(AUDIO10_EP_CTRL_SAMPLING_FREQ, 0)), // Control Selector = Sampling Freq, Channel = 0
.wIndex = tu_htole16((uint16_t) ep_addr),
.wLength = 3
};
Comment on lines +653 to +668
bool tuh_audio_get_sampling_freq(uint8_t daddr, uint8_t ep_addr, uint32_t *sampling_freq,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
TU_VERIFY(sampling_freq, false);
*sampling_freq = 0;

tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_ENDPOINT,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN
},
.bRequest = AUDIO10_CS_REQ_GET_CUR,
.wValue = tu_htole16(tu_u16(AUDIO10_EP_CTRL_SAMPLING_FREQ, 0)), // Control Selector = Sampling Freq, Channel = 0
.wIndex = tu_htole16((uint16_t) ep_addr),
.wLength = 3
};
Comment on lines +683 to +696
bool tuh_audio_feature_unit_set(uint8_t daddr, uint8_t itf_num, uint8_t unit_id,
uint8_t control_selector, uint8_t channel,
uint16_t value, tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_OUT
},
.bRequest = AUDIO10_CS_REQ_SET_CUR,
.wValue = tu_htole16(tu_u16(control_selector, channel)),
.wIndex = tu_htole16(tu_u16(unit_id, itf_num)),
.wLength = 2
};
Comment on lines +717 to +731
bool tuh_audio_feature_unit_get(uint8_t daddr, uint8_t itf_num, uint8_t unit_id,
uint8_t control_selector, uint8_t channel,
void *buffer, uint8_t len,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_CLASS,
.direction = TUSB_DIR_IN
},
.bRequest = AUDIO10_CS_REQ_GET_CUR,
.wValue = tu_htole16(tu_u16(control_selector, channel)),
.wIndex = tu_htole16(tu_u16(unit_id, itf_num)),
.wLength = len
};
Comment on lines +799 to +823
bool tuh_audio_set_interface(uint8_t daddr, uint8_t itf_num, uint8_t alt_setting,
tuh_xfer_cb_t complete_cb, uintptr_t user_data) {
tusb_control_request_t const request = {
.bmRequestType_bit = {
.recipient = TUSB_REQ_RCPT_INTERFACE,
.type = TUSB_REQ_TYPE_STANDARD,
.direction = TUSB_DIR_OUT
},
.bRequest = TUSB_REQ_SET_INTERFACE,
.wValue = alt_setting,
.wIndex = itf_num,
.wLength = 0
};

tuh_xfer_t xfer = {
.daddr = daddr,
.ep_addr = 0,
.setup = &request,
.buffer = NULL,
.complete_cb = complete_cb,
.user_data = user_data
};

return tuh_control_xfer(&xfer);
}
Comment on lines +141 to +153
// Callback after IN sampling frequency is set
static void in_sampling_freq_set_cb(tuh_xfer_t *xfer) {
if (xfer->result != XFER_RESULT_SUCCESS) {
printf(" Sampling frequency set FAILED: result=%u\r\n", xfer->result);
return;
}
printf(" Sampling frequency set OK, ready for isochronous transfer\r\n");
// Set Feature Unit volume to un-mute
set_feature_unit_volume();
// Set OUT sampling frequency then send empty packet to kick-start device
tuh_audio_set_sampling_freq(audio_dev_addr, audio_ep_out, sampling_freq, NULL, 0);
audio_ready = true;
}
Comment on lines +207 to +222
if (xferred_bytes > 0 && audio_ep_out != 0 && !audio_tx_busy) {
bool ok;
if (audio_mic_channels == 1) {
// Mono microphone, convert to stereo and send to OUT endpoint
uint16_t samples = xferred_bytes / 2;
mono_to_stereo(audio_rx_buffer, audio_tx_buffer, samples);
ok = tuh_audio_send(audio_dev_addr, audio_idx, audio_tx_buffer, xferred_bytes * 2);
} else {
// Stereo microphone, send directly to OUT endpoint
ok = tuh_audio_send(audio_dev_addr, audio_idx, audio_rx_buffer, xferred_bytes);
}

if (ok) {
audio_tx_busy = true;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants