diff --git a/examples/usb_headset/_static/fft.gif b/examples/usb_headset/_static/fft.gif index 0d1137b..c5b7cc1 100644 Binary files a/examples/usb_headset/_static/fft.gif and b/examples/usb_headset/_static/fft.gif differ diff --git a/examples/usb_headset/main/CMakeLists.txt b/examples/usb_headset/main/CMakeLists.txt index 0c36160..9e15092 100644 --- a/examples/usb_headset/main/CMakeLists.txt +++ b/examples/usb_headset/main/CMakeLists.txt @@ -1,8 +1,3 @@ idf_component_register( SRC_DIRS "src" "." EXCLUDE_SRCS "src/usb_descriptors.c" INCLUDE_DIRS include/.) -idf_component_get_property(tusb_lib espressif__tinyusb COMPONENT_LIB) -idf_component_get_property(tusb_path espressif__tinyusb COMPONENT_DIR) -target_include_directories(${tusb_lib} PRIVATE include) -set_source_files_properties("${tusb_path}/src/portable/espressif/esp32sx/dcd_esp32sx.c" PROPERTIES EXCLUDE_FROM_ALL TRUE TARGET ${tusb_lib}) -target_sources(${tusb_lib} PUBLIC "src/usb_descriptors.c" "${tusb_path}/src/portable/synopsys/dwc2/dcd_dwc2.c") \ No newline at end of file diff --git a/examples/usb_headset/main/idf_component.yml b/examples/usb_headset/main/idf_component.yml index 1a7afb1..8719609 100644 --- a/examples/usb_headset/main/idf_component.yml +++ b/examples/usb_headset/main/idf_component.yml @@ -2,4 +2,5 @@ dependencies: idf: ">=5.0" espressif/esp-dsp: "^1.2.1" - espressif/tinyusb: "^0.15.0" \ No newline at end of file + espressif/usb_device_uac: + version: "^0.1.1" diff --git a/examples/usb_headset/main/include/tusb_config.h b/examples/usb_headset/main/include/tusb_config.h deleted file mode 100644 index 2325e0e..0000000 --- a/examples/usb_headset/main/include/tusb_config.h +++ /dev/null @@ -1,138 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#ifndef _TUSB_CONFIG_H_ -#define _TUSB_CONFIG_H_ - -#ifdef __cplusplus -extern "C" { -#endif - -#include "usb_descriptors.h" -#include "usb_headset.h" - -//--------------------------------------------------------------------+ -// Board Specific Configuration -//--------------------------------------------------------------------+ - -// RHPort number used for device can be defined by board.mk, default to port 0 -#ifndef BOARD_TUD_RHPORT -#define BOARD_TUD_RHPORT 0 -#endif - -// RHPort max operational speed can defined by board.mk -#ifndef BOARD_TUD_MAX_SPEED -#define BOARD_TUD_MAX_SPEED OPT_MODE_DEFAULT_SPEED -#endif - -//-------------------------------------------------------------------- -// Common Configuration -//-------------------------------------------------------------------- - -// defined by compiler flags for flexibility -#ifndef CFG_TUSB_MCU -#error CFG_TUSB_MCU must be defined -#endif - -#ifndef CFG_TUSB_OS -#define CFG_TUSB_OS OPT_OS_FREERTOS -#endif - -#ifndef ESP_PLATFORM -#define ESP_PLATFORM 1 -#endif - -#ifndef CFG_TUSB_DEBUG -#define CFG_TUSB_DEBUG 0 -#endif - -#if TU_CHECK_MCU(OPT_MCU_ESP32S2, OPT_MCU_ESP32S3) -#define CFG_TUSB_OS_INC_PATH freertos/ -#endif - - -// Enable Device stack -#define CFG_TUD_ENABLED 1 - -// Default is max speed that hardware controller could support with on-chip PHY -#define CFG_TUD_MAX_SPEED BOARD_TUD_MAX_SPEED - -/* USB DMA on some MCUs can only access a specific SRAM region with restriction on alignment. - * Tinyusb use follows macros to declare transferring memory so that they can be put - * into those specific section. - * e.g - * - CFG_TUSB_MEM SECTION : __attribute__ (( section(".usb_ram") )) - * - CFG_TUSB_MEM_ALIGN : __attribute__ ((aligned(4))) - */ -#ifndef CFG_TUSB_MEM_SECTION -#define CFG_TUSB_MEM_SECTION -#endif - -#ifndef CFG_TUSB_MEM_ALIGN -#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4))) -#endif - -//-------------------------------------------------------------------- -// DEVICE CONFIGURATION -//-------------------------------------------------------------------- - -#ifndef CFG_TUD_ENDPOINT0_SIZE -#define CFG_TUD_ENDPOINT0_SIZE 64 -#endif - -//------------- CLASS -------------// -#define CFG_TUD_CDC 0 -#define CFG_TUD_MSC 0 -#define CFG_TUD_HID 0 -#define CFG_TUD_MIDI 0 -#define CFG_TUD_AUDIO 1 -#define CFG_TUD_VENDOR 0 - -//-------------------------------------------------------------------- -// AUDIO CLASS DRIVER CONFIGURATION -//-------------------------------------------------------------------- - -#define CFG_TUD_AUDIO_FUNC_1_DESC_LEN TUD_AUDIO_HEADSET_STEREO_DESC_LEN - -// How many formats are used, need to adjust USB descriptor if changed -#define CFG_TUD_AUDIO_FUNC_1_N_FORMATS 1 - -#define CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE DEFAULT_SAMPLE_RATE // 24bit/48kHz is the best quality for full-speed, high-speed is needed beyond this -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX DEFAULT_RECORDER_CHANNEL -#define CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX DEFAULT_PLAYER_CHANNEL - -// 16bit in 16bit slots -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX (DEFAULT_RECORDER_WIDTH/8) -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX DEFAULT_RECORDER_WIDTH -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX (DEFAULT_PLAYER_WIDTH/8) -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX DEFAULT_PLAYER_WIDTH - - -// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) -#define CFG_TUD_AUDIO_ENABLE_EP_IN 1 -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_MS 11 // using a bigger buffer size than the packet size is recommended to avoid packet loss -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX) -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ (CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN * CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_MS) -#define CFG_TUD_AUDIO_FUNC_1_EP_IN_SZ_MAX CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_IN // Maximum EP IN size for all AS alternate settings used - -// EP and buffer size - for isochronous EP´s, the buffer and EP size are equal (different sizes would not make sense) -#define CFG_TUD_AUDIO_ENABLE_EP_OUT 1 -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_MS 11 // using a bigger buffer size than the packet size is recommended to avoid packet loss -#define CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX) -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ (CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT * CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_MS) -#define CFG_TUD_AUDIO_FUNC_1_EP_OUT_SZ_MAX CFG_TUD_AUDIO_FUNC_1_FORMAT_1_EP_SZ_OUT // Maximum EP IN size for all AS alternate settings used - -// Number of Standard AS Interface Descriptors (4.9.1) defined per audio function - this is required to be able to remember the current alternate settings of these interfaces - We restrict us here to have a constant number for all audio functions (which means this has to be the maximum number of AS interfaces an audio function has and a second audio function with less AS interfaces just wastes a few bytes) -#define CFG_TUD_AUDIO_FUNC_1_N_AS_INT 1 - -// Size of control request buffer -#define CFG_TUD_AUDIO_FUNC_1_CTRL_BUF_SZ 64 - -#ifdef __cplusplus -} -#endif - -#endif /* _TUSB_CONFIG_H_ */ diff --git a/examples/usb_headset/main/include/usb_descriptors.h b/examples/usb_headset/main/include/usb_descriptors.h deleted file mode 100644 index d09af82..0000000 --- a/examples/usb_headset/main/include/usb_descriptors.h +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#ifndef _USB_DESCRIPTORS_H_ -#define _USB_DESCRIPTORS_H_ - -#include "usb_headset.h" - -// VID -#define USB_VID 0x303A // Espressif VID 0x303A -// This PID is used for testing, please apply for PID through https://github.com/espressif/usb-pids -//#define USB_PID 0x7100 -// BCD_DEVICE = 0x0200, 0x02: version 2, 0x00: sub version 2.0 -#define BCD_DEVICE 0x0200 - -// Unit numbers are arbitrary selected -#define UAC2_ENTITY_CLOCK 0x04 -// Speaker path -#define UAC2_ENTITY_SPK_INPUT_TERMINAL 0x01 -#define UAC2_ENTITY_SPK_FEATURE_UNIT 0x02 -#define UAC2_ENTITY_SPK_OUTPUT_TERMINAL 0x03 -// Microphone path -#define UAC2_ENTITY_MIC_INPUT_TERMINAL 0x11 -#define UAC2_ENTITY_MIC_OUTPUT_TERMINAL 0x13 - -enum { - ITF_NUM_AUDIO_CONTROL = 0, - ITF_NUM_AUDIO_STREAMING_SPK, - ITF_NUM_AUDIO_STREAMING_MIC, - ITF_NUM_TOTAL -}; - -#if DEFAULT_PLAYER_CHANNEL == 1 -#define TUD_AUDIO_DESC_FEATURE_UNIT_LEN TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL_LEN -#define TUD_AUDIO_DESC_FEATURE_UNIT() TUD_AUDIO_DESC_FEATURE_UNIT_ONE_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_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) -#define SPK_CHANNEL_CONFIG (AUDIO_CHANNEL_CONFIG_FRONT_CENTER) -#elif DEFAULT_PLAYER_CHANNEL == 2 -#define TUD_AUDIO_DESC_FEATURE_UNIT_LEN TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL_LEN -#define TUD_AUDIO_DESC_FEATURE_UNIT() TUD_AUDIO_DESC_FEATURE_UNIT_TWO_CHANNEL(/*_unitid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_srcid*/ UAC2_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) -#define SPK_CHANNEL_CONFIG (AUDIO_CHANNEL_CONFIG_FRONT_RIGHT | AUDIO_CHANNEL_CONFIG_FRONT_LEFT) -#else -#error "Unsupported player channel count" -#endif - -#if DEFAULT_RECORDER_CHANNEL == 1 -#define MIC_CHANNEL_CONFIG (AUDIO_CHANNEL_CONFIG_FRONT_CENTER) -#elif DEFAULT_RECORDER_CHANNEL == 2 -#define MIC_CHANNEL_CONFIG (AUDIO_CHANNEL_CONFIG_FRONT_RIGHT | AUDIO_CHANNEL_CONFIG_FRONT_LEFT) -#else -#error "Unsupported recorder channel count" -#endif - -#define TUD_AUDIO_HEADSET_STEREO_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\ - + TUD_AUDIO_DESC_INPUT_TERM_LEN\ - + TUD_AUDIO_DESC_FEATURE_UNIT_LEN\ - + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ - + TUD_AUDIO_DESC_INPUT_TERM_LEN\ - + TUD_AUDIO_DESC_OUTPUT_TERM_LEN\ - /* Interface 1, Alternate 0 */\ - + TUD_AUDIO_DESC_STD_AS_INT_LEN\ - /* Interface 1, Alternate 1 */\ - + 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\ - /* Interface 2, Alternate 0 */\ - + TUD_AUDIO_DESC_STD_AS_INT_LEN\ - /* Interface 2, Alternate 1 */\ - + 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 TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(_stridx, _epout, _epin) \ - /* Standard Interface Association Descriptor (IAD) */\ - TUD_AUDIO_DESC_IAD(/*_firstitfs*/ ITF_NUM_AUDIO_CONTROL, /*_nitfs*/ 3, /*_stridx*/ 0x00),\ - /* Standard AC Interface Descriptor(4.7.1) */\ - TUD_AUDIO_DESC_STD_AC(/*_itfnum*/ ITF_NUM_AUDIO_CONTROL, /*_nEPs*/ 0x00, /*_stridx*/ _stridx),\ - /* Class-Specific AC Interface Header Descriptor(4.7.2) */\ - TUD_AUDIO_DESC_CS_AC(/*_bcdADC*/ 0x0200, /*_category*/ AUDIO_FUNC_HEADSET, /*_totallen*/ TUD_AUDIO_DESC_CLK_SRC_LEN+TUD_AUDIO_DESC_FEATURE_UNIT_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN+TUD_AUDIO_DESC_INPUT_TERM_LEN+TUD_AUDIO_DESC_OUTPUT_TERM_LEN, /*_ctrl*/ AUDIO_CS_AS_INTERFACE_CTRL_LATENCY_POS),\ - /* Clock Source Descriptor(4.7.2.1) */\ - TUD_AUDIO_DESC_CLK_SRC(/*_clkid*/ UAC2_ENTITY_CLOCK, /*_attr*/ 3, /*_ctrl*/ 7, /*_assocTerm*/ 0x00, /*_stridx*/ 0x00), \ - /* Input Terminal Descriptor(4.7.2.4) */\ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ SPK_CHANNEL_CONFIG, /*_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(),\ - /* Output Terminal Descriptor(4.7.2.5) */\ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_SPK_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_OUT_GENERIC_SPEAKER, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_SPK_FEATURE_UNIT, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_stridx*/ 0x00),\ - /* Input Terminal Descriptor(4.7.2.4) */\ - TUD_AUDIO_DESC_INPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_IN_GENERIC_MIC, /*_assocTerm*/ 0x00, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_nchannelslogical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ MIC_CHANNEL_CONFIG, /*_idxchannelnames*/ 0x00, /*_ctrl*/ 0 * (AUDIO_CTRL_R << AUDIO_IN_TERM_CTRL_CONNECTOR_POS), /*_stridx*/ 0x00),\ - /* Output Terminal Descriptor(4.7.2.5) */\ - TUD_AUDIO_DESC_OUTPUT_TERM(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_termtype*/ AUDIO_TERM_TYPE_USB_STREAMING, /*_assocTerm*/ 0x00, /*_srcid*/ UAC2_ENTITY_MIC_INPUT_TERMINAL, /*_clkid*/ UAC2_ENTITY_CLOCK, /*_ctrl*/ 0x0000, /*_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)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x04),\ - /* 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)(ITF_NUM_AUDIO_STREAMING_SPK), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x04),\ - /* Class-Specific AS Interface Descriptor(4.9.2) */\ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_SPK_INPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX, /*_channelcfg*/ SPK_CHANNEL_CONFIG, /*_stridx*/ 0x00),\ - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ - TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX),\ - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epout, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_SYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_RX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX), /*_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_MILLISEC, /*_lockdelay*/ 0x0001),\ - /* Interface 2, Alternate 0 - default alternate setting with 0 bandwidth */\ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x00, /*_nEPs*/ 0x00, /*_stridx*/ 0x05),\ - /* Standard AS Interface Descriptor(4.9.1) */\ - /* Interface 2, Alternate 1 - alternate interface for data streaming */\ - TUD_AUDIO_DESC_STD_AS_INT(/*_itfnum*/ (uint8_t)(ITF_NUM_AUDIO_STREAMING_MIC), /*_altset*/ 0x01, /*_nEPs*/ 0x01, /*_stridx*/ 0x05),\ - /* Class-Specific AS Interface Descriptor(4.9.2) */\ - TUD_AUDIO_DESC_CS_AS_INT(/*_termid*/ UAC2_ENTITY_MIC_OUTPUT_TERMINAL, /*_ctrl*/ AUDIO_CTRL_NONE, /*_formattype*/ AUDIO_FORMAT_TYPE_I, /*_formats*/ AUDIO_DATA_FORMAT_TYPE_I_PCM, /*_nchannelsphysical*/ CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX, /*_channelcfg*/ MIC_CHANNEL_CONFIG, /*_stridx*/ 0x00),\ - /* Type I Format Type Descriptor(2.3.1.6 - Audio Formats) */\ - TUD_AUDIO_DESC_TYPE_I_FORMAT(CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX),\ - /* Standard AS Isochronous Audio Data Endpoint Descriptor(4.10.1.1) */\ - TUD_AUDIO_DESC_STD_AS_ISO_EP(/*_ep*/ _epin, /*_attr*/ (TUSB_XFER_ISOCHRONOUS | TUSB_ISO_EP_ATT_ASYNCHRONOUS | TUSB_ISO_EP_ATT_DATA), /*_maxEPsize*/ TUD_AUDIO_EP_SIZE(CFG_TUD_AUDIO_FUNC_1_MAX_SAMPLE_RATE, CFG_TUD_AUDIO_FUNC_1_FORMAT_1_N_BYTES_PER_SAMPLE_TX, CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX), /*_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) - -#endif diff --git a/examples/usb_headset/main/include/usb_headset.h b/examples/usb_headset/main/include/usb_headset.h index 9c0f38c..68f7652 100644 --- a/examples/usb_headset/main/include/usb_headset.h +++ b/examples/usb_headset/main/include/usb_headset.h @@ -12,12 +12,12 @@ extern "C" { #include "esp_err.h" -#define DEFAULT_SAMPLE_RATE (48000) +#define DEFAULT_UAC_SAMPLE_RATE (CONFIG_UAC_SAMPLE_RATE) #define DEFAULT_VOLUME (99) // Currently the player and recorder should use the same channel and width -#define DEFAULT_RECORDER_CHANNEL (1) +#define DEFAULT_RECORDER_CHANNEL (CONFIG_UAC_MIC_CHANNEL_NUM) #define DEFAULT_RECORDER_WIDTH (16) -#define DEFAULT_PLAYER_CHANNEL (1) +#define DEFAULT_PLAYER_CHANNEL (CONFIG_UAC_SPEAKER_CHANNEL_NUM) #define DEFAULT_PLAYER_WIDTH (16) #define DEBUG_USB_HEADSET (0) #define DEBUG_SYSTEM_VIEW (0) diff --git a/examples/usb_headset/main/include/usb_headset_debug.h b/examples/usb_headset/main/include/usb_headset_debug.h index 34acc5a..60994bb 100644 --- a/examples/usb_headset/main/include/usb_headset_debug.h +++ b/examples/usb_headset/main/include/usb_headset_debug.h @@ -42,7 +42,7 @@ esp_err_t bsp_i2s_read_debug(void *buf, size_t size, size_t *bytes_read, TickTyp { static uint64_t last_time = 0; uint64_t current_time = esp_timer_get_time(); - size_t data_read_size = (current_time - last_time) / 1000 * sample_rate * mic_resolution / 8; + size_t data_read_size = (current_time - last_time) / 1000 * DEFAULT_UAC_SAMPLE_RATE * DEFAULT_RECORDER_WIDTH / 8; if (data_read_size > size) { data_read_size = size; } @@ -50,14 +50,14 @@ esp_err_t bsp_i2s_read_debug(void *buf, size_t size, size_t *bytes_read, TickTyp #if DEFAULT_RECORDER_CHANNEL == 1 int16_t *data_buf = (int16_t *)buf; for (int i = 0; i < data_read_size / 2; i++) { - data_buf[i] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / sample_rate)); + data_buf[i] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / DEFAULT_UAC_SAMPLE_RATE)); } *bytes_read = data_read_size; #elif DEFAULT_RECORDER_CHANNEL == 2 int16_t *data_buf = (int16_t *)buf; for (int i = 0; i < data_read_size / 4; i++) { - data_buf[2 * i] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / sample_rate)); - data_buf[2 * i + 1] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / sample_rate)); + data_buf[2 * i] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / DEFAULT_UAC_SAMPLE_RATE)); + data_buf[2 * i + 1] = (int16_t)(32767 * sin(2 * M_PI * 1000 * i / DEFAULT_UAC_SAMPLE_RATE)); } *bytes_read = data_read_size; #else diff --git a/examples/usb_headset/main/main.c b/examples/usb_headset/main/main.c index a836d52..e80a994 100644 --- a/examples/usb_headset/main/main.c +++ b/examples/usb_headset/main/main.c @@ -36,6 +36,15 @@ void app_main(void) { + gpio_config_t io_conf = {0}; + io_conf.intr_type = GPIO_INTR_DISABLE; + io_conf.mode = GPIO_MODE_OUTPUT; + io_conf.pin_bit_mask = (1ULL << GPIO_NUM_12); + io_conf.pull_down_en = 0; + io_conf.pull_up_en = 0; + gpio_config(&io_conf); + gpio_set_level(GPIO_NUM_12, 0); + #if !DEBUG_USB_HEADSET /* Initialize I2C (for touch and audio) */ bsp_i2c_init(); @@ -47,7 +56,7 @@ void app_main(void) fft_convert_init(); /* Initialize audio i2s */ - i2s_std_config_t i2s_config = BSP_I2S_DUPLEX_MONO_CFG(DEFAULT_SAMPLE_RATE); + i2s_std_config_t i2s_config = BSP_I2S_DUPLEX_MONO_CFG(DEFAULT_UAC_SAMPLE_RATE); i2s_config.clk_cfg.mclk_multiple = I2S_MCLK_MULTIPLE_384; bsp_audio_init(&i2s_config); @@ -55,7 +64,7 @@ void app_main(void) bsp_board_init(); /* Initialize codec with defaults */ - bsp_codec_set_fs(DEFAULT_SAMPLE_RATE, DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_CHANNEL); + bsp_codec_set_fs(DEFAULT_UAC_SAMPLE_RATE, DEFAULT_PLAYER_WIDTH, DEFAULT_PLAYER_CHANNEL); bsp_codec_volume_set(DEFAULT_VOLUME, NULL); bsp_codec_mute_set(false); #endif diff --git a/examples/usb_headset/main/src/usb_descriptors.c b/examples/usb_headset/main/src/usb_descriptors.c deleted file mode 100644 index 03d4535..0000000 --- a/examples/usb_headset/main/src/usb_descriptors.c +++ /dev/null @@ -1,130 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD - * - * SPDX-License-Identifier: Unlicense OR CC0-1.0 - */ - -#include "tusb.h" -#include "usb_descriptors.h" - -/* A combination of interfaces must have a unique product id, since PC will save device driver after the first plug. - * Same VID/PID with different interface e.g MSC (first), then CDC (later) will possibly cause system error on PC. - * - * Auto ProductID layout's Bitmap: - * [MSB] AUDIO | MIDI | HID | MSC | CDC [LSB] - */ -#ifndef USB_PID -#define _PID_MAP(itf, n) ( (CFG_TUD_##itf) << (n) ) -#define USB_PID (0x4000 | _PID_MAP(CDC, 0) | _PID_MAP(MSC, 1) | _PID_MAP(HID, 2) | \ - _PID_MAP(MIDI, 3) | _PID_MAP(AUDIO, 4) | _PID_MAP(VENDOR, 5) ) -#endif - -//--------------------------------------------------------------------+ -// Device Descriptors -//--------------------------------------------------------------------+ -tusb_desc_device_t const desc_device = { - .bLength = sizeof(tusb_desc_device_t), - .bDescriptorType = TUSB_DESC_DEVICE, - .bcdUSB = 0x0200, - - // Use Interface Association Descriptor (IAD) for CDC - // As required by USB Specs IAD's subclass must be common class (2) and protocol must be IAD (1) - .bDeviceClass = TUSB_CLASS_MISC, - .bDeviceSubClass = MISC_SUBCLASS_COMMON, - .bDeviceProtocol = MISC_PROTOCOL_IAD, - .bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE, - - .idVendor = USB_VID, - .idProduct = USB_PID, - .bcdDevice = BCD_DEVICE, - - .iManufacturer = 0x01, - .iProduct = 0x02, - .iSerialNumber = 0x03, - - .bNumConfigurations = 0x01 -}; - -// Invoked when received GET DEVICE DESCRIPTOR -// Application return pointer to descriptor -uint8_t const *tud_descriptor_device_cb(void) -{ - return (uint8_t const *)&desc_device; -} - -//--------------------------------------------------------------------+ -// Configuration Descriptor -//--------------------------------------------------------------------+ -#define CONFIG_TOTAL_LEN (TUD_CONFIG_DESC_LEN + CFG_TUD_AUDIO * TUD_AUDIO_HEADSET_STEREO_DESC_LEN) -#define EPNUM_AUDIO_IN 0x81 -#define EPNUM_AUDIO_OUT 0x01 - -uint8_t const desc_configuration[] = { - // Interface count, string index, total length, attribute, power in mA - TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, CONFIG_TOTAL_LEN, 0x00, 500), - - // String index, EP Out & EP In address - TUD_AUDIO_HEADSET_STEREO_DESCRIPTOR(2, EPNUM_AUDIO_OUT, EPNUM_AUDIO_IN) -}; - -// Invoked when received GET CONFIGURATION DESCRIPTOR -// Application return pointer to descriptor -// Descriptor contents must exist long enough for transfer to complete -uint8_t const *tud_descriptor_configuration_cb(uint8_t index) -{ - (void)index; // for multiple configurations - return desc_configuration; -} - -//--------------------------------------------------------------------+ -// String Descriptors -//--------------------------------------------------------------------+ - -// array of pointer to string descriptors -char const *string_desc_arr [] = { - (const char[]) { 0x09, 0x04 }, // 0: is supported language is English (0x0409) - "Espressif", // 1: Manufacturer - "ESP-BOX headset", // 2: Product - "000001", // 3: Serials, should use chip ID - "ESP-BOX Speakers", // 4: Audio Interface - "ESP-BOX Microphone", // 5: Audio Interface -}; - -static uint16_t _desc_str[32]; - -// Invoked when received GET STRING DESCRIPTOR request -// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete -uint16_t const *tud_descriptor_string_cb(uint8_t index, uint16_t langid) -{ - (void)langid; - - uint8_t chr_count; - - if (index == 0) { - memcpy(&_desc_str[1], string_desc_arr[0], 2); - chr_count = 1; - } else { - // Convert ASCII string into UTF-16 - - if (!(index < sizeof(string_desc_arr) / sizeof(string_desc_arr[0]))) { - return NULL; - } - - const char *str = string_desc_arr[index]; - - // Cap at max char - chr_count = (uint8_t) strlen(str); - if (chr_count > 31) { - chr_count = 31; - } - - for (uint8_t i = 0; i < chr_count; i++) { - _desc_str[1 + i] = str[i]; - } - } - - // first byte is length (including header), second byte is string type - _desc_str[0] = (uint16_t) ((TUSB_DESC_STRING << 8 ) | (2 * chr_count + 2)); - - return _desc_str; -} diff --git a/examples/usb_headset/main/src/usb_headset.c b/examples/usb_headset/main/src/usb_headset.c index 3bd200f..74f183f 100644 --- a/examples/usb_headset/main/src/usb_headset.c +++ b/examples/usb_headset/main/src/usb_headset.c @@ -6,484 +6,72 @@ #include #include -#include "freertos/FreeRTOS.h" -#include "esp_private/usb_phy.h" #include "esp_log.h" #include "bsp/esp-bsp.h" #include "bsp_board.h" -#include "tusb.h" -#include "usb_descriptors.h" -#include "usb_headset.h" #include "fft_convert.h" +#include "usb_headset.h" +#include "usb_device_uac.h" + +#include "tusb.h" +#include "usb_headset_debug.h" const static char *TAG = "usb_headset"; -//--------------------------------------------------------------------+ -// MACRO CONSTANT TYPEDEF PROTOTYPES -//--------------------------------------------------------------------+ -// Volume control range -enum { - VOLUME_CTRL_0_DB = 0, - VOLUME_CTRL_10_DB = 2560, - VOLUME_CTRL_20_DB = 5120, - VOLUME_CTRL_30_DB = 7680, - VOLUME_CTRL_40_DB = 10240, - VOLUME_CTRL_50_DB = 12800, - VOLUME_CTRL_60_DB = 15360, - VOLUME_CTRL_70_DB = 17920, - VOLUME_CTRL_80_DB = 20480, - VOLUME_CTRL_90_DB = 23040, - VOLUME_CTRL_100_DB = 25600, - VOLUME_CTRL_SILENCE = 0x8000, -}; -// List of supported sample rates -// we just support one sample rate, due to the limitation of the codec -const uint32_t sample_rates[] = {DEFAULT_SAMPLE_RATE}; -static uint32_t s_sample_rate = DEFAULT_SAMPLE_RATE; -#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates) - -// Audio controls, current states -static int8_t spk_mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -static int16_t spk_volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_TX + 1]; // +1 for master channel 0 -static audio_control_range_2_n_t(1) range_vol = { - .wNumSubRanges = tu_htole16(1), - .subrange[0] = { .bMin = tu_htole16(-VOLUME_CTRL_50_DB), tu_htole16(VOLUME_CTRL_0_DB), tu_htole16(256) } -}; - -// Buffer for microphone data -static int16_t s_mic_buf1[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 2] = {0}; -static int16_t s_mic_buf2[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 2] = {0}; -static int16_t *s_mic_write_buf = s_mic_buf1; -static int16_t *s_mic_read_buf = s_mic_buf2; -volatile static size_t s_mic_read_buf_len = 0; -static TaskHandle_t mic_task_handle; -// Buffer for speaker data -static int16_t s_spk_buf[CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_SZ / 2] = {0}; -volatile static size_t s_spk_buf_len = 0; -static TaskHandle_t spk_task_handle; -// Speaker and microphone status -volatile static bool s_spk_active; -volatile static bool s_mic_active; -// Resolution per format, Note: due to the limitation of the codec, we currently just support one resolution -const uint8_t spk_resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_RX - }; -const uint8_t mic_resolutions_per_format[CFG_TUD_AUDIO_FUNC_1_N_FORMATS] = {CFG_TUD_AUDIO_FUNC_1_FORMAT_1_RESOLUTION_TX - }; -// Current resolution, update on format change -static uint8_t s_spk_resolution = spk_resolutions_per_format[0]; -static uint8_t s_mic_resolution = mic_resolutions_per_format[0]; -static size_t s_spk_bytes_ms = 0; -static size_t s_mic_bytes_ms = 0; -static portMUX_TYPE s_mux = portMUX_INITIALIZER_UNLOCKED; -#define UAC_ENTER_CRITICAL() portENTER_CRITICAL(&s_mux) -#define UAC_EXIT_CRITICAL() portEXIT_CRITICAL(&s_mux) - -// for debug purpose -#include "usb_headset_debug.h" - -static void usb_phy_init(void) +static esp_err_t uac_device_output_cb(uint8_t *buf, size_t len, void *arg) { - // Configure USB PHY - usb_phy_handle_t phy_hdl; - usb_phy_config_t phy_conf = { - .controller = USB_PHY_CTRL_OTG, - .otg_mode = USB_OTG_MODE_DEVICE, - }; - phy_conf.target = USB_PHY_TARGET_INT; - usb_new_phy(&phy_conf, &phy_hdl); -} - -static void usb_task(void *pvParam) -{ - (void) pvParam; - usb_phy_init(); - if (tud_init(BOARD_TUD_RHPORT) == false) { - ESP_LOGE(TAG, "Failed to initialize TinyUSB"); - goto error; + size_t bytes_written = 0; + esp_err_t ret = bsp_i2s_write(buf, len, &bytes_written, 0); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "i2s write failed"); + return ESP_FAIL; } - do { - // TinyUSB Core task - tud_task(); - } while (true); +#if !DEBUG_USB_HEADSET + rb_write((int16_t *)buf, bytes_written); +#endif -error: - vTaskDelete(NULL); + return ESP_OK; } -static void usb_headset_spk(void *pvParam) +static esp_err_t uac_device_input_cb(uint8_t *buf, size_t len, size_t *bytes_read, void *arg) { - while (1) { - SYSVIEW_SPK_WAIT_EVENT_START(); - if (s_spk_active == false) { - ulTaskNotifyTake(pdFAIL, portMAX_DELAY); - continue; - } - // clear the notification - ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - if (s_spk_buf_len == 0) { - continue; - } - // playback the data from the ring buffer chunk by chunk - SYSVIEW_SPK_WAIT_EVENT_END(); - SYSVIEW_SPK_SEND_EVENT_START(); - size_t bytes_written = 0; - bsp_i2s_write(s_spk_buf, s_spk_buf_len, &bytes_written, 0); - for (int i = 0; i < bytes_written / 2; i ++) { - rb_write(s_spk_buf + i, 2); - } - s_spk_buf_len = 0; - SYSVIEW_SPK_SEND_EVENT_END(); + esp_err_t ret = bsp_i2s_read(buf, len, bytes_read, 10); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "i2s read failed"); + return ESP_FAIL; } + + return ESP_OK; } -static void usb_headset_mic(void *pvParam) +static void uac_device_set_mute_cb(uint32_t mute, void *arg) { - while (1) { - if (s_mic_active == false) { - ulTaskNotifyTake(pdFAIL, portMAX_DELAY); - continue; - } - // clear the notification - ulTaskNotifyTake(pdTRUE, 0); - // read data from the microphone chunk by chunk - SYSVIEW_MIC_READ_EVENT_START(); - size_t bytes_read = 0; - size_t bytes_require = s_mic_bytes_ms * (CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_MS - 1); - esp_err_t ret = bsp_i2s_read(s_mic_write_buf, bytes_require, &bytes_read, 0); - if (ret != ESP_OK) { - ESP_LOGD(TAG, "Failed to read data from I2S, ret = %d", ret); - SYSVIEW_MIC_READ_EVENT_END(); - continue; - } - // swap the buffer - int16_t *tmp_buf = s_mic_read_buf; - UAC_ENTER_CRITICAL(); - s_mic_read_buf = s_mic_write_buf; - s_mic_read_buf_len = bytes_read; - s_mic_write_buf = tmp_buf; - UAC_EXIT_CRITICAL(); - SYSVIEW_MIC_READ_EVENT_END(); - } + bsp_codec_mute_set(mute); + ESP_LOGI(TAG, "set uac-device mute to: %"PRIu32"", mute); +} + +static void uac_device_set_volume_cb(uint32_t volume, void *arg) +{ + bsp_codec_volume_set(volume, NULL); + ESP_LOGI(TAG, "set uac-device volume to: %"PRIu32"", volume); } esp_err_t usb_headset_init(void) { - s_spk_bytes_ms = s_sample_rate / 1000 * s_spk_resolution * DEFAULT_PLAYER_CHANNEL / 8; - s_mic_bytes_ms = s_sample_rate / 1000 * s_mic_resolution * DEFAULT_RECORDER_CHANNEL / 8; - // we give the higher priority to playback task, to avoid the data pending in the ring buffer - BaseType_t ret_val = xTaskCreate(usb_headset_spk, "usb_headset_spk", 4 * 1024, NULL, 8, &spk_task_handle); - if (ret_val != pdPASS) { - ESP_LOGE(TAG, "Failed to create usb_headset_spk task"); + uac_device_config_t config = { + .output_cb = uac_device_output_cb, + .input_cb = uac_device_input_cb, + .set_mute_cb = uac_device_set_mute_cb, + .set_volume_cb = uac_device_set_volume_cb, + .cb_ctx = NULL, + }; + esp_err_t ret = uac_device_init(&config); + if (ret != ESP_OK) { + ESP_LOGE(TAG, "uac device init failed"); return ESP_FAIL; } - // we give the lower priority to record task, to avoid the data pending in the ring buffer - ret_val = xTaskCreate(usb_headset_mic, "usb_headset_mic", 4 * 1024, NULL, 8, &mic_task_handle); - if (ret_val != pdPASS) { - ESP_LOGE(TAG, "Failed to create usb_headset_mic task"); - return ESP_FAIL; - } - ret_val = xTaskCreatePinnedToCore(usb_task, "usb_task", 4 * 1024, NULL, 10, NULL, 1); - if (ret_val != pdPASS) { - ESP_LOGE(TAG, "Failed to create usb_task"); - return ESP_FAIL; - } - ESP_LOGI(TAG, "TinyUSB initialized"); + + ESP_LOGI(TAG, "USB initialized"); return ESP_OK; } - -// Invoked when device is mounted -void tud_mount_cb(void) -{ - s_spk_active = false; - s_mic_active = false; - ESP_LOGI(TAG, "USB mounted"); -} - -// Invoked when device is unmounted -void tud_umount_cb(void) -{ - ESP_LOGI(TAG, "USB unmounted"); -} - -// Invoked when usb bus is suspended -// remote_wakeup_en : if host allow us to perform remote wakeup -// Within 7ms, device must draw an average of current less than 2.5 mA from bus -void tud_suspend_cb(bool remote_wakeup_en) -{ - s_spk_active = false; - s_mic_active = false; - (void)remote_wakeup_en; - ESP_LOGI(TAG, "USB suspended"); -} - -// Invoked when usb bus is resumed -void tud_resume_cb(void) -{ - ESP_LOGI(TAG, "USB resumed"); -} - -// Helper for clock get requests -static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request) -{ - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { - if (request->bRequest == AUDIO_CS_REQ_CUR) { - TU_LOG1("Clock get current freq %lu\r\n", s_sample_rate); - - audio_control_cur_4_t curf = { (int32_t) tu_htole32(s_sample_rate) }; - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &curf, sizeof(curf)); - } else if (request->bRequest == AUDIO_CS_REQ_RANGE) { - audio_control_range_4_n_t(N_SAMPLE_RATES) rangef = { - .wNumSubRanges = tu_htole16(N_SAMPLE_RATES) - }; - TU_LOG1("Clock get %d freq ranges\r\n", N_SAMPLE_RATES); - for (uint8_t i = 0; i < N_SAMPLE_RATES; i++) { - rangef.subrange[i].bMin = (int32_t) sample_rates[i]; - rangef.subrange[i].bMax = (int32_t) sample_rates[i]; - rangef.subrange[i].bRes = 0; - TU_LOG1("Range %d (%d, %d, %d)\r\n", i, (int)rangef.subrange[i].bMin, (int)rangef.subrange[i].bMax, (int)rangef.subrange[i].bRes); - } - - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &rangef, sizeof(rangef)); - } - } else if (request->bControlSelector == AUDIO_CS_CTRL_CLK_VALID && - request->bRequest == AUDIO_CS_REQ_CUR) { - audio_control_cur_1_t cur_valid = { .bCur = 1 }; - TU_LOG1("Clock get is valid %u\r\n", cur_valid.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_valid, sizeof(cur_valid)); - } - TU_LOG1("Clock get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; -} - -// Helper for clock set requests -static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; - - TU_ASSERT(request->bEntityID == UAC2_ENTITY_CLOCK); - TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - - if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ) { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t)); - - uint32_t target_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur; - TU_LOG1("Clock set current freq: %ld\r\n", target_sample_rate); - if (target_sample_rate != s_sample_rate) { - bool target_exists = false; - for (int i = 0; i < N_SAMPLE_RATES; i++) { - if (target_sample_rate == sample_rates[i]) { - target_exists = true; - break; - } - } - - if (target_exists == false) { - TU_LOG1("Unsupported sample rate %ld", target_sample_rate); - return false; - } - // Currently the bsp_codec_set_fs() can not support different sample rate for mic and speaker - // the dynamic sample rate change is not supported - // TODO: bsp_codec_set_fs(target_sample_rate, s_spk_resolution, DEFAULT_PLAYER_CHANNEL); - s_sample_rate = target_sample_rate; - s_spk_bytes_ms = s_sample_rate / 1000 * s_spk_resolution * DEFAULT_PLAYER_CHANNEL / 8; - s_mic_bytes_ms = s_sample_rate / 1000 * s_mic_resolution * DEFAULT_RECORDER_CHANNEL / 8; - TU_LOG1("Mic/Speaker frequency %" PRIu32 ", resolution %d, ch %d", target_sample_rate, s_spk_resolution, DEFAULT_PLAYER_CHANNEL); - return true; - } - return true; - } else { - TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; - } -} - -// Helper for feature unit get requests -static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request) -{ - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE && request->bRequest == AUDIO_CS_REQ_CUR) { - audio_control_cur_1_t mute1 = { .bCur = spk_mute[request->bChannelNumber] }; - TU_LOG1("Get channel %u spk_mute %d\r\n", request->bChannelNumber, mute1.bCur); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &mute1, sizeof(mute1)); - } else if (UAC2_ENTITY_SPK_FEATURE_UNIT && request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { - if (request->bRequest == AUDIO_CS_REQ_RANGE) { - TU_LOG1("Get channel %u spk_volume range (%d, %d, %u) dB\r\n", request->bChannelNumber, - range_vol.subrange[0].bMin / 256, range_vol.subrange[0].bMax / 256, range_vol.subrange[0].bRes / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &range_vol, sizeof(range_vol)); - } else if (request->bRequest == AUDIO_CS_REQ_CUR) { - audio_control_cur_2_t cur_vol = { .bCur = tu_htole16(spk_volume[request->bChannelNumber]) }; - TU_LOG1("Get channel %u spk_volume %d dB\r\n", request->bChannelNumber, cur_vol.bCur / 256); - return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *)request, &cur_vol, sizeof(cur_vol)); - } - } - TU_LOG1("Feature unit get request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - - return false; -} - -// Helper for feature unit set requests -static bool tud_audio_feature_unit_set_request(uint8_t rhport, audio_control_request_t const *request, uint8_t const *buf) -{ - (void)rhport; - - TU_ASSERT(request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT); - TU_VERIFY(request->bRequest == AUDIO_CS_REQ_CUR); - - if (request->bControlSelector == AUDIO_FU_CTRL_MUTE) { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t)); - spk_mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur; - TU_LOG1("Set speaker channel %d Mute: %d\r\n", request->bChannelNumber, spk_mute[request->bChannelNumber]); - bsp_codec_mute_set(spk_mute[request->bChannelNumber]); - return true; - } else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME) { - TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t)); - spk_volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur; - int spk_volume_db = spk_volume[request->bChannelNumber] / 256; // Convert to dB - int volume = (spk_volume_db + 50) * 2; // Map to range 0 to 100 - TU_LOG1("Set speaker channel %d volume: %d dB (%d)\r\n", request->bChannelNumber, spk_volume_db, volume); - bsp_codec_volume_set(volume, NULL); - return true; - } else { - TU_LOG1("Feature unit set request not supported, entity = %u, selector = %u, request = %u\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - return false; - } -} - -//--------------------------------------------------------------------+ -// Application Callback API Implementations -//--------------------------------------------------------------------+ - -// Invoked when audio class specific get request received for an entity -bool tud_audio_get_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; - - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - return tud_audio_clock_get_request(rhport, request); - } - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - return tud_audio_feature_unit_get_request(rhport, request); - } else { - TU_LOG1("Get request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - } - return false; -} - -// Invoked when audio class specific set request received for an entity -bool tud_audio_set_req_entity_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) -{ - audio_control_request_t const *request = (audio_control_request_t const *)p_request; - - if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT) { - return tud_audio_feature_unit_set_request(rhport, request, buf); - } - if (request->bEntityID == UAC2_ENTITY_CLOCK) { - return tud_audio_clock_set_request(rhport, request, buf); - } - TU_LOG1("Set request not handled, entity = %d, selector = %d, request = %d\r\n", - request->bEntityID, request->bControlSelector, request->bRequest); - - return false; -} - -bool tud_audio_set_itf_close_EP_cb(uint8_t rhport, tusb_control_request_t const *p_request) -{ - (void)rhport; - - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); - - if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) { - TU_LOG2("Speaker interface closed"); - s_spk_active = false; - } else if (ITF_NUM_AUDIO_STREAMING_MIC == itf && alt == 0) { - TU_LOG2("Microphone interface closed"); - s_mic_active = false; - } - - return true; -} - -bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const *p_request) -{ - (void)rhport; - uint8_t const itf = tu_u16_low(tu_le16toh(p_request->wIndex)); - uint8_t const alt = tu_u16_low(tu_le16toh(p_request->wValue)); - - TU_LOG2("Set interface %d alt %d\r\n", itf, alt); - if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) { - // due to the limitation of the codec, we just support one resolution - // TODO: configuration of the resolution of the speaker - uint8_t spk_resolution = spk_resolutions_per_format[alt - 1]; - s_spk_resolution = spk_resolution; - s_spk_bytes_ms = s_sample_rate / 1000 * s_spk_resolution * DEFAULT_PLAYER_CHANNEL / 8; - s_spk_active = true; - s_spk_buf_len = 0; - xTaskNotifyGive(spk_task_handle); - TU_LOG1("Speaker interface %d-%d opened\n", itf, alt); - } else if (ITF_NUM_AUDIO_STREAMING_MIC == itf && alt != 0) { - // due to the limitation of the codec, we just support one resolution - // TODO: configuration of the resolution of the microphone - uint8_t mic_resolution = mic_resolutions_per_format[alt - 1]; - s_mic_resolution = mic_resolution; - s_mic_bytes_ms = s_sample_rate / 1000 * s_mic_resolution * DEFAULT_RECORDER_CHANNEL / 8; - s_mic_active = true; - s_mic_read_buf_len = 0; - xTaskNotifyGive(mic_task_handle); - TU_LOG1("Microphone interface %d-%d opened\n", itf, alt); - } - - return true; -} - -bool tud_audio_rx_done_post_read_cb(uint8_t rhport, uint16_t n_bytes_received, uint8_t func_id, uint8_t ep_out, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)func_id; - (void)ep_out; - (void)cur_alt_setting; - - int bytes_remained = tud_audio_available(); - size_t bytes_require = (CFG_TUD_AUDIO_FUNC_1_EP_OUT_SW_BUF_MS - 1) * s_spk_bytes_ms; - if (bytes_remained < bytes_require) { - return true; - } - // read data chunk by chunk - size_t bytes_frame = s_spk_resolution * DEFAULT_PLAYER_CHANNEL / 8; - bytes_require = bytes_remained - bytes_remained % bytes_frame; - s_spk_buf_len = tud_audio_read(s_spk_buf, bytes_require); - xTaskNotifyGive(spk_task_handle); - return true; -} - -bool tud_audio_tx_done_pre_load_cb(uint8_t rhport, uint8_t itf, uint8_t ep_in, uint8_t cur_alt_setting) -{ - (void)rhport; - (void)itf; - (void)ep_in; - (void)cur_alt_setting; - - size_t bytes_require = (CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_MS - 1) * s_mic_bytes_ms; - tu_fifo_t *sw_in_fifo = tud_audio_get_ep_in_ff(); - uint16_t fifo_remained = tu_fifo_remaining(sw_in_fifo); - if (fifo_remained < bytes_require) { - return true; - } - // load data chunk by chunk - UAC_ENTER_CRITICAL(); - if (s_mic_read_buf_len > 0) { - tud_audio_write(s_mic_read_buf, s_mic_read_buf_len); - s_mic_read_buf_len = 0; - } - UAC_EXIT_CRITICAL(); - return true; -} diff --git a/examples/usb_headset/sdkconfig.defaults b/examples/usb_headset/sdkconfig.defaults index 5c19183..a91bb74 100644 --- a/examples/usb_headset/sdkconfig.defaults +++ b/examples/usb_headset/sdkconfig.defaults @@ -41,4 +41,10 @@ CONFIG_ESP32S3_DATA_CACHE_64KB=y CONFIG_ESP32S3_DATA_CACHE_LINE_64B=y # BSP -CONFIG_BSP_LCD_DRAW_BUF_HEIGHT=10 \ No newline at end of file +CONFIG_BSP_LCD_DRAW_BUF_HEIGHT=10 + +# +# USB Device UAC +# +CONFIG_UAC_SPEAKER_CHANNEL_NUM=1 +CONFIG_UAC_MIC_CHANNEL_NUM=1