summaryrefslogtreecommitdiff
path: root/examples/device/uac2_headset/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'examples/device/uac2_headset/src/main.c')
-rw-r--r--examples/device/uac2_headset/src/main.c544
1 files changed, 355 insertions, 189 deletions
diff --git a/examples/device/uac2_headset/src/main.c b/examples/device/uac2_headset/src/main.c
index 6b2ab0973..96fa66f1e 100644
--- a/examples/device/uac2_headset/src/main.c
+++ b/examples/device/uac2_headset/src/main.c
@@ -37,9 +37,9 @@
// List of supported sample rates
const uint32_t sample_rates[] = {44100, 48000};
-uint32_t current_sample_rate = 44100;
+uint32_t current_sample_rate = 44100;
-#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
+#define N_SAMPLE_RATES TU_ARRAY_SIZE(sample_rates)
/* Blink pattern
* - 25 ms : streaming data
@@ -47,16 +47,14 @@ uint32_t current_sample_rate = 44100;
* - 1000 ms : device mounted
* - 2500 ms : device is suspended
*/
-enum
-{
+enum {
BLINK_STREAMING = 25,
BLINK_NOT_MOUNTED = 250,
BLINK_MOUNTED = 1000,
BLINK_SUSPENDED = 2500,
};
-enum
-{
+enum {
VOLUME_CTRL_0_DB = 0,
VOLUME_CTRL_10_DB = 2560,
VOLUME_CTRL_20_DB = 5120,
@@ -75,8 +73,8 @@ static uint32_t blink_interval_ms = BLINK_NOT_MOUNTED;
// Audio controls
// Current states
-int8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
-int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
+uint8_t mute[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1]; // +1 for master channel 0
+int16_t volume[CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1];// +1 for master channel 0
// Buffer for microphone data
int32_t mic_buf[CFG_TUD_AUDIO_FUNC_1_EP_IN_SW_BUF_SZ / 4];
@@ -95,26 +93,21 @@ void audio_task(void);
void audio_control_task(void);
/*------------- MAIN -------------*/
-int main(void)
-{
+int main(void) {
board_init();
// init device stack on configured roothub port
tusb_rhport_init_t dev_init = {
- .role = TUSB_ROLE_DEVICE,
- .speed = TUSB_SPEED_AUTO
- };
+ .role = TUSB_ROLE_DEVICE,
+ .speed = TUSB_SPEED_AUTO};
tusb_init(BOARD_TUD_RHPORT, &dev_init);
- if (board_init_after_tusb) {
- board_init_after_tusb();
- }
+ board_init_after_tusb();
TU_LOG1("Headset running\r\n");
- while (1)
- {
- tud_task(); // TinyUSB device task
+ while (1) {
+ tud_task();// TinyUSB device task
audio_task();
audio_control_task();
led_blinking_task();
@@ -126,70 +119,234 @@ int main(void)
//--------------------------------------------------------------------+
// Invoked when device is mounted
-void tud_mount_cb(void)
-{
+void tud_mount_cb(void) {
blink_interval_ms = BLINK_MOUNTED;
}
// Invoked when device is unmounted
-void tud_umount_cb(void)
-{
+void tud_umount_cb(void) {
blink_interval_ms = BLINK_NOT_MOUNTED;
}
// 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)
-{
- (void)remote_wakeup_en;
+void tud_suspend_cb(bool remote_wakeup_en) {
+ (void) remote_wakeup_en;
blink_interval_ms = BLINK_SUSPENDED;
}
// Invoked when usb bus is resumed
-void tud_resume_cb(void)
-{
+void tud_resume_cb(void) {
blink_interval_ms = tud_mounted() ? BLINK_MOUNTED : BLINK_NOT_MOUNTED;
}
+//--------------------------------------------------------------------+
+// Audio Callback Functions
+//--------------------------------------------------------------------+
+
+//--------------------------------------------------------------------+
+// UAC1 Helper Functions
+//--------------------------------------------------------------------+
+
+static bool audio10_set_req_ep(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_SET_CUR) {
+ // Request uses 3 bytes
+ TU_VERIFY(p_request->wLength == 3);
+
+ current_sample_rate = tu_unaligned_read32(pBuff) & 0x00FFFFFF;
+
+ TU_LOG2("EP set current freq: %" PRIu32 "\r\n", current_sample_rate);
+
+ return true;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_ep(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+
+ switch (ctrlSel) {
+ case AUDIO10_EP_CTRL_SAMPLING_FREQ:
+ if (p_request->bRequest == AUDIO10_CS_REQ_GET_CUR) {
+ TU_LOG2("EP get current freq\r\n");
+
+ uint8_t freq[3];
+ freq[0] = (uint8_t) (current_sample_rate & 0xFF);
+ freq[1] = (uint8_t) ((current_sample_rate >> 8) & 0xFF);
+ freq[2] = (uint8_t) ((current_sample_rate >> 16) & 0xFF);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, freq, sizeof(freq));
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+
+ return false;
+}
+
+static bool audio10_set_req_entity(tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our speaker feature unit
+ if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 1);
+
+ mute[channelNum] = pBuff[0];
+
+ TU_LOG2(" Set Mute: %d of channel: %u\r\n", mute[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_SET_CUR:
+ // Only 1st form is supported
+ TU_VERIFY(p_request->wLength == 2);
+
+ volume[channelNum] = (int16_t)tu_unaligned_read16(pBuff) / 256;
+
+ TU_LOG2(" Set Volume: %d dB of channel: %u\r\n", volume[channelNum], channelNum);
+ return true;
+
+ default:
+ return false; // not supported
+ }
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+static bool audio10_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
+ uint8_t channelNum = TU_U16_LOW(p_request->wValue);
+ uint8_t ctrlSel = TU_U16_HIGH(p_request->wValue);
+ uint8_t entityID = TU_U16_HIGH(p_request->wIndex);
+
+ // If request is for our speaker feature unit
+ if (entityID == UAC1_ENTITY_SPK_FEATURE_UNIT) {
+ switch (ctrlSel) {
+ case AUDIO10_FU_CTRL_MUTE:
+ // Audio control mute cur parameter block consists of only one byte - we thus can send it right away
+ // There does not exist a range parameter block for mute
+ TU_LOG2(" Get Mute of channel: %u\r\n", channelNum);
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &mute[channelNum], 1);
+
+ case AUDIO10_FU_CTRL_VOLUME:
+ switch (p_request->bRequest) {
+ case AUDIO10_CS_REQ_GET_CUR:
+ TU_LOG2(" Get Volume of channel: %u\r\n", channelNum);
+ {
+ int16_t vol = (int16_t) volume[channelNum];
+ vol = vol * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &vol, sizeof(vol));
+ }
+
+ case AUDIO10_CS_REQ_GET_MIN:
+ TU_LOG2(" Get Volume min of channel: %u\r\n", channelNum);
+ {
+ int16_t min = -90; // -90 dB
+ min = min * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &min, sizeof(min));
+ }
+
+ case AUDIO10_CS_REQ_GET_MAX:
+ TU_LOG2(" Get Volume max of channel: %u\r\n", channelNum);
+ {
+ int16_t max = 30; // +30 dB
+ max = max * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &max, sizeof(max));
+ }
+
+ case AUDIO10_CS_REQ_GET_RES:
+ TU_LOG2(" Get Volume res of channel: %u\r\n", channelNum);
+ {
+ int16_t res = 1; // 1 dB
+ res = res * 256; // convert to 1/256 dB units
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, p_request, &res, sizeof(res));
+ }
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ break;
+
+ // Unknown/Unsupported control
+ default:
+ TU_BREAKPOINT();
+ return false;
+ }
+ }
+
+ return false;
+}
+
+//--------------------------------------------------------------------+
+// UAC2 Helper Functions
+//--------------------------------------------------------------------+
+
+#if TUD_OPT_HIGH_SPEED
+
// Helper for clock get requests
-static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t const *request)
-{
+static bool audio20_clock_get_request(uint8_t rhport, audio20_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)
- {
+ if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
+ if (request->bRequest == AUDIO20_CS_REQ_CUR) {
TU_LOG1("Clock get current freq %" PRIu32 "\r\n", current_sample_rate);
- audio_control_cur_4_t curf = { (int32_t) tu_htole32(current_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)
- };
+ audio20_control_cur_4_t curf = {(int32_t) tu_htole32(current_sample_rate)};
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &curf, sizeof(curf));
+ } else if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ audio20_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++)
- {
+ 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);
+ 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));
+ 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 };
+ } else if (request->bControlSelector == AUDIO20_CS_CTRL_CLK_VALID &&
+ request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_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));
+ 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);
@@ -197,25 +354,21 @@ static bool tud_audio_clock_get_request(uint8_t rhport, audio_control_request_t
}
// 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;
+static bool audio20_clock_set_request(uint8_t rhport, audio20_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);
+ TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_CS_CTRL_SAM_FREQ)
- {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_4_t));
+ if (request->bControlSelector == AUDIO20_CS_CTRL_SAM_FREQ) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_4_t));
- current_sample_rate = (uint32_t) ((audio_control_cur_4_t const *)buf)->bCur;
+ current_sample_rate = (uint32_t) ((audio20_control_cur_4_t const *) buf)->bCur;
TU_LOG1("Clock set current freq: %" PRIu32 "\r\n", current_sample_rate);
return true;
- }
- else
- {
+ } else {
TU_LOG1("Clock set request not supported, entity = %u, selector = %u, request = %u\r\n",
request->bEntityID, request->bControlSelector, request->bRequest);
return false;
@@ -223,33 +376,25 @@ static bool tud_audio_clock_set_request(uint8_t rhport, audio_control_request_t
}
// Helper for feature unit get requests
-static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_request_t const *request)
-{
+static bool audio20_feature_unit_get_request(uint8_t rhport, audio20_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 = mute[request->bChannelNumber] };
+ if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE && request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_1_t mute1 = {.bCur = mute[request->bChannelNumber]};
TU_LOG1("Get channel %u 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 (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
- {
- if (request->bRequest == AUDIO_CS_REQ_RANGE)
- {
- 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) }
- };
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &mute1, sizeof(mute1));
+ } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
+ if (request->bRequest == AUDIO20_CS_REQ_RANGE) {
+ audio20_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)}};
TU_LOG1("Get channel %u 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(volume[request->bChannelNumber]) };
+ return tud_audio_buffer_and_schedule_control_xfer(rhport, (tusb_control_request_t const *) request, &range_vol, sizeof(range_vol));
+ } else if (request->bRequest == AUDIO20_CS_REQ_CUR) {
+ audio20_control_cur_2_t cur_vol = {.bCur = tu_htole16(volume[request->bChannelNumber])};
TU_LOG1("Get channel %u 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));
+ 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",
@@ -259,179 +404,199 @@ static bool tud_audio_feature_unit_get_request(uint8_t rhport, audio_control_req
}
// 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;
+static bool audio20_feature_unit_set_request(uint8_t rhport, audio20_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);
+ TU_VERIFY(request->bRequest == AUDIO20_CS_REQ_CUR);
- if (request->bControlSelector == AUDIO_FU_CTRL_MUTE)
- {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_1_t));
+ if (request->bControlSelector == AUDIO20_FU_CTRL_MUTE) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_1_t));
- mute[request->bChannelNumber] = ((audio_control_cur_1_t const *)buf)->bCur;
+ mute[request->bChannelNumber] = ((audio20_control_cur_1_t const *) buf)->bCur;
TU_LOG1("Set channel %d Mute: %d\r\n", request->bChannelNumber, mute[request->bChannelNumber]);
return true;
- }
- else if (request->bControlSelector == AUDIO_FU_CTRL_VOLUME)
- {
- TU_VERIFY(request->wLength == sizeof(audio_control_cur_2_t));
+ } else if (request->bControlSelector == AUDIO20_FU_CTRL_VOLUME) {
+ TU_VERIFY(request->wLength == sizeof(audio20_control_cur_2_t));
- volume[request->bChannelNumber] = ((audio_control_cur_2_t const *)buf)->bCur;
+ volume[request->bChannelNumber] = ((audio20_control_cur_2_t const *) buf)->bCur;
TU_LOG1("Set channel %d volume: %d dB\r\n", request->bChannelNumber, volume[request->bChannelNumber] / 256);
return true;
- }
- else
- {
+ } 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;
+static bool audio20_get_req_entity(uint8_t rhport, tusb_control_request_t const *p_request) {
+ audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_get_request(rhport, request);
+ return audio20_clock_get_request(rhport, request);
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return tud_audio_feature_unit_get_request(rhport, request);
- else
- {
+ return audio20_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;
+static bool audio20_set_req_entity(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *buf) {
+ audio20_control_request_t const *request = (audio20_control_request_t const *) p_request;
if (request->bEntityID == UAC2_ENTITY_SPK_FEATURE_UNIT)
- return tud_audio_feature_unit_set_request(rhport, request, buf);
+ return audio20_feature_unit_set_request(rhport, request, buf);
if (request->bEntityID == UAC2_ENTITY_CLOCK)
- return tud_audio_clock_set_request(rhport, request, buf);
+ return audio20_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;
+#endif // TUD_OPT_HIGH_SPEED
+
+// Invoked when audio class specific set request received for an EP
+bool tud_audio_set_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff) {
+ (void) rhport;
+ (void) pBuff;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_ep(p_request, pBuff);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// Invoked when audio class specific get request received for an EP
+bool tud_audio_get_req_ep_cb(uint8_t rhport, tusb_control_request_t const *p_request) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_ep(rhport, p_request);
+ } else if (tud_audio_version() == 2) {
+ // We do not support any requests here
+ }
+
+ return false;// Yet not implemented
+}
+
+// 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) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_get_req_entity(rhport, p_request);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_get_req_entity(rhport, p_request);
+#endif
+ }
+
+ 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) {
+ (void) rhport;
+
+ if (tud_audio_version() == 1) {
+ return audio10_set_req_entity(p_request, buf);
+#if TUD_OPT_HIGH_SPEED
+ } else if (tud_audio_version() == 2) {
+ return audio20_set_req_entity(rhport, p_request, buf);
+#endif
+ }
+
+ 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)
- blink_interval_ms = BLINK_MOUNTED;
+ if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt == 0) {
+ blink_interval_ms = BLINK_MOUNTED;
+ }
return true;
}
-bool tud_audio_set_itf_cb(uint8_t rhport, tusb_control_request_t const * p_request)
-{
- (void)rhport;
+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)
- blink_interval_ms = BLINK_STREAMING;
+ if (ITF_NUM_AUDIO_STREAMING_SPK == itf && alt != 0) {
+ blink_interval_ms = BLINK_STREAMING;
+ }
// Clear buffer when streaming format is changed
spk_data_size = 0;
- if(alt != 0)
- {
- current_resolution = resolutions_per_format[alt-1];
+ if (alt != 0) {
+ current_resolution = resolutions_per_format[alt - 1];
}
return true;
}
-bool tud_audio_rx_done_pre_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;
-
- spk_data_size = tud_audio_read(spk_buf, n_bytes_received);
- 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;
-
- // This callback could be used to fill microphone data separately
- return true;
-}
-
//--------------------------------------------------------------------+
// AUDIO Task
//--------------------------------------------------------------------+
-void audio_task(void)
-{
+// This task simulates an audio transfer callback, one frame is sent/received every 1ms.
+// In a real application, this would be replaced with actual I2S send/receive callback.
+void audio_task(void) {
+ static uint32_t start_ms = 0;
+ uint32_t curr_ms = board_millis();
+ if (start_ms == curr_ms) return;// not enough time
+ start_ms = curr_ms;
// When new data arrived, copy data from speaker buffer, to microphone buffer
// and send it over
// Only support speaker & headphone both have the same resolution
// If one is 16bit another is 24bit be care of LOUD noise !
- if (spk_data_size)
- {
- if (current_resolution == 16)
- {
- int16_t *src = (int16_t*)spk_buf;
- int16_t *limit = (int16_t*)spk_buf + spk_data_size / 2;
- int16_t *dst = (int16_t*)mic_buf;
- while (src < limit)
- {
+ spk_data_size = tud_audio_read(spk_buf, sizeof(spk_buf));
+ if (spk_data_size) {
+ if (current_resolution == 16) {
+ int16_t *src = (int16_t *) spk_buf;
+ int16_t *limit = (int16_t *) spk_buf + spk_data_size / 2;
+ int16_t *dst = (int16_t *) mic_buf;
+ while (src < limit) {
// Combine two channels into one
int32_t left = *src++;
int32_t right = *src++;
*dst++ = (int16_t) ((left >> 1) + (right >> 1));
}
- tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
+ tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
spk_data_size = 0;
- }
- else if (current_resolution == 24)
- {
+ } else if (current_resolution == 24) {
int32_t *src = spk_buf;
int32_t *limit = spk_buf + spk_data_size / 4;
int32_t *dst = mic_buf;
- while (src < limit)
- {
+ while (src < limit) {
// Combine two channels into one
int32_t left = *src++;
int32_t right = *src++;
*dst++ = (int32_t) ((uint32_t) ((left >> 1) + (right >> 1)) & 0xffffff00ul);
}
- tud_audio_write((uint8_t *)mic_buf, (uint16_t) (spk_data_size / 2));
+ tud_audio_write((uint8_t *) mic_buf, (uint16_t) (spk_data_size / 2));
spk_data_size = 0;
}
}
}
-void audio_control_task(void)
-{
+void audio_control_task(void) {
// Press on-board button to control volume
// Open host volume control, volume should switch between 10% and 100%
@@ -440,28 +605,30 @@ void audio_control_task(void)
static uint32_t start_ms = 0;
static uint32_t btn_prev = 0;
- if ( board_millis() - start_ms < interval_ms) return; // not enough time
+ if (board_millis() - start_ms < interval_ms) return;// not enough time
start_ms += interval_ms;
uint32_t btn = board_button_read();
- if (!btn_prev && btn)
- {
+ // Even UAC1 spec have status interrupt support like UAC2, most host do not support it
+ // So you have to either use UAC2 or use old day HID volume control
+ TU_VERIFY((tud_audio_version() == 1),);
+
+ if (!btn_prev && btn) {
// Adjust volume between 0dB (100%) and -30dB (10%)
- for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++)
- {
+ for (int i = 0; i < CFG_TUD_AUDIO_FUNC_1_N_CHANNELS_RX + 1; i++) {
volume[i] = volume[i] == 0 ? -VOLUME_CTRL_30_DB : 0;
}
// 6.1 Interrupt Data Message
- const audio_interrupt_data_t data = {
- .bInfo = 0, // Class-specific interrupt, originated from an interface
- .bAttribute = AUDIO_CS_REQ_CUR, // Caused by current settings
- .wValue_cn_or_mcn = 0, // CH0: master volume
- .wValue_cs = AUDIO_FU_CTRL_VOLUME, // Volume change
- .wIndex_ep_or_int = 0, // From the interface itself
- .wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT, // From feature unit
- };
+ const audio_interrupt_data_t data = {.v2 = {
+ .bInfo = 0, // Class-specific interrupt, originated from an interface
+ .bAttribute = AUDIO20_CS_REQ_CUR, // Caused by current settings
+ .wValue_cn_or_mcn = 0, // CH0: master volume
+ .wValue_cs = AUDIO20_FU_CTRL_VOLUME, // Volume change
+ .wIndex_ep_or_int = 0, // From the interface itself
+ .wIndex_entity_id = UAC2_ENTITY_SPK_FEATURE_UNIT,// From feature unit
+ }};
tud_audio_int_write(&data);
}
@@ -472,8 +639,7 @@ void audio_control_task(void)
//--------------------------------------------------------------------+
// BLINKING TASK
//--------------------------------------------------------------------+
-void led_blinking_task(void)
-{
+void led_blinking_task(void) {
static uint32_t start_ms = 0;
static bool led_state = false;