arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioDeviceBase.h
Go to the documentation of this file.
1#pragma once
2#include <cmath>
3#include <cstddef>
4#include <cstdint>
5#include <cstring>
6#include <functional>
7#include <mutex>
8#include <vector>
9
12
13#ifdef ESP32
14#ifndef ARDUINO_USB_MODE
15#error This Microcontroller has no Native USB interface
16#else
17#if ARDUINO_USB_MODE == 1
18#error This sketch should be used when USB is in OTG mode
19#endif
20#endif
21#else
22#ifndef USE_TINYUSB
23#error This Microcontroller has no Native USB interface
24#endif
25#endif
26
27#include "AudioLogger.h"
30
31extern "C" {
32#include "device/usbd.h"
33#include "device/usbd_pvt.h"
34#include "tusb.h"
35}
36
37#define USB_DESCR_MAX_LEN 512
38
39// TinyUSB >= 0.15 (ESP32 IDF v5+) renamed UAC2 symbols with an AUDIO20_ prefix
40// and changed three function signatures. These shims let the driver build
41// against both versions; detect the new API by the presence of the new define.
42#ifdef TUD_AUDIO20_DESC_IAD_LEN
43#define TUD_AUDIO_DESC_IAD_LEN TUD_AUDIO20_DESC_IAD_LEN
44#define AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL \
45 AUDIO20_CS_AC_INTERFACE_INPUT_TERMINAL
46#define AUDIO_CS_AC_INTERFACE_OUTPUT_TERMINAL \
47 AUDIO20_CS_AC_INTERFACE_OUTPUT_TERMINAL
48#define AUDIO_CS_CTRL_SAM_FREQ AUDIO20_CS_CTRL_SAM_FREQ
49#define AUDIO_CS_CTRL_CLK_VALID AUDIO20_CS_CTRL_CLK_VALID
50#define AUDIO_CS_REQ_CUR AUDIO20_CS_REQ_CUR
51#define AUDIO_CS_REQ_RANGE AUDIO20_CS_REQ_RANGE
52#define AUDIO_CS_AS_INTERFACE_AS_GENERAL AUDIO20_CS_AS_INTERFACE_AS_GENERAL
53#define AUDIO_CS_AS_INTERFACE_FORMAT_TYPE AUDIO20_CS_AS_INTERFACE_FORMAT_TYPE
54#define audio_desc_cs_ac_interface_t audio20_desc_cs_ac_interface_t
55#define audio_desc_cs_as_interface_t audio20_desc_cs_as_interface_t
56#define audio_desc_type_I_format_t audio20_desc_type_I_format_t
57// New: is_isr parameter added; fifo element-size arg removed
58#define TUSB_EDPT_XFER(rp, ep, buf, sz) usbd_edpt_xfer(rp, ep, buf, sz, false)
59#define TUSB_EDPT_XFER_FIFO(rp, ep, ff, sz) \
60 usbd_edpt_xfer_fifo(rp, ep, ff, sz, false)
61#define TUSB_FIFO_CONFIG(f, buf, d, ov) tu_fifo_config(f, buf, d, ov)
62#else
63// Old TinyUSB (RP2040 / Adafruit bundle)
64#define TUSB_EDPT_XFER(rp, ep, buf, sz) usbd_edpt_xfer(rp, ep, buf, sz)
65#define TUSB_EDPT_XFER_FIFO(rp, ep, ff, sz) usbd_edpt_xfer_fifo(rp, ep, ff, sz)
66#define TUSB_FIFO_CONFIG(f, buf, d, ov) tu_fifo_config(f, buf, d, 1, ov)
67// Ensure control selector and request constants are available on old TinyUSB
68// too
69#ifndef AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL
70#define AUDIO10_CS_AC_INTERFACE_INPUT_TERMINAL \
71 AUDIO_CS_AC_INTERFACE_INPUT_TERMINAL
72#endif
73#ifndef AUDIO_CS_CTRL_CLK_VALID
74#define AUDIO_CS_CTRL_CLK_VALID 0x02u
75#endif
76#ifndef AUDIO_CS_REQ_RANGE
77#define AUDIO_CS_REQ_RANGE 0x02u
78#endif
79#endif
80
81// Feature Unit control selectors (UAC2 Table A-23)
82#ifndef AUDIO_FU_CTRL_MUTE
83#define AUDIO_FU_CTRL_MUTE 0x01u
84#endif
85#ifndef AUDIO_FU_CTRL_VOLUME
86#define AUDIO_FU_CTRL_VOLUME 0x02u
87#endif
88
89namespace audio_tools {
90
91// Discrete sample rates supported by the UAC2 clock source.
92static constexpr uint32_t kSupportedSampleRates[] = {
93 8000, 11025, 16000, 22050, 24000, 32000, 44100, 48000};
96
117 enum audio_format_type_t {
118 AUDIO_FORMAT_TYPE_I,
119 AUDIO_FORMAT_TYPE_II,
120 AUDIO_FORMAT_TYPE_III,
121 };
122
129 enum audio_feedback_method_t {
130 AUDIO_FEEDBACK_METHOD_DISABLED,
131 AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
132 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
133 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only
134 AUDIO_FEEDBACK_METHOD_FIFO_COUNT
135 };
136
143 struct audiod_function_t {
144 uint8_t rhport;
145 uint8_t const* p_desc; // Pointer to Standard AC Interface Descriptor
146 uint8_t ep_in; // TX audio data EP.
147 uint16_t ep_in_sz; // Current size of TX EP
148 uint8_t
149 ep_in_as_intf_num; // Standard AS Interface Descriptor number for IN
150 uint8_t ep_out; // RX audio data EP.
151 uint16_t ep_out_sz; // Current size of RX EP
152 uint8_t
153 ep_out_as_intf_num; // Standard AS Interface Descriptor number for OUT
154 uint8_t ep_fb; // Feedback EP.
155 uint8_t ep_int; // Audio control interrupt EP.
156 bool mounted; // Device opened
157 uint16_t desc_length; // Length of audio function descriptor
158 struct {
159 uint32_t value;
160 uint32_t min_value;
161 uint32_t max_value;
162 uint8_t frame_shift;
163 uint8_t compute_method;
164 bool format_correction;
165 union {
166 uint8_t power_of_2;
167 float float_const;
168 struct {
169 uint32_t sample_freq;
170 uint32_t mclk_freq;
171 } fixed;
172 struct {
173 uint32_t nom_value;
174 uint32_t fifo_lvl_avg;
175 uint16_t fifo_lvl_thr;
176 uint16_t rate_const[2];
177 } fifo_count;
178 } compute;
179 } feedback;
180 uint32_t sample_rate_tx;
181 uint16_t packet_sz_tx[3];
182 uint8_t bclock_id_tx;
183 uint8_t interval_tx;
184 audio_format_type_t format_type_tx;
185 uint8_t n_channels_tx;
186 uint8_t n_bytes_per_sample_tx;
187 // Fractional sample accumulator for IN-endpoint flow control. Carries the
188 // sub-frame remainder (e.g. the 0.1 sample/frame of 44100 Hz) so the
189 // long-term average packet size matches the real sample rate.
190 uint32_t tx_sample_acc;
191 // From this point, data is not cleared by bus reset
192 uint8_t ctrl_buf_sz;
193 tu_fifo_t ep_out_ff;
194 tu_fifo_t ep_in_ff;
195 std::vector<uint8_t> ctrl_buf;
196 std::vector<uint8_t> alt_setting;
197 std::vector<uint8_t> lin_buf_out;
198 std::vector<uint8_t> lin_buf_in;
199 std::vector<uint32_t> fb_buf;
200 std::vector<uint8_t> ep_in_sw_buf; // For feedback EP
201 std::vector<uint8_t> ep_out_sw_buf; // For feedback EP
202 };
203
210 struct audio_feedback_params_t {
211 uint8_t method;
212 uint32_t sample_freq; // sample frequency in Hz
213
214 union {
215 struct {
216 uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to
217 // which sample clock is based on
218 } frequency;
219 };
220 };
221
222 public:
225
230
237 USBAudioConfig cfg;
238 switch (mode) {
239 case RX_MODE:
240 cfg.enable_ep_out = true;
241 cfg.enable_ep_in = false;
242 break;
243 case TX_MODE:
244 cfg.enable_ep_out = false;
245 cfg.enable_ep_in = true;
246 break;
247 case RXTX_MODE:
248 cfg.enable_ep_out = true;
249 cfg.enable_ep_in = true;
250 break;
251 default:
252 break;
253 }
254 return cfg;
255 }
256
258 void setAudioInfo(AudioInfo info) override {
260 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
262 return;
263 }
264 // full flexibility when not started yet
265 if (!is_started_) {
269 return;
270 }
271
272 // when started only sample rate can be changed on the fly
274 if (config_.channels != info.channels) {
275 LOGE(
276 "Could not change channel count from %d to %d: channel count is "
277 "fixed at startup!",
279 }
281 LOGE(
282 "Could not change bits per sample from %d to %d: bits per sample is "
283 "fixed at startup!",
285 }
286 // notifiy subscribed entities
288 // notify host about sample rate change via control request callback;
290 }
291
300 bool begin(const USBAudioConfig& cfg) {
301 if (!is_started_) {
302 config_ = cfg;
303 } else if (!configChanged(cfg)) {
304 return true; // already running with same config
305 } else {
307 }
308 return begin();
309 }
310
317 bool begin() {
318 if (!is_started_) {
320 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
322 return false;
323 }
324
325 // Resize platform buffers — virtual so each platform uses the right API.
327
328 const int n = getAudioCount();
329 // 192 bytes needed for multi-rate RANGE (2+14*12=170); 64 suffices for single rate
331 ctrl_buf_sz_.assign(n, cb_sz);
332
333 const uint16_t sw_buf = fifoSize();
334 ep_in_sw_buf_sz_.assign(n, sw_buf);
335 ep_out_sw_buf_sz_.assign(n, sw_buf);
336
339 desc_len_.assign(n, desc_len);
340
341 // master (index 0) + one per channel
342 const size_t vol_sz = (size_t)config_.channels + 1;
343 volume_.assign(vol_sz, 1.0f);
344 mute_.assign(vol_sz, false);
345
346
347 audiod_init();
348
349 if (!beginUSB()) {
350 LOGE("beginUSB failed");
351 return false;
352 }
353 is_started_ = true;
354 }
355
356 // Push current state to the host. sendInterruptNotification()
357 // is a no-op when not yet mounted, so this is harmless on first boot.
359 for (uint8_t ch = 0; ch < (uint8_t)volume_.size(); ch++) {
361 setMute(isMute(ch), ch);
362 }
363 is_active_ = true;
364 return true;
365 }
373
375 inline bool isEpInEnabled() const { return config_.enable_ep_in; }
376
378 inline bool isEpOutEnabled() const { return config_.enable_ep_out; }
379
384
391
392 // ── Volume / Mute / Sample-rate API ─────────────────────────────────────
393
395 float volume() override { return volume(0); }
396
398 bool setVolume(float volume) override { return setVolume(volume, 0); }
399
403 float volume(uint8_t channel) {
404 return (channel < volume_.size()) ? volume_[channel] : 0.0f;
405 }
406
411 bool setVolume(float vol, uint8_t channel) {
412 LOGW("setVolume %f channel: %d", vol, channel);
413 if (channel >= volume_.size()) return false;
414 volume_[channel] = vol;
415 if (volume_cb_) volume_cb_(vol, channel);
418 return true;
419 }
420
423 bool isMute(uint8_t channel = 0) const {
424 return (channel < mute_.size()) ? mute_[channel] : false;
425 }
426
431 bool setMute(bool m, uint8_t channel = 0) {
432 LOGW("setMute %s channel: %d", m ? "true" : "false", channel);
433 if (channel >= mute_.size()) return false;
434 mute_[channel] = m;
435 if (mute_cb_) mute_cb_(m, channel);
438 return true;
439 }
440
444 void setVolumeCallback(std::function<void(float, uint8_t)> cb) {
445 volume_cb_ = std::move(cb);
446 }
447
451 void setMuteCallback(std::function<void(bool, uint8_t)> cb) {
452 mute_cb_ = std::move(cb);
453 }
454
458 void setSampleRateCallback(std::function<void(uint32_t)> cb) {
459 sample_rate_cb_ = std::move(cb);
460 }
461
466 void setStreamingStateCallback(std::function<void(bool, bool)> cb) {
467 streaming_state_cb_ = std::move(cb);
468 }
469
471 bool isStreamingActive() const {
473 }
474
476 bool isStreamingActiveTx() const {
477 for (const auto& fct : audiod_fct_) {
478 if (fct.ep_in != 0) return true;
479 }
480 return false;
481 }
482
484 bool isStreamingActiveRx() const {
485 for (const auto& fct : audiod_fct_) {
486 if (fct.ep_out != 0) return true;
487 }
488 return false;
489 }
492
494 bool isFifoMutexEnabled() const { return true; }
495
497 uint8_t getAudioCount() const { return 1; }
498
500 bool mounted() const { return tud_mounted(); }
501
508 cb) {
509 get_req_itf_cb_ = cb;
510 }
511
518 cb) {
519 get_req_ep_cb_ = cb;
520 }
521
526 void setFbDoneCallback(std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
527 fb_done_cb_ = cb;
528 }
529
535 std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
536 int_done_cb_ = cb;
537 }
538
544 std::function<bool(USBAudioDeviceBase*, uint8_t, audiod_function_t*)>
545 cb) {
546 tx_done_cb_ = cb;
547 }
548
554 audiod_function_t*, uint16_t)>
555 cb) {
556 rx_done_cb_ = cb;
557 }
558
564 std::function<bool(USBAudioDeviceBase*, uint8_t)> cb) {
565 req_entity_cb_ = cb;
566 }
567
573 std::function<bool(USBAudioDeviceBase*, uint8_t,
575 cb) {
577 }
578
584 std::function<bool(USBAudioDeviceBase*, uint8_t,
586 cb) {
588 }
589
595 std::function<bool(USBAudioDeviceBase*, uint8_t,
597 cb) {
599 }
600
606 std::function<bool(USBAudioDeviceBase*, uint8_t,
608 cb) {
610 }
611
618 cb) {
620 }
621
627 std::function<void(USBAudioDeviceBase*, uint8_t, uint8_t,
628 audio_feedback_params_t*)>
629 cb) {
631 }
632
641
645 size_t write(const uint8_t* data, size_t len) {
646 if (!is_started_) return 0;
648
649 // disregard data if the host has not opened the capture device (alt=0)
650 if (!isStreamingActiveTx()) return len;
651
652 // update the volume
653 if (config_.volume_active) processVolume((uint8_t*)data, len);
654
655 // Write all data, retrying if the buffer is full. On single-core
656 // platforms (RP2040), serviceTinyUSB() drains the buffer by running
657 // tud_task() → xfer_cb. On dual-core (ESP32), the USB task drains
658 // independently and the SynchronizedNBufferRTOS blocks internally.
659 size_t written = 0;
660 while (written < len) {
661 int n = bufferTx().writeArray(data + written, len - written);
662 written += n;
663 if (written < len) {
664 serviceTinyUSB(); // drain buffer to make space
665 if (!isStreamingActiveTx()) break; // host stopped
666 }
667 }
668 return written;
669 }
670
673 size_t readBytes(uint8_t* buffer, size_t bufsize) {
674 if (!is_started_) return 0;
676 // get the data from the buffer
677 size_t ret = bufferRx().readArray(buffer, bufsize);
678 // upate the volume
679 if (config_.volume_active) processVolume(buffer, ret);
680
681 return ret;
682 }
683
685 int available() override {
686 if (!is_started_) return 0;
687 return bufferRx().available();
688 }
689
691 int availableForWrite() override {
692 if (!is_started_) return 0;
693 return bufferTx().availableForWrite();
694 }
695
697 operator bool() override { return is_started_ && mounted(); }
698
700 void end() {
701 for (auto& audio : audiod_fct_) {
702 tu_fifo_clear(&audio.ep_in_ff);
703 tu_fifo_clear(&audio.ep_out_ff);
704 std::fill(audio.lin_buf_in.begin(), audio.lin_buf_in.end(), 0);
705 std::fill(audio.lin_buf_out.begin(), audio.lin_buf_out.end(), 0);
706 }
707 bufferTx().reset();
708 bufferRx().reset();
709 is_started_ = false;
710 }
711
714 uint16_t audioPacketSize() const { return packetSize(); }
715
728 active_config_ = config_; // save active config
730 }
731
738 return (uint8_t)(1 + (config_.enable_ep_out ? 1 : 0) +
739 (config_.enable_ep_in ? 1 : 0));
740 }
741
748 static usbd_class_driver_t driver;
749 driver.name = "AUDIO";
750 driver.init = [](void) {
752 };
753 driver.deinit = [](void) {
755 };
756 driver.reset = [](uint8_t rhport) {
758 };
759 driver.open = [](uint8_t rhport, tusb_desc_interface_t const* itf_desc,
762 max_len);
763 };
764 driver.control_xfer_cb = [](uint8_t rhport, uint8_t stage,
765 tusb_control_request_t const* request) {
767 rhport, stage, request);
768 };
769 driver.xfer_cb = [](uint8_t rhport, uint8_t ep_addr, xfer_result_t result,
772 rhport, ep_addr, result, xferred_bytes);
773 };
774 driver.sof = [](uint8_t rhport, uint32_t frame_count) {
776 };
777
778 *count = 1;
779 return &driver;
780 }
781
783 bool isTxXferArmed() const { return tx_xfer_armed_; }
784
802 return audiod_fct_.empty() ? 0 : audiod_fct_[0].sample_rate_tx;
803 }
806 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_channels_tx;
807 }
810 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_bytes_per_sample_tx;
811 }
814 return audiod_fct_.empty() ? 0 : audiod_fct_[0].interval_tx;
815 }
816
817 protected:
818 bool is_started_ = false;
819 bool tx_xfer_armed_ = false;
826 bool is_active_ = false;
827 // ── Volume / mute state (sized to channels+1 in begin()) ─────────────────
828 std::vector<float> volume_;
829 std::vector<bool> mute_;
830 std::function<void(float, uint8_t)> volume_cb_;
831 std::function<void(bool, uint8_t)> mute_cb_;
833 std::function<void(bool, bool)> streaming_state_cb_;
837 std::function<void(USBAudioDeviceBase*, uint8_t rhport)> int_done_cb_;
838 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*)>
840 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*,
843 // Callback for interface GET requests
844 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
847 // Callback for endpoint GET requests
848 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
851
852 // Callback for feedback done event
855 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
858 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
861
862 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
865
866 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
869
870 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
873
875 audio_feedback_params_t* feedback_param)>
877
878 std::function<bool(USBAudioDeviceBase*, uint8_t func_id)>
881
882 std::vector<uint16_t> ep_out_sw_buf_sz_;
883 // (TUD_OPT_HIGH_SPEED ? 32 : 4) * CFG_TUD_AUDIO_EP_SZ_IN // Example write
884 // FIFO every 1ms, so it should be 8 times larger for HS device
885 std::vector<uint16_t> ep_in_sw_buf_sz_;
886 // calculate!
887 std::vector<uint16_t> desc_len_;
888 // 64
889 std::vector<uint16_t> ctrl_buf_sz_;
890
891 std::vector<audiod_function_t> audiod_fct_;
892 // std::vector<osal_mutex_def_t> ep_in_ff_mutex_wr_;
893 // std::vector<osal_mutex_def_t> ep_in_ff_mutex_rd_;
894 std::vector<osal_mutex_def_t> ep_out_ff_mutex_rd_;
895
896 // s_active_ lets getClassDriver() and the static process() trampoline
897 // reach the last-constructed instance without a singleton.
898 inline static USBAudioDeviceBase* s_active_ = nullptr;
899
901 void setConfig(const USBAudioConfig& cfg) { config_ = cfg; }
902
907#ifdef USE_TINYUSB
908 tud_task();
909#endif
910 }
911
914
917
919 void processVolume(uint8_t* data, size_t len) {
920 switch (config_.bits_per_sample) {
921 case 8:
922 processVolume<int8_t>((int8_t*)data, len);
923 break;
924 case 16:
925 processVolume<int16_t>((int16_t*)data, len / 2);
926 break;
927 case 24:
929 break;
930 case 32:
931 processVolume<int32_t>((int32_t*)data, len / 4);
932 break;
933 default:
934 // Unsupported bit depth; do nothing.
935 break;
936 }
937 }
938
941 float getVolumeExt(uint8_t channel) const {
942 if (volume_.empty()) return 1.0f;
943 if (mute_[0]) return 0.0f; // master mute
944 float master = volume_[0];
945 if (channel >= volume_.size()) return master; // no per-channel entry
946 if (mute_[channel]) return 0.0f; // per-channel mute
947 return master * volume_[channel];
948 }
949
950 template <typename T>
951 void processVolume(T* data, size_t sample_count) {
953 for (size_t i = 0; i < sample_count; i++) {
954 uint8_t ch = (uint8_t)(i % ch_count) + 1; // 1-based per-channel index
955 float vol = getVolumeExt(ch);
956 data[i] = (T)(data[i] * vol);
957 }
958 }
959
969 bool rate_updated = rate != config_.sample_rate;
970 config_.sample_rate = rate;
971 LOGW("Sample rate changed to %u Hz", rate);
972 if (rate_updated) {
975 }
976 for (auto& fct : audiod_fct_) fct.sample_rate_tx = rate;
980 }
981
986 virtual bool beginUSB() = 0;
987
993 virtual void resizeBuffers() = 0;
994
997 static constexpr int16_t kVolumeMinDb256 = -25600; // -100 dB in 1/256 dB
998
1001 static int16_t floatToUac2(float vol) {
1002 if (vol <= 0.0f) return (int16_t)0x8000;
1003 if (vol >= 1.0f) return 0;
1004 return (int16_t)((1.0f - vol) * kVolumeMinDb256);
1005 }
1006
1009 static float uac2ToFloat(int16_t v) {
1010 if (v == (int16_t)0x8000) return 0.0f;
1011 if (v >= 0) return 1.0f;
1012 if (v <= kVolumeMinDb256) return 0.0f;
1013 return 1.0f - (float)v / (float)kVolumeMinDb256;
1014 }
1015
1022
1031 uint8_t entityID) {
1032 if (!tud_mounted()) return;
1033 for (uint8_t i = 0; i < (uint8_t)audiod_fct_.size(); i++) {
1034 if (audiod_fct_[i].ep_int == 0) continue;
1035 int_notify_buf_[0] = 0x00; // bInfo: interface, not vendor
1036 int_notify_buf_[1] = AUDIO_CS_REQ_CUR; // bAttribute: CUR changed
1037 int_notify_buf_[2] = channel; // wValue low = CN
1038 int_notify_buf_[3] = ctrlSel; // wValue high = CS
1039 int_notify_buf_[4] = config_.itf_num_ac; // wIndex low = interface
1040 int_notify_buf_[5] = entityID; // wIndex high = entity ID
1042 break;
1043 }
1044 }
1045
1046 bool configChanged(const USBAudioConfig& n) { return config_ != n; }
1047
1048 // Returns the control buffer size for a given function number
1050 return (fn < ctrl_buf_sz_.size()) ? ctrl_buf_sz_[fn] : 64;
1051 }
1052
1053 // Returns the OUT software buffer size for a given function number
1055 return (fn < ep_out_sw_buf_sz_.size()) ? ep_out_sw_buf_sz_[fn] : 0;
1056 }
1057
1058 // Returns the IN software buffer size for a given function number
1060 return (fn < ep_in_sw_buf_sz_.size()) ? ep_in_sw_buf_sz_[fn] : 0;
1061 }
1062
1063 // Returns the descriptor length for a given function number
1065 return (fn < desc_len_.size()) ? desc_len_[fn] : 0;
1066 }
1067
1069
1071
1073 return bps == 16 || bps == 24 || bps == 32;
1074 }
1075
1080
1081 // Max Bytes for one 1 ms isochronous USB packet.
1083
1084 // Total audio FIFO size in bytes.
1087 // // Round up to next power of 2 — some tu_fifo implementations use
1088 // // idx & (depth-1) for index wrapping, which requires power-of-2 depth.
1089 uint16_t p = 256;
1090 while (p < sz) p <<= 1;
1091 return p;
1092 }
1093
1094 // Returns the reset size for audiod_function_t up to and including
1095 // ctrl_buf_sz
1096 static constexpr size_t getResetSize() {
1097 return offsetof(audiod_function_t, ctrl_buf_sz) +
1098 sizeof(((audiod_function_t*)0)->ctrl_buf_sz);
1099 }
1100
1101 // Called by audiod_sof_isr() at the feedback interval.
1102 // Computes the current feedback value then claims the EP and sends it.
1104 uint32_t /*frame_count*/,
1105 uint8_t frame_shift) {
1106 audiod_function_t* audio = &audiod_fct_[func_id];
1107
1108 switch (audio->feedback.compute_method) {
1109 case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
1110 uint32_t ff_count = tu_fifo_count(&audio->ep_out_ff);
1111 // Exponential weighted average keeps the level estimate stable
1112 audio->feedback.compute.fifo_count.fifo_lvl_avg =
1113 audio->feedback.compute.fifo_count.fifo_lvl_avg -
1114 (audio->feedback.compute.fifo_count.fifo_lvl_avg >> 8) +
1115 ((uint32_t)ff_count << 8);
1116 uint32_t avg = audio->feedback.compute.fifo_count.fifo_lvl_avg >> 8;
1117 uint32_t thr = audio->feedback.compute.fifo_count.fifo_lvl_thr;
1118 uint32_t nom = audio->feedback.compute.fifo_count.nom_value;
1119 if (avg > thr) {
1120 audio->feedback.value =
1121 nom + (uint32_t)audio->feedback.compute.fifo_count.rate_const[0] *
1122 (avg - thr);
1123 } else {
1124 uint32_t drop =
1125 (uint32_t)audio->feedback.compute.fifo_count.rate_const[1] *
1126 (thr - avg);
1127 audio->feedback.value =
1128 (nom > drop) ? nom - drop : audio->feedback.min_value;
1129 }
1130 audio->feedback.value =
1131 TU_MIN(TU_MAX(audio->feedback.value, audio->feedback.min_value),
1132 audio->feedback.max_value);
1133 } break;
1134
1135 case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
1136 audio->feedback.value = 1UL << audio->feedback.compute.power_of_2;
1137 break;
1138
1139 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
1140 audio->feedback.value =
1141 (uint32_t)(audio->feedback.compute.float_const *
1142 (float)(1UL << (16u - (frame_shift - 1u))));
1143 break;
1144
1145 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED: {
1147 (TUSB_SPEED_FULL == tud_speed_get()) ? 1000u : 8000u;
1148 audio->feedback.value =
1149 (audio->feedback.compute.fixed.sample_freq << 16) / frame_div;
1150 } break;
1151
1152 default:
1153 break;
1154 }
1155
1156 if (usbd_edpt_claim(audio->rhport, audio->ep_fb)) {
1157 audiod_fb_send(audio);
1158 }
1159 }
1160
1161 // USBD Driver API
1162 void audiod_init(void) {
1163 audiod_fct_.resize(getAudioCount());
1164 alloc_mutex();
1165
1166 // Initialize control buffers
1167 for (uint8_t i = 0; i < getAudioCount(); i++) {
1168 audiod_function_t* audio = &audiod_fct_[i];
1169 // Initialize control buffers
1170 int size = getCtrlBufSz(i);
1171 audio->ctrl_buf.resize(size);
1172 audio->ctrl_buf_sz = size;
1173 // Initialize active alternate interface buffers
1174 audio->alt_setting.resize(descr_builder.audioFunctionsCount());
1175 // Initialize IN EP — lin_buf_in is the DMA staging buffer (one frame).
1176 // Audio data flows through bufferTx() (resized in begin()), not ep_in_ff.
1177 if (isEpInEnabled()) {
1178 // Max packet across all supported rates (192 kHz):
1180 audio->lin_buf_in.resize(max_pkt);
1181 }
1182 // Initialize OUT EP — always set up both FIFO and linear buffer.
1183 // The FIFO is needed by TinyUSB internals even in linear buffer mode.
1184 if (isEpOutEnabled()) {
1185 audio->ep_out_sw_buf.resize(getEpOutSwBufSz(i));
1186 TUSB_FIFO_CONFIG(&audio->ep_out_ff, audio->ep_out_sw_buf.data(),
1187 getEpOutSwBufSz(i), true);
1188 if (isFifoMutexEnabled()) {
1189 tu_fifo_config_mutex(&audio->ep_out_ff, NULL,
1191 }
1193 audio->lin_buf_out.resize(max_pkt);
1194 }
1195 if (isFeedbackEpEnabled()) {
1196 audio->fb_buf.resize(1); // one uint32_t = 4 bytes of feedback data
1197 }
1198 }
1199 }
1200
1202 if (isFifoMutexEnabled()) {
1203 if (isEpOutEnabled()) {
1205 }
1206 }
1207 }
1208
1209 bool audiod_deinit(void) {
1210 return false; // TODO not implemented yet
1211 }
1212
1213 void audiod_reset(uint8_t rhport) {
1214 (void)rhport;
1215 for (uint8_t i = 0; i < getAudioCount(); i++) {
1216 audiod_function_t* audio = &audiod_fct_[i];
1217 memset(audio, 0, getResetSize());
1218 if (isEpInEnabled()) {
1219 tu_fifo_clear(&audio->ep_in_ff);
1220 bufferTx().reset();
1221 }
1222 if (isEpOutEnabled()) {
1223 tu_fifo_clear(&audio->ep_out_ff);
1224 bufferRx().reset();
1225 }
1226 }
1227 }
1228
1230 uint16_t max_len) {
1231 (void)max_len;
1232 TU_VERIFY(TUSB_CLASS_AUDIO == itf_desc->bInterfaceClass &&
1233 AUDIO_SUBCLASS_CONTROL == itf_desc->bInterfaceSubClass);
1234 TU_VERIFY(itf_desc->bInterfaceProtocol == AUDIO_INT_PROTOCOL_CODE_V2);
1235 TU_ASSERT(itf_desc->bNumEndpoints <= 1);
1236 if (itf_desc->bNumEndpoints == 1) {
1238 }
1239 TU_VERIFY(itf_desc->bAlternateSetting == 0);
1240 uint8_t i;
1241 for (i = 0; i < getAudioCount(); i++) {
1242 if (!audiod_fct_[i].p_desc) {
1243 audiod_fct_[i].p_desc = (uint8_t const*)itf_desc;
1244 audiod_fct_[i].rhport = rhport;
1245 audiod_fct_[i].desc_length = getDescLen(i);
1246 // audiod_reset() zeroes ctrl_buf_sz via memset — restore it so
1247 // tud_control_xfer() receives the correct buffer length.
1248 audiod_fct_[i].ctrl_buf_sz = getCtrlBufSz(i);
1250 uint8_t ep_in = 0, ep_out = 0, ep_fb = 0;
1252 tusb_desc_endpoint_t const* desc_ep_out = nullptr;
1253 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1254 uint8_t const* p_desc_end =
1255 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
1256 while (p_desc_end - p_desc > 0) {
1257 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
1259 (tusb_desc_endpoint_t const*)p_desc;
1260 if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS) {
1261 if (isFeedbackEpEnabled() && desc_ep->bmAttributes.usage == 1) {
1262 ep_fb = desc_ep->bEndpointAddress;
1263 }
1264 if (desc_ep->bmAttributes.usage == 0) {
1265 if (isEpInEnabled() &&
1266 tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
1267 ep_in = desc_ep->bEndpointAddress;
1268 ep_in_size =
1270 } else if (isEpOutEnabled() &&
1271 tu_edpt_dir(desc_ep->bEndpointAddress) ==
1272 TUSB_DIR_OUT) {
1273 ep_out = desc_ep->bEndpointAddress;
1274 ep_out_size =
1277 }
1278 }
1279 }
1280 }
1281 p_desc = tu_desc_next(p_desc);
1282 }
1283 if (isEpInEnabled() && ep_in) {
1284 bool alloc_ok = usbd_edpt_iso_alloc(rhport, ep_in, ep_in_size);
1285 LOGD("iso_alloc IN ep=0x%02x sz=%u: %s", ep_in, ep_in_size,
1286 alloc_ok ? "OK" : "FAIL");
1287 }
1288 if (isEpOutEnabled() && ep_out) {
1289 bool alloc_ok = usbd_edpt_iso_alloc(rhport, ep_out, ep_out_size);
1290 LOGD("iso_alloc OUT ep=0x%02x sz=%u: %s", ep_out, ep_out_size,
1291 alloc_ok ? "OK" : "FAIL");
1292#ifdef TUP_DCD_EDPT_ISO_ALLOC
1293 // Pre-activate during enumeration (no isochronous traffic).
1294 // Cannot be done in SET_INTERFACE because iso_activate blocks
1295 // on ESP32's DWC2 when the host is already sending.
1296 // release clears the busy flag so XFER can arm later.
1297 if (desc_ep_out) {
1299 usbd_edpt_release(rhport, ep_out);
1300 LOGD("iso_activate+release OUT: done");
1301 }
1302#endif
1303 }
1304 if (isFeedbackEpEnabled() && ep_fb) {
1305 usbd_edpt_iso_alloc(rhport, ep_fb, 4);
1306 }
1307 }
1308 // Scan for bclock_id_tx (clock entity referenced by the USB-streaming
1309 // terminal) and interval_tx. Runs in TX, RX, and RXTX mode so that
1310 // clock-validity/frequency GET requests always succeed.
1311 // TX/RXTX: Output Terminal type=USB_STREAMING → bCSourceID at [8]
1312 // RX: Input Terminal type=USB_STREAMING → bCSourceID at [7]
1313 // interval_tx is only meaningful for the ISO IN endpoint (TX/RXTX).
1314 if (isEpInEnabled() || isEpOutEnabled()) {
1315 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1316 uint8_t const* p_desc_end =
1317 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
1318 while (p_desc_end - p_desc > 0) {
1319 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
1320 if (isEpInEnabled()) {
1322 (tusb_desc_endpoint_t const*)p_desc;
1323 if (desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS &&
1324 desc_ep->bmAttributes.usage == 0 &&
1325 tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_IN) {
1326 audiod_fct_[i].interval_tx = desc_ep->bInterval;
1327 }
1328 }
1329 } else if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE) {
1330 if (tu_desc_subtype(p_desc) ==
1332 if (tu_unaligned_read16(p_desc + 4) ==
1334 audiod_fct_[i].bclock_id_tx = p_desc[8]; // OT bCSourceID
1335 }
1336 } else if (tu_desc_subtype(p_desc) ==
1338 if (tu_unaligned_read16(p_desc + 4) ==
1340 audiod_fct_[i].bclock_id_tx = p_desc[7]; // IT bCSourceID
1341 }
1342 }
1343 }
1344 p_desc = tu_desc_next(p_desc);
1345 }
1346 }
1347
1348 if (isInterruptEpEnabled()) {
1349 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1350 uint8_t const* p_desc_end =
1351 p_desc + audiod_fct_[i].desc_length - TUD_AUDIO_DESC_IAD_LEN;
1352 while (p_desc_end - p_desc > 0) {
1353 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
1355 (tusb_desc_endpoint_t const*)p_desc;
1356 uint8_t const ep_addr = desc_ep->bEndpointAddress;
1358 desc_ep->bmAttributes.xfer == TUSB_XFER_INTERRUPT) {
1359 if (usbd_edpt_open(audiod_fct_[i].rhport, desc_ep)) {
1360 audiod_fct_[i].ep_int = ep_addr;
1361 } else {
1362 LOGE(" UAC2: interrupt EP 0x%02x open failed", ep_addr);
1363
1364 }
1365 }
1366 }
1367 p_desc = tu_desc_next(p_desc);
1368 }
1369 }
1370 audiod_fct_[i].mounted = true;
1371 break;
1372 }
1373 }
1374 TU_ASSERT(i < getAudioCount());
1376 return drv_len;
1377 }
1378
1380 tusb_control_request_t const* request) {
1381 if (stage == CONTROL_STAGE_SETUP) {
1382 return audiod_control_request(rhport, request);
1383 } else if (stage == CONTROL_STAGE_DATA) {
1384 return audiod_control_complete(rhport, request);
1385 }
1386 return true;
1387 }
1388 // Invoked when class request DATA stage is finished.
1389 // return false to stall control EP (e.g Host send non-sense DATA)
1392 // Handle audio class specific set requests
1393 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS &&
1394 p_request->bmRequestType_bit.direction == TUSB_DIR_OUT) {
1396
1397 switch (p_request->bmRequestType_bit.recipient) {
1399 uint8_t itf = TU_U16_LOW(p_request->wIndex);
1401
1402 if (entityID != 0) {
1403 func_id = 0;
1405 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1406
1407 // ── Clock Source SET_CUR (sample rate) ──────────────
1410 p_request->bRequest == AUDIO_CS_REQ_CUR) {
1412 }
1413
1414 // ── Feature Unit SET_CUR (mute / volume) ────────────
1415 if (isFeatureUnit(entityID) &&
1416 p_request->bRequest == AUDIO_CS_REQ_CUR) {
1417 uint8_t channel = TU_U16_LOW(p_request->wValue);
1418 if (ctrlSel == AUDIO_FU_CTRL_MUTE) {
1419 setMute(cb[0] != 0, channel);
1420 } else if (ctrlSel == AUDIO_FU_CTRL_VOLUME) {
1421 int16_t v;
1422 memcpy(&v, cb, 2);
1423 setVolume(uac2ToFloat(v), channel);
1424 }
1425 }
1426
1427 // Invoke callback
1429 return tud_audio_set_req_entity_cb_(this, rhport, p_request, cb);
1430 }
1431 } else {
1432 // Find index of audio driver structure and verify interface really
1433 // exists
1435
1436 // Invoke callback
1439 this, rhport, p_request,
1440 audiod_fct_[func_id].ctrl_buf.data());
1441 }
1442 }
1443 } break;
1444
1446 uint8_t ep = TU_U16_LOW(p_request->wIndex);
1447
1448 // Check if entity is present and get corresponding driver index
1450
1451 // Invoke callback
1454 this, rhport, p_request, audiod_fct_[func_id].ctrl_buf.data());
1455 }
1456 } break;
1457 // Unknown/Unsupported recipient
1458 default:
1459 TU_BREAKPOINT();
1460 return false;
1461 }
1462 }
1463 return true;
1464 }
1465
1469 (void)result;
1471 for (uint8_t func_id = 0; func_id < getAudioCount(); func_id++) {
1472 audiod_function_t* audio = &audiod_fct_[func_id];
1473 if (isInterruptEpEnabled() && audio->ep_int == ep_addr) {
1474 if (int_done_cb_) int_done_cb_(this, rhport);
1475 return true;
1476 }
1477 if (isEpInEnabled() && audio->ep_in == ep_addr &&
1478 audio->alt_setting.size() != 0) {
1480 if (tx_done_cb_) tx_done_cb_(this, rhport, audio);
1481
1484 : audio->ep_in_sz;
1485 if (frame_bytes > audio->ep_in_sz) frame_bytes = audio->ep_in_sz;
1488
1489 // Drain platform buffer into lin_buf_in, zero-pad, send via DMA.
1490 {
1491 uint8_t* dst = audio->lin_buf_in.data();
1494 if (n < frame_bytes) memset(dst + n, 0, frame_bytes - n);
1495 (void)TUSB_EDPT_XFER(rhport, audio->ep_in, dst, frame_bytes);
1496 }
1497 return true;
1498 }
1499 if (isEpOutEnabled() && audio->ep_out == ep_addr) {
1502 if (isUseLinearBufferRx()) {
1503 // Copy DMA-received data into the platform buffer, re-arm DMA.
1504 if (xferred_bytes > 0)
1505 bufferRx().writeArray(audio->lin_buf_out.data(),
1506 (int)xferred_bytes);
1507 if (rx_done_cb_)
1508 rx_done_cb_(this, rhport, audio, (uint16_t)xferred_bytes);
1509 (void)TUSB_EDPT_XFER(rhport, audio->ep_out, audio->lin_buf_out.data(),
1510 audio->ep_out_sz);
1511 } else {
1512 // FIFO mode: data is already in ep_out_ff from DMA, copy to buffer
1513 if (xferred_bytes > 0) {
1514 uint8_t tmp[768];
1515 uint16_t n =
1516 tu_fifo_read_n(&audio->ep_out_ff, tmp, (uint16_t)xferred_bytes);
1517 if (n > 0) bufferRx().writeArray(tmp, n);
1518 }
1519 if (rx_done_cb_)
1520 rx_done_cb_(this, rhport, audio, (uint16_t)xferred_bytes);
1521 (void)TUSB_EDPT_XFER_FIFO(rhport, audio->ep_out, &audio->ep_out_ff,
1522 audio->ep_out_sz);
1523 }
1524 return true;
1525 }
1526 if (isFeedbackEpEnabled() && audio->ep_fb == ep_addr) {
1527 // SOF ISR owns re-sending; just notify the application.
1528 if (fb_done_cb_) fb_done_cb_(this, func_id);
1529 return true;
1530 }
1531 }
1532 return false;
1533 }
1534
1536 (void)rhport;
1537 (void)frame_count;
1539 for (uint8_t i = 0; i < getAudioCount(); i++) {
1540 audiod_function_t* audio = &audiod_fct_[i];
1541 if (audio->ep_fb != 0) {
1542 uint8_t const hs_adjust =
1543 (TUSB_SPEED_HIGH == tud_speed_get()) ? 3 : 0;
1544 uint32_t const interval =
1545 1UL << (audio->feedback.frame_shift - hs_adjust);
1546 if (0 == (frame_count & (interval - 1))) {
1547 tud_audio_feedback_interval_isr(i, frame_count,
1548 audio->feedback.frame_shift);
1549 }
1550 }
1551 }
1552 }
1553 }
1554
1555 // ── Clock Source GET handler ────────────────────────────────────────────
1558 uint8_t* cb) {
1561 p_request->bRequest == AUDIO_CS_REQ_CUR) {
1562 cb[0] = 1;
1563 return tud_control_xfer(rhport, p_request, cb, 1);
1564 }
1567 if (p_request->bRequest == AUDIO_CS_REQ_CUR) {
1568 memcpy(cb, &rate, 4);
1569 return tud_control_xfer(rhport, p_request, cb, 4);
1570 }
1571 if (p_request->bRequest == AUDIO_CS_REQ_RANGE) {
1573 // List all supported discrete rates
1575 memcpy(cb, &cnt, 2);
1576 for (uint8_t i = 0; i < kNumSupportedSampleRates; i++) {
1578 uint32_t z = 0;
1579 memcpy(cb + 2 + i * 12, &r, 4);
1580 memcpy(cb + 2 + i * 12 + 4, &r, 4);
1581 memcpy(cb + 2 + i * 12 + 8, &z, 4);
1582 }
1583 return tud_control_xfer(rhport, p_request, cb,
1585 } else {
1586 // Single fixed rate from config
1587 uint16_t cnt = 1;
1588 uint32_t z = 0;
1589 memcpy(cb, &cnt, 2);
1590 memcpy(cb + 2, &rate, 4); // dMIN
1591 memcpy(cb + 6, &rate, 4); // dMAX
1592 memcpy(cb + 10, &z, 4); // dRES = 0 (fixed)
1593 return tud_control_xfer(rhport, p_request, cb, 14);
1594 }
1595 }
1596 }
1597 return false;
1598 }
1599
1600 // ── Feature Unit GET handler ──────────────────────────────────────────
1603 uint8_t* cb) {
1605 uint8_t channel = TU_U16_LOW(p_request->wValue);
1606 if (ctrlSel == AUDIO_FU_CTRL_MUTE &&
1607 p_request->bRequest == AUDIO_CS_REQ_CUR) {
1608 cb[0] = isMute(channel) ? 1 : 0;
1609 return tud_control_xfer(rhport, p_request, cb, 1);
1610 }
1612 if (p_request->bRequest == AUDIO_CS_REQ_CUR) {
1613 int16_t v = floatToUac2(volume(channel));
1614 memcpy(cb, &v, 2);
1615 return tud_control_xfer(rhport, p_request, cb, 2);
1616 }
1617 if (p_request->bRequest == AUDIO_CS_REQ_RANGE) {
1618 uint16_t cnt = 1;
1619 int16_t vmin = -25600, vmax = 0, vres = 256;
1620 memcpy(cb + 0, &cnt, 2);
1621 memcpy(cb + 2, &vmin, 2);
1622 memcpy(cb + 4, &vmax, 2);
1623 memcpy(cb + 6, &vres, 2);
1624 return tud_control_xfer(rhport, p_request, cb, 8);
1625 }
1626 }
1627 return false;
1628 }
1629
1630 // ── Entity request handler (Clock Source + Feature Unit) ──────────────
1633 uint8_t entityID) {
1634 uint8_t func_id = 0;
1635 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1636 bool is_get = (p_request->bmRequestType_bit.direction == TUSB_DIR_IN);
1637
1639 if (is_get && handleClockSourceGet(rhport, p_request, cb)) return true;
1640 // SET — schedule data receive for audiod_control_complete()
1641 return tud_control_xfer(rhport, p_request, cb,
1642 audiod_fct_[func_id].ctrl_buf_sz);
1643 }
1644
1645 if (isFeatureUnit(entityID)) {
1646 if (is_get && handleFeatureUnitGet(rhport, p_request, cb)) return true;
1647 // SET — schedule data receive
1648 return tud_control_xfer(rhport, p_request, cb,
1649 audiod_fct_[func_id].ctrl_buf_sz);
1650 }
1651
1652 // Unknown entity — try generic verify
1653 uint8_t itf = TU_U16_LOW(p_request->wIndex);
1656 return true;
1657 }
1658 if (is_get && req_entity_cb_) return req_entity_cb_(this, func_id);
1659 return tud_control_xfer(rhport, p_request,
1660 audiod_fct_[func_id].ctrl_buf.data(),
1661 audiod_fct_[func_id].ctrl_buf_sz);
1662 }
1663
1664 // ── Interface request handler (entityID == 0) ─────────────────────────
1667 uint8_t itf = TU_U16_LOW(p_request->wIndex);
1670 if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
1671 if (get_req_itf_cb_) return get_req_itf_cb_(this, rhport, p_request);
1672 return false;
1673 }
1674 return tud_control_xfer(rhport, p_request,
1675 audiod_fct_[func_id].ctrl_buf.data(),
1676 audiod_fct_[func_id].ctrl_buf_sz);
1677 }
1678
1679 // ── Endpoint request handler ──────────────────────────────────────────
1682 uint8_t ep = TU_U16_LOW(p_request->wIndex);
1685 if (p_request->bmRequestType_bit.direction == TUSB_DIR_IN) {
1686 if (get_req_ep_cb_) return get_req_ep_cb_(this, rhport, p_request);
1687 return false;
1688 }
1689 return tud_control_xfer(rhport, p_request,
1690 audiod_fct_[func_id].ctrl_buf.data(),
1691 audiod_fct_[func_id].ctrl_buf_sz);
1692 }
1693
1694 // ── Main control request dispatcher ───────────────────────────────────
1697 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_STANDARD) {
1698 switch (p_request->bRequest) {
1700 return audiod_get_interface(rhport, p_request);
1702 return audiod_set_interface(rhport, p_request);
1704 return true;
1705 default:
1706 return false;
1707 }
1708 }
1709
1710 if (p_request->bmRequestType_bit.type == TUSB_REQ_TYPE_CLASS) {
1711 switch (p_request->bmRequestType_bit.recipient) {
1714 return (entityID != 0)
1717 }
1719 return handleEndpointRequest(rhport, p_request);
1720 default:
1721 return false;
1722 }
1723 }
1724
1725 return false;
1726 }
1727
1728 // Verify an entity with the given ID exists and returns also the
1729 // corresponding driver index
1731 uint8_t* func_id) {
1732 uint8_t i;
1733 for (i = 0; i < getAudioCount(); i++) {
1734 // Look for the correct driver by checking if the unique standard AC
1735 // interface number fits
1736 if (audiod_fct_[i].p_desc &&
1737 ((tusb_desc_interface_t const*)audiod_fct_[i].p_desc)
1738 ->bInterfaceNumber == itf) {
1739 // Get pointers after class specific AC descriptors and end of AC
1740 // descriptors - entities are defined in between
1741 uint8_t const* p_desc =
1742 tu_desc_next(audiod_fct_[i].p_desc); // Points to CS AC descriptor
1743 uint8_t const* p_desc_end =
1744 ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength +
1745 p_desc;
1746 p_desc = tu_desc_next(p_desc); // Get past CS AC descriptor
1747
1748 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1749 // strict-overflow warning
1750 while (p_desc_end - p_desc > 0) {
1751 if (p_desc[3] == entityID) // Entity IDs are always at offset 3
1752 {
1753 *func_id = i;
1754 return true;
1755 }
1756 p_desc = tu_desc_next(p_desc);
1757 }
1758 }
1759 }
1760 return false;
1761 }
1762
1764 uint8_t i;
1765 for (i = 0; i < getAudioCount(); i++) {
1766 if (audiod_fct_[i].p_desc) {
1767 // Get pointer at end
1768 uint8_t const* p_desc_end =
1769 audiod_fct_[i].p_desc + audiod_fct_[i].desc_length;
1770
1771 // Advance past AC descriptors - EP we look for are streaming EPs
1772 uint8_t const* p_desc = tu_desc_next(audiod_fct_[i].p_desc);
1773 p_desc += ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength;
1774
1775 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1776 // strict-overflow warning
1777 while (p_desc_end - p_desc > 0) {
1778 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT &&
1779 ((tusb_desc_endpoint_t const*)p_desc)->bEndpointAddress == ep) {
1780 *func_id = i;
1781 return true;
1782 }
1783 p_desc = tu_desc_next(p_desc);
1784 }
1785 }
1786 }
1787 return false;
1788 }
1789
1791 uint8_t i;
1792 for (i = 0; i < getAudioCount(); i++) {
1793 if (audiod_fct_[i].p_desc) {
1794 // Get pointer at beginning and end
1795 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1796 uint8_t const* p_desc_end = audiod_fct_[i].p_desc +
1797 audiod_fct_[i].desc_length -
1799 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1800 // strict-overflow warning
1801 while (p_desc_end - p_desc > 0) {
1802 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
1803 ((tusb_desc_interface_t const*)audiod_fct_[i].p_desc)
1804 ->bInterfaceNumber == itf) {
1805 *func_id = i;
1806 return true;
1807 }
1808 p_desc = tu_desc_next(p_desc);
1809 }
1810 }
1811 }
1812 return false;
1813 }
1814
1815 void audiod_parse_flow_control_params(audiod_function_t* audio,
1816 uint8_t const* p_desc) {
1817 // Seed the TX sample rate from the configured AudioInfo so packet-size
1818 // calculation works even when the host never issues a SET_CUR(SAM_FREQ)
1819 // request (typical for single-frequency clock sources). The host may still
1820 // override this later via audiod_control_complete().
1821 if (audio->sample_rate_tx == 0)
1822 audio->sample_rate_tx = (uint32_t)config_.sample_rate;
1823
1824 p_desc = tu_desc_next(p_desc); // Exclude standard AS interface descriptor
1825 // of current alternate interface descriptor
1826
1827 // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format
1828 // type and format and also to get number of physical channels
1829 if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
1831 audio->n_channels_tx =
1832 ((audio_desc_cs_as_interface_t const*)p_desc)->bNrChannels;
1833 audio->format_type_tx =
1834 (audio_format_type_t)(((audio_desc_cs_as_interface_t const*)p_desc)
1835 ->bFormatType);
1836 // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
1837 p_desc = tu_desc_next(p_desc);
1838 if (tu_desc_type(p_desc) == TUSB_DESC_CS_INTERFACE &&
1840 ((audio_desc_type_I_format_t const*)p_desc)->bFormatType ==
1841 AUDIO_FORMAT_TYPE_I) {
1842 audio->n_bytes_per_sample_tx =
1843 ((audio_desc_type_I_format_t const*)p_desc)->bSubslotSize;
1844 }
1845 }
1846
1847 // Fallback from config if descriptor parsing missed any field.
1848 // The descriptor struct layout may differ across TinyUSB versions.
1849 if (audio->n_channels_tx == 0) audio->n_channels_tx = config_.channels;
1850 if (audio->n_bytes_per_sample_tx == 0)
1851 audio->n_bytes_per_sample_tx = config_.bits_per_sample / 8;
1852 if (audio->format_type_tx == 0) audio->format_type_tx = AUDIO_FORMAT_TYPE_I;
1853 }
1854
1855 // This helper function finds for a given audio function and AS interface
1856 // number the index of the attached driver structure, the index of the
1857 // interface in the audio function
1858 // (e.g. the std. AS interface with interface number 15 is the first AS
1859 // interface for the given audio function and thus gets index zero), and
1860 // finally a pointer to the std. AS interface, where the pointer always points
1861 // to the first alternate setting i.e. alternate interface zero.
1862 bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t* audio,
1863 uint8_t* idxItf,
1864 uint8_t const** pp_desc_int) {
1865 if (audio->p_desc) {
1866 // Get pointer at end
1867 uint8_t const* p_desc_end =
1868 audio->p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
1869
1870 // Advance past AC descriptors
1871 uint8_t const* p_desc = tu_desc_next(audio->p_desc);
1872 p_desc += ((audio_desc_cs_ac_interface_t const*)p_desc)->wTotalLength;
1873
1874 uint8_t tmp = 0;
1875 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1876 // strict-overflow warning
1877 while (p_desc_end - p_desc > 0) {
1878 // We assume the number of alternate settings is increasing thus we
1879 // return the index of alternate setting zero!
1880 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
1881 ((tusb_desc_interface_t const*)p_desc)->bAlternateSetting == 0) {
1882 if (((tusb_desc_interface_t const*)p_desc)->bInterfaceNumber == itf) {
1883 *idxItf = tmp;
1884 *pp_desc_int = p_desc;
1885 return true;
1886 }
1887 // Increase index, bytes read, and pointer
1888 tmp++;
1889 }
1890 p_desc = tu_desc_next(p_desc);
1891 }
1892 }
1893 return false;
1894 }
1895
1896 // This helper function finds for a given AS interface number the index of the
1897 // attached driver structure, the index of the interface in the audio function
1898 // (e.g. the std. AS interface with interface number 15 is the first AS
1899 // interface for the given audio function and thus gets index zero), and
1900 // finally a pointer to the std. AS interface, where the pointer always points
1901 // to the first alternate setting i.e. alternate interface zero.
1903 uint8_t* idxItf,
1904 uint8_t const** pp_desc_int) {
1905 // Loop over audio driver interfaces
1906 uint8_t i;
1907 for (i = 0; i < getAudioCount(); i++) {
1909 pp_desc_int)) {
1910 *func_id = i;
1911 return true;
1912 }
1913 }
1914
1915 return false;
1916 }
1917
1920 uint8_t const itf = tu_u16_low(p_request->wIndex);
1921
1922 // Find index of audio streaming interface
1924 uint8_t const* dummy;
1925
1926 TU_VERIFY(
1929 &audiod_fct_[func_id].alt_setting[idxItf], 1));
1930
1931 LOGI(" Get itf: %u - current alt: %u", itf,
1932 audiod_fct_[func_id].alt_setting[idxItf]);
1933
1934 return true;
1935 }
1936
1937 bool audiod_fb_send(audiod_function_t* audio) {
1939 audio->feedback.format_correction;
1940 // Format the feedback value
1941 if (apply_correction) {
1942 uint8_t* fb = (uint8_t*)audio->fb_buf.data();
1943
1944 // For FS format is 10.14
1945 *(fb++) = (audio->feedback.value >> 2) & 0xFF;
1946 *(fb++) = (audio->feedback.value >> 10) & 0xFF;
1947 *(fb++) = (audio->feedback.value >> 18) & 0xFF;
1948 *fb = 0;
1949 } else {
1950 audio->fb_buf[0] = audio->feedback.value;
1951 }
1952
1953 // About feedback format on FS
1954 //
1955 // 3 variables: Format | packetSize | sendSize | Working OS:
1956 // 16.16 4 4 Linux, Windows
1957 // 16.16 4 3 Linux
1958 // 16.16 3 4 Linux
1959 // 16.16 3 3 Linux
1960 // 10.14 4 4 Linux
1961 // 10.14 4 3 Linux
1962 // 10.14 3 4 Linux, OSX
1963 // 10.14 3 3 Linux, OSX
1964 //
1965 // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty
1966 // ugly
1967 return TUSB_EDPT_XFER(audio->rhport, audio->ep_fb,
1968 (uint8_t*)audio->fb_buf.data(),
1969 apply_correction ? 3 : 4);
1970 }
1971
1972 // ── Close existing EPs for this interface ──────────────────────────────
1973 void closeEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1975 if (!isEpInEnabled() || audio->ep_in_as_intf_num != itf) return;
1976 audio->ep_in_as_intf_num = 0;
1977#ifndef TUP_DCD_EDPT_ISO_ALLOC
1978 usbd_edpt_close(rhport, audio->ep_in);
1979#endif
1980 tu_fifo_clear(&audio->ep_in_ff);
1981 bufferTx().reset();
1984 audio->ep_in = 0;
1986 audio->packet_sz_tx[0] = 0;
1987 audio->packet_sz_tx[1] = 0;
1988 audio->packet_sz_tx[2] = 0;
1989 }
1991 }
1992
1993 void closeEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1995 if (!isEpOutEnabled() || audio->ep_out_as_intf_num != itf) return;
1996 audio->ep_out_as_intf_num = 0;
1997#ifndef TUP_DCD_EDPT_ISO_ALLOC
1998 usbd_edpt_close(rhport, audio->ep_out);
1999#endif
2000 tu_fifo_clear(&audio->ep_out_ff);
2003 audio->ep_out = 0;
2004 if (isFeedbackEpEnabled()) {
2005 audio->ep_fb = 0;
2006 tu_memclr(&audio->feedback, sizeof(audio->feedback));
2007 }
2009 }
2010
2011 // ── Activate a single endpoint found in the descriptor ────────────────
2014#ifdef TUP_DCD_EDPT_ISO_ALLOC
2015 // Skip iso_activate for isochronous OUT — on ESP32's DWC2 it blocks
2016 // for the entire playback duration. The endpoint DPRAM was already
2017 // allocated by iso_alloc in audiod_open(). The XFER call in
2018 // openEpOut will configure the DCD to receive.
2019 if (dir == TUSB_DIR_OUT &&
2020 desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS)
2021 return true;
2022 return usbd_edpt_iso_activate(rhport, desc_ep);
2023#else
2024 (void)dir;
2025 return usbd_edpt_open(rhport, desc_ep);
2026#endif
2027 }
2028
2029 // ── Open the IN (TX) data endpoint ────────────────────────────────────
2030 void openEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
2032 uint8_t const* p_desc_for_params) {
2033 audio->ep_in = desc_ep->bEndpointAddress;
2034 audio->ep_in_as_intf_num = itf;
2035 audio->ep_in_sz = tu_edpt_packet_size(desc_ep);
2036 if (audio->ep_in_sz == 0) return;
2037
2040
2041 // Arm initial transfer (silence — copier fills the buffer).
2043 if (first_pkt > audio->ep_in_sz) first_pkt = audio->ep_in_sz;
2044 audio->lin_buf_in.assign(audio->ep_in_sz, 0);
2045 tx_xfer_armed_ = TUSB_EDPT_XFER(rhport, audio->ep_in,
2046 audio->lin_buf_in.data(), first_pkt);
2048 }
2049
2050 // ── Open the OUT (RX) data endpoint ───────────────────────────────────
2051 void openEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
2053 LOGD("openEpOut: ep=0x%02x sz=%u", desc_ep->bEndpointAddress,
2055 audio->ep_out = desc_ep->bEndpointAddress;
2056 audio->ep_out_as_intf_num = itf;
2057 audio->ep_out_sz = tu_edpt_packet_size(desc_ep);
2058 if (audio->ep_out_sz == 0) return;
2059
2060 // iso_activate was done in audiod_open() (no traffic, instant).
2061 // Just arm the XFER here.
2062 if (isUseLinearBufferRx()) {
2063 if (audio->lin_buf_out.size() < audio->ep_out_sz)
2064 audio->lin_buf_out.assign(audio->ep_out_sz, 0);
2065 bool xfer_ok = TUSB_EDPT_XFER(rhport, audio->ep_out,
2066 audio->lin_buf_out.data(),
2067 audio->ep_out_sz);
2068 LOGD(" XFER armed: %s, buf=%p sz=%u", xfer_ok ? "OK" : "FAIL",
2069 audio->lin_buf_out.data(), audio->ep_out_sz);
2070 } else {
2071 bool xfer_ok = TUSB_EDPT_XFER_FIFO(rhport, audio->ep_out,
2072 &audio->ep_out_ff,
2073 audio->ep_out_sz);
2074 LOGD(" XFER_FIFO armed: %s", xfer_ok ? "OK" : "FAIL");
2075 }
2077 }
2078
2079 // ── Open the explicit feedback endpoint ───────────────────────────────
2080 void openEpFeedback(audiod_function_t* audio,
2082 audio->ep_fb = desc_ep->bEndpointAddress;
2083 audio->feedback.frame_shift = desc_ep->bInterval - 1;
2084 }
2085
2086 // ── Configure feedback computation parameters ─────────────────────────
2087 void setupFeedback(audiod_function_t* audio, uint8_t func_id, uint8_t alt) {
2088 if (!isFeedbackEpEnabled() || audio->ep_fb == 0) return;
2089
2090 audio_feedback_params_t fb_param = {};
2091 fb_param.method = AUDIO_FEEDBACK_METHOD_FIFO_COUNT;
2092 fb_param.sample_freq = config_.sample_rate;
2095 audio->feedback.compute_method = fb_param.method;
2096
2097 if (TUSB_SPEED_FULL == tud_speed_get() &&
2099 audio->feedback.format_correction =
2101
2102 uint32_t const frame_div =
2103 (TUSB_SPEED_FULL == tud_speed_get()) ? 1000 : 8000;
2104 audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
2105 audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
2106
2107 switch (fb_param.method) {
2108 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED:
2109 case AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT:
2110 case AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2:
2111 audiod_set_fb_params_freq(audio, fb_param.sample_freq,
2112 fb_param.frequency.mclk_freq);
2113 break;
2114 case AUDIO_FEEDBACK_METHOD_FIFO_COUNT: {
2115 // Use bufferRx() size — ep_out_ff may be uninitialized in linear buffer mode
2117 if (fifo_depth == 0) fifo_depth = 1; // guard against div-by-zero
2118 uint16_t fifo_lvl_thr = fifo_depth / 2;
2119 audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
2120 audio->feedback.compute.fifo_count.fifo_lvl_avg =
2121 ((uint32_t)fifo_lvl_thr) << 16;
2123 ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
2124 audio->feedback.compute.fifo_count.nom_value = nominal;
2125 audio->feedback.compute.fifo_count.rate_const[0] =
2126 (uint16_t)((audio->feedback.max_value - nominal) / fifo_lvl_thr);
2127 audio->feedback.compute.fifo_count.rate_const[1] =
2128 (uint16_t)((nominal - audio->feedback.min_value) / fifo_lvl_thr);
2129 if (tud_speed_get() == TUSB_SPEED_HIGH) {
2130 audio->feedback.compute.fifo_count.rate_const[0] /= 8;
2131 audio->feedback.compute.fifo_count.rate_const[1] /= 8;
2132 }
2133 } break;
2134 default:
2135 break;
2136 }
2137 }
2138
2139 // ── Scan descriptor for endpoints and open them ───────────────────────
2140 bool openEndpointsForAltSetting(uint8_t rhport, audiod_function_t* audio,
2142 uint8_t const* p_desc = audio->p_desc;
2143 uint8_t const* p_desc_end =
2144 p_desc + audio->desc_length - TUD_AUDIO_DESC_IAD_LEN;
2145 LOGD(" openEPs: p_desc=%p end=%p len=%u itf=%u alt=%u",
2146 p_desc, p_desc_end, audio->desc_length, itf, alt);
2147
2148 while (p_desc_end - p_desc > 0) {
2149 if (tu_desc_type(p_desc) == TUSB_DESC_INTERFACE &&
2150 ((tusb_desc_interface_t const*)p_desc)->bInterfaceNumber == itf &&
2151 ((tusb_desc_interface_t const*)p_desc)->bAlternateSetting == alt) {
2152 uint8_t const* p_desc_for_params =
2153 (isEpInEnabled() && isEpInFlowControlEnabled()) ? p_desc : nullptr;
2154 uint8_t foundEPs = 0;
2155 uint8_t nEps = ((tusb_desc_interface_t const*)p_desc)->bNumEndpoints;
2156 LOGD(" matched itf=%u alt=%u nEps=%u", itf, alt, nEps);
2157
2158 while (foundEPs < nEps && (p_desc_end - p_desc > 0)) {
2159 LOGD(" scan: type=0x%02x len=%u offset=%d",
2160 p_desc[1], p_desc[0], (int)(p_desc - audio->p_desc));
2161 if (tu_desc_type(p_desc) == TUSB_DESC_ENDPOINT) {
2163 (tusb_desc_endpoint_t const*)p_desc;
2164
2165 LOGD(" activating ep=0x%02x type=%u...",
2166 desc_ep->bEndpointAddress, desc_ep->bmAttributes.xfer);
2167 if (!activateEndpoint(rhport, desc_ep,
2168 tu_edpt_dir(desc_ep->bEndpointAddress))) {
2169 LOGD(" activateEndpoint FAILED");
2170 p_desc = tu_desc_next(p_desc);
2171 continue;
2172 }
2173 LOGD(" activated OK");
2174 // Skip clear_stall for isochronous OUT (iso_activate was also
2175 // skipped). For other endpoints, clear the stall as usual.
2176 if (!(tu_edpt_dir(desc_ep->bEndpointAddress) == TUSB_DIR_OUT &&
2177 desc_ep->bmAttributes.xfer == TUSB_XFER_ISOCHRONOUS))
2178 usbd_edpt_clear_stall(rhport, desc_ep->bEndpointAddress);
2179
2180 uint8_t ep_addr = desc_ep->bEndpointAddress;
2182 desc_ep->bmAttributes.usage == 0x00)
2183 openEpIn(rhport, audio, itf, desc_ep, p_desc_for_params);
2184
2185 if (isEpOutEnabled()) {
2187 openEpOut(rhport, audio, itf, desc_ep);
2188 if (isFeedbackEpEnabled() &&
2190 desc_ep->bmAttributes.usage == 1)
2191 openEpFeedback(audio, desc_ep);
2192 }
2193 foundEPs += 1;
2194 }
2195 p_desc = tu_desc_next(p_desc);
2196 }
2197
2198 if (foundEPs != nEps) return true; // ZLP already sent
2199
2200 if (tud_audio_set_itf_cb_) tud_audio_set_itf_cb_(this, rhport, nullptr);
2201
2202 setupFeedback(audio, func_id, alt);
2203 return true;
2204 }
2205 p_desc = tu_desc_next(p_desc);
2206 }
2207 return true;
2208 }
2209
2210 // ── Main SET_INTERFACE handler ────────────────────────────────────────
2213 uint8_t const itf = tu_u16_low(p_request->wIndex);
2214 uint8_t const alt = tu_u16_low(p_request->wValue);
2215 LOGD("SET_ITF itf=%u alt=%u [start]", itf, alt);
2216
2218 uint8_t const* p_desc;
2220 &p_desc)) {
2221 LOGD(" AS interface %u not found", itf);
2223 return true;
2224 }
2225 LOGD(" found func=%u idx=%u", func_id, idxItf);
2226
2227 audiod_function_t* audio = &audiod_fct_[func_id];
2228
2229 // 1. Close existing EPs
2230 LOGD(" close EPs");
2231 closeEpIn(rhport, audio, itf, p_request);
2232 closeEpOut(rhport, audio, itf, p_request);
2233
2234 // 2. Save alt setting and acknowledge
2235 audio->alt_setting[idxItf] = alt;
2236
2238 openEndpointsForAltSetting(rhport, audio, func_id, itf, alt);
2239
2240 // 4. Update SOF and flow control
2241 if (isFeedbackEpEnabled()) {
2242 bool enable_sof = false;
2243 for (uint8_t i = 0; i < getAudioCount(); i++) {
2244 if (audiod_fct_[i].ep_fb != 0) {
2245 enable_sof = true;
2246 break;
2247 }
2248 }
2250 }
2253
2254 return true;
2255 }
2256
2257 bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq,
2258 uint32_t mclk_freq) {
2259 // Check if frame interval is within sane limits
2260 // The interval value n_frames was taken from the descriptors within
2261
2262 // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s
2263 // / f_m) for high speed this lower limit ensures the measures feedback
2264 // value has sufficient precision
2265 uint32_t const k = (TUSB_SPEED_FULL == tud_speed_get()) ? 10 : 13;
2266 uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
2267
2268 if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
2269 LOGE(" UAC2 feedback interval too small");
2270 TU_BREAKPOINT();
2271 return false;
2272 }
2273
2274 // Check if parameters really allow for a power of two division
2275 if ((mclk_freq % sample_freq) == 0 &&
2276 tu_is_power_of_two(mclk_freq / sample_freq)) {
2277 audio->feedback.compute_method =
2278 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2;
2279 audio->feedback.compute.power_of_2 =
2280 (uint8_t)(16 - (audio->feedback.frame_shift - 1) -
2281 tu_log2(mclk_freq / sample_freq));
2282 } else if (audio->feedback.compute_method ==
2283 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT) {
2284 audio->feedback.compute.float_const =
2285 (float)sample_freq / (float)mclk_freq *
2286 (1UL << (16 - (audio->feedback.frame_shift - 1)));
2287 } else {
2288 audio->feedback.compute.fixed.sample_freq = sample_freq;
2289 audio->feedback.compute.fixed.mclk_freq = mclk_freq;
2290 }
2291
2292 return true;
2293 }
2294
2295 bool audiod_calc_tx_packet_sz(audiod_function_t* audio) {
2296 TU_VERIFY(audio->format_type_tx == AUDIO_FORMAT_TYPE_I);
2297 TU_VERIFY(audio->n_channels_tx);
2298 TU_VERIFY(audio->n_bytes_per_sample_tx);
2299 TU_VERIFY(audio->interval_tx);
2300 TU_VERIFY(audio->sample_rate_tx);
2301
2302 // Restart the fractional accumulator for this streaming session.
2303 audio->tx_sample_acc = 0;
2304
2306 ? audio->interval_tx
2307 : 1 << (audio->interval_tx - 1);
2308
2310 (uint16_t)(audio->sample_rate_tx * interval /
2311 ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
2313 (uint16_t)(audio->sample_rate_tx * interval %
2314 ((tud_speed_get() == TUSB_SPEED_FULL) ? 1000 : 8000));
2315
2317 (uint16_t)((sample_normimal - 1) * audio->n_channels_tx *
2318 audio->n_bytes_per_sample_tx);
2320 (uint16_t)(sample_normimal * audio->n_channels_tx *
2321 audio->n_bytes_per_sample_tx);
2323 (uint16_t)((sample_normimal + 1) * audio->n_channels_tx *
2324 audio->n_bytes_per_sample_tx);
2325
2326 // Endpoint size must larger than packet size
2328
2329 // Frmt20.pdf 2.3.1.1 USB Packets
2330 if (sample_reminder) {
2331 // All virtual frame packets must either contain INT(nav) audio slots
2332 // (small VFP) or INT(nav)+1 (large VFP) audio slots
2333 audio->packet_sz_tx[0] = packet_sz_tx_norm;
2334 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2335 audio->packet_sz_tx[2] = packet_sz_tx_max;
2336 } else {
2337 // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small
2338 // VFP), INT(nav) (medium VFP) and INT(nav)+1 (large VFP).
2339 audio->packet_sz_tx[0] = packet_sz_tx_min;
2340 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2341 audio->packet_sz_tx[2] = packet_sz_tx_max;
2342 }
2343
2344 return true;
2345 }
2346
2347 // Number of audio bytes to transmit in the current (micro)frame when IN
2348 // flow control is enabled. A fractional accumulator distributes the
2349 // sub-frame sample remainder over successive frames so the long-term average
2350 // matches the configured sample rate (e.g. alternating 176/180 bytes for
2351 // 44100 Hz stereo 16-bit, averaging 176.4 bytes = 44.1 samples per frame).
2352 uint16_t audiod_tx_packet_size_fc(audiod_function_t* audio) {
2353 if (audio->sample_rate_tx == 0 || audio->n_channels_tx == 0 ||
2354 audio->n_bytes_per_sample_tx == 0) {
2355 // Not enough info to size precisely: fall back to the max packet.
2356 return audio->ep_in_sz;
2357 }
2358 const uint32_t denom = (tud_speed_get() == TUSB_SPEED_FULL) ? 1000u : 8000u;
2359 const uint8_t iv = audio->interval_tx ? audio->interval_tx : 1;
2360 const uint32_t interval =
2361 (tud_speed_get() == TUSB_SPEED_FULL) ? iv : (1u << (iv - 1));
2362
2363 audio->tx_sample_acc += audio->sample_rate_tx * interval;
2364 const uint32_t samples = audio->tx_sample_acc / denom;
2365 audio->tx_sample_acc -= samples * denom;
2366
2367 uint32_t bytes =
2368 samples * audio->n_channels_tx * audio->n_bytes_per_sample_tx;
2369 if (bytes > audio->ep_in_sz) bytes = audio->ep_in_sz;
2370 return (uint16_t)bytes;
2371 }
2372
2375 // Always write to the FIFO. In linear-buffer mode the xfer_cb drains the
2376 // FIFO into lin_buf_in after DMA has finished, so there is no race.
2377 return tu_fifo_write_n(&audiod_fct_[func_id].ep_in_ff, data, len);
2378 }
2379
2384
2387 return tu_fifo_read_n(&audiod_fct_[func_id].ep_out_ff, buffer, bufsize);
2388 }
2389};
2390
2391} // namespace audio_tools
2392
2393// Custom driver registration — routes to whichever USBAudioDeviceBase subclass
2394// was constructed (base or derived; set via s_active_ in the constructor).
2395extern "C" usbd_class_driver_t const* usbd_app_driver_get_cb(uint8_t* count) {
2397 count);
2398}
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGD(...)
Definition AudioLoggerIDF.h:27
#define LOGE(...)
Definition AudioLoggerIDF.h:30
#define AUDIO_CS_CTRL_CLK_VALID
Definition USBAudioDeviceBase.h:74
#define TUSB_EDPT_XFER(rp, ep, buf, sz)
Definition USBAudioDeviceBase.h:64
#define TUSB_FIFO_CONFIG(f, buf, d, ov)
Definition USBAudioDeviceBase.h:66
#define AUDIO_FU_CTRL_VOLUME
Definition USBAudioDeviceBase.h:86
#define AUDIO_FU_CTRL_MUTE
Definition USBAudioDeviceBase.h:83
#define AUDIO_CS_REQ_RANGE
Definition USBAudioDeviceBase.h:77
#define USB_DESCR_MAX_LEN
Definition USBAudioDeviceBase.h:37
usbd_class_driver_t const * usbd_app_driver_get_cb(uint8_t *count)
Definition USBAudioDeviceBase.h:2395
#define TUSB_EDPT_XFER_FIFO(rp, ep, ff, sz)
Definition USBAudioDeviceBase.h:65
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:178
Base class for all Audio Streams. It support the boolean operator to test if the object is ready with...
Definition BaseStream.h:123
AudioInfo info
Definition BaseStream.h:174
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:131
Shared functionality of all buffers.
Definition Buffers.h:22
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:33
virtual void reset()=0
clears the buffer
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:55
virtual size_t size()=0
virtual int availableForWrite()=0
provides the number of entries that are available to write
virtual int available()=0
provides the number of entries that are available to read
USB Audio Class 2.0 descriptor generator.
Definition USBAudio2DescriptorBuilder.h:30
const uint16_t buildFullDescriptor(uint8_t *desc)
Definition USBAudio2DescriptorBuilder.h:54
static constexpr uint8_t ENTITY_FU2
second Feature Unit (RXTX)
Definition USBAudio2DescriptorBuilder.h:40
static constexpr uint8_t ENTITY_FU1
first Feature Unit
Definition USBAudio2DescriptorBuilder.h:37
int audioFunctionsCount() const
Definition USBAudio2DescriptorBuilder.h:145
static constexpr uint8_t ENTITY_CLOCK
Definition USBAudio2DescriptorBuilder.h:35
uint16_t calcPacketSizeForRate(uint32_t rate) const
Definition USBAudio2DescriptorBuilder.h:161
uint16_t calcMaxPacketSize() const
Definition USBAudio2DescriptorBuilder.h:167
bool enableFeedbackEp() const
Definition USBAudio2DescriptorBuilder.h:154
USB Audio Device class for audio streaming over USB.
Definition USBAudioDeviceBase.h:111
std::function< void(float, uint8_t)> volume_cb_
Definition USBAudioDeviceBase.h:830
std::function< void(USBAudioDeviceBase *, uint8_t func_id, uint8_t alt_itf, audio_feedback_params_t *feedback_param)> tud_audio_feedback_params_cb_
Definition USBAudioDeviceBase.h:876
bool isUseLinearBufferRx() const
Definition USBAudioDeviceBase.h:1068
bool begin(const USBAudioConfig &cfg)
Apply a config and start the USB audio device.
Definition USBAudioDeviceBase.h:300
uint16_t audiod_open(uint8_t rhport, tusb_desc_interface_t const *itf_desc, uint16_t max_len)
Definition USBAudioDeviceBase.h:1229
void setRxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for RX done events.
Definition USBAudioDeviceBase.h:553
bool setVolume(float vol, uint8_t channel)
Set the volume for a channel and notify the host.
Definition USBAudioDeviceBase.h:411
void openEpFeedback(audiod_function_t *audio, tusb_desc_endpoint_t const *desc_ep)
Definition USBAudioDeviceBase.h:2080
volatile uint32_t xfer_cb_rx_count_
Definition USBAudioDeviceBase.h:822
volatile uint32_t xfer_cb_tx_count_
Definition USBAudioDeviceBase.h:820
USBAudioDeviceBase(USBAudioConfig cfg)
Constructor which provides configuration at construction time.
Definition USBAudioDeviceBase.h:227
static constexpr int16_t kVolumeMinDb256
Convert AudioTools volume (0.0–1.0) to UAC2 int16 (1/256 dB). 0.0 maps to 0x8000 (silence),...
Definition USBAudioDeviceBase.h:997
std::vector< float > volume_
Definition USBAudioDeviceBase.h:828
bool isUseLinearBufferTx() const
Definition USBAudioDeviceBase.h:1070
bool isEpInEnabled() const
Returns true if the IN endpoint is enabled.
Definition USBAudioDeviceBase.h:375
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *p_request)> tud_audio_set_itf_close_EP_cb_
Definition USBAudioDeviceBase.h:872
bool audiod_fb_send(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:1937
void setVolumeCallback(std::function< void(float, uint8_t)> cb)
Register a callback invoked when the host (or device) changes the volume.
Definition USBAudioDeviceBase.h:444
void setTxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *)> cb)
Register a callback for TX done events.
Definition USBAudioDeviceBase.h:543
bool isMute(uint8_t channel=0) const
Returns the current mute state for the given channel.
Definition USBAudioDeviceBase.h:423
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, tusb_control_request_t const *request)
Definition USBAudioDeviceBase.h:1379
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for entity requests.
Definition USBAudioDeviceBase.h:563
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff)> tud_audio_set_req_entity_cb_
Definition USBAudioDeviceBase.h:860
bool isFeatureUnit(uint8_t id) const
Returns true if the given entity ID is a Feature Unit (FU1 or FU2).
Definition USBAudioDeviceBase.h:1018
void setAudioFeedbackFormatCorrectionCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for audio feedback format correction events.
Definition USBAudioDeviceBase.h:637
static int16_t floatToUac2(float vol)
Convert linear volume (0.0–1.0) to UAC2 int16 (1/256 dB). Linear mapping: 0.0 → -100 dB (min),...
Definition USBAudioDeviceBase.h:1001
uint32_t getTxSampleRate() const
TX sample rate parsed from the descriptor (must be non-zero for flow control).
Definition USBAudioDeviceBase.h:801
uint16_t getEpInSwBufSz(uint8_t fn) const
Definition USBAudioDeviceBase.h:1059
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *)> tx_done_cb_
Definition USBAudioDeviceBase.h:839
std::function< void(USBAudioDeviceBase *, uint8_t func_id)> fb_done_cb_
Definition USBAudioDeviceBase.h:853
void serviceTinyUSB()
Process pending USB events on platforms where the application drives the stack (RP2040)....
Definition USBAudioDeviceBase.h:906
void setGetReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for GET requests on the interface.
Definition USBAudioDeviceBase.h:506
float volume(uint8_t channel)
Returns the current volume for the given channel.
Definition USBAudioDeviceBase.h:403
bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t *func_id, uint8_t *idxItf, uint8_t const **pp_desc_int)
Definition USBAudioDeviceBase.h:1902
static USBAudioDeviceBase * s_active_
Definition USBAudioDeviceBase.h:898
bool isStreamingActiveTx() const
Returns true if the host has opened the IN (capture) stream.
Definition USBAudioDeviceBase.h:476
uint16_t getDescLen(uint8_t fn) const
Definition USBAudioDeviceBase.h:1064
virtual BaseBuffer< uint8_t > & bufferTx()=0
Returns the TX audio buffer. Must be overridden by subclasses.
USBAudioConfig config_
Definition USBAudioDeviceBase.h:834
void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc)
Definition USBAudioDeviceBase.h:1815
void setIntDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for interrupt done events.
Definition USBAudioDeviceBase.h:534
void setConfig(const USBAudioConfig &cfg)
Set the USB audio configuration (use begin(cfg) instead).
Definition USBAudioDeviceBase.h:901
volatile uint32_t tx_fifo_read_total_
Definition USBAudioDeviceBase.h:821
uint32_t getRxXferCount() const
Number of times audiod_xfer_cb fired for the OUT endpoint.
Definition USBAudioDeviceBase.h:788
bool is_started_
Definition USBAudioDeviceBase.h:818
void audiod_init(void)
Definition USBAudioDeviceBase.h:1162
void setReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:594
volatile uint32_t rx_total_bytes_
Definition USBAudioDeviceBase.h:823
void setItfCloseEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for interface close endpoint events.
Definition USBAudioDeviceBase.h:616
bool tx_xfer_armed_
Definition USBAudioDeviceBase.h:819
void setTudAudioSetItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:572
volatile uint16_t tx_frame_bytes_last_
Definition USBAudioDeviceBase.h:824
uint32_t getRxTotalBytes() const
Total bytes received from host via OUT endpoint.
Definition USBAudioDeviceBase.h:790
bool begin()
(Re-)start the USB audio device with the current config.
Definition USBAudioDeviceBase.h:317
void closeEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1993
bool isStreamingActiveRx() const
Returns true if the host has opened the OUT (playback) stream.
Definition USBAudioDeviceBase.h:484
bool setVolume(float volume) override
sets the volume for the master channel (channel 0)
Definition USBAudioDeviceBase.h:398
TU_ATTR_FAST_FUNC void audiod_sof_isr(uint8_t rhport, uint32_t frame_count)
Definition USBAudioDeviceBase.h:1535
uint8_t getAudioCount() const
Returns the number of audio functions (always 1).
Definition USBAudioDeviceBase.h:497
int available() override
Bytes of received audio waiting in the RX buffer.
Definition USBAudioDeviceBase.h:685
bool audiod_deinit(void)
Definition USBAudioDeviceBase.h:1209
bool is_active_
Definition USBAudioDeviceBase.h:826
uint16_t tud_audio_n_read(uint8_t func_id, void *buffer, uint16_t bufsize)
Definition USBAudioDeviceBase.h:2385
std::function< void(bool, bool)> streaming_state_cb_
Definition USBAudioDeviceBase.h:833
void sendInterruptNotification(uint8_t ctrlSel, uint8_t channel, uint8_t entityID)
Send a UAC2 status/change notification via the AC interrupt EP.
Definition USBAudioDeviceBase.h:1030
float volume() override
gets the volume for the master channel (channel 0)
Definition USBAudioDeviceBase.h:395
bool audiod_get_interface(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1918
static bool isValidBitsPerSample(uint8_t bps)
Definition USBAudioDeviceBase.h:1072
bool audiod_calc_tx_packet_sz(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2295
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> req_entity_cb_
Definition USBAudioDeviceBase.h:854
void setReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for endpoint set requests.
Definition USBAudioDeviceBase.h:605
void setMuteCallback(std::function< void(bool, uint8_t)> cb)
Register a callback invoked when the host (or device) changes the mute state.
Definition USBAudioDeviceBase.h:451
virtual BaseBuffer< uint8_t > & bufferRx()=0
Returns the RX audio buffer. Must be overridden by subclasses.
void setFbDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for feedback done events.
Definition USBAudioDeviceBase.h:526
std::function< void(uint32_t)> sample_rate_cb_
Definition USBAudioDeviceBase.h:832
USBAudioDeviceBase()
Default Constructor.
Definition USBAudioDeviceBase.h:224
void alloc_mutex()
Definition USBAudioDeviceBase.h:1201
int availableForWrite() override
Bytes of free space in the TX buffer.
Definition USBAudioDeviceBase.h:691
bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1730
bool isFifoMutexEnabled() const
Returns true if FIFO mutex is enabled.
Definition USBAudioDeviceBase.h:494
void processVolume(uint8_t *data, size_t len)
Process audio data for volume control.
Definition USBAudioDeviceBase.h:919
bool mounted() const
Returns true if the device is mounted by the USB host.
Definition USBAudioDeviceBase.h:500
virtual bool beginUSB()=0
Override in platform subclasses to register descriptors and start the USB host-controller stack (e....
bool handleFeatureUnitGet(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1601
uint16_t getDescriptor(uint8_t *desc)
Returns the audio-function descriptor block for use in tud_descriptor_configuration_cb().
Definition USBAudioDeviceBase.h:727
static constexpr size_t getResetSize()
Definition USBAudioDeviceBase.h:1096
void setSampleRate(uint32_t rate)
Device-initiated sample rate change.
Definition USBAudioDeviceBase.h:968
void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t, uint8_t frame_shift)
Definition USBAudioDeviceBase.h:1103
uint16_t tud_audio_n_write(uint8_t func_id, const void *data, uint16_t len)
Definition USBAudioDeviceBase.h:2373
std::function< void(bool, uint8_t)> mute_cb_
Definition USBAudioDeviceBase.h:831
static float uac2ToFloat(int16_t v)
Convert UAC2 int16 (1/256 dB) to linear volume (0.0–1.0). Linear mapping within the -100....
Definition USBAudioDeviceBase.h:1009
void audiod_reset(uint8_t rhport)
Definition USBAudioDeviceBase.h:1213
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> tud_audio_feedback_format_correction_cb_
Definition USBAudioDeviceBase.h:879
usbd_class_driver_t const * getClassDriver(uint8_t *count)
Get the USB device class driver for TinyUSB integration.
Definition USBAudioDeviceBase.h:747
bool openEndpointsForAltSetting(uint8_t rhport, audiod_function_t *audio, uint8_t func_id, uint8_t itf, uint8_t alt)
Definition USBAudioDeviceBase.h:2140
bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, xfer_result_t result, uint32_t xferred_bytes)
TODO refactor control request handling to separate function and reduce nesting.
Definition USBAudioDeviceBase.h:1467
bool isInterruptEpEnabled() const
Returns true if the interrupt endpoint is enabled.
Definition USBAudioDeviceBase.h:491
uint32_t getTxFifoReadTotal() const
Definition USBAudioDeviceBase.h:793
uint8_t numInterfaces() const
Total number of USB interfaces claimed by the audio function (1 AC + 1 or 2 AS), for use in the bNumI...
Definition USBAudioDeviceBase.h:737
std::vector< uint16_t > desc_len_
Definition USBAudioDeviceBase.h:887
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *p_request)> tud_audio_set_itf_cb_
Definition USBAudioDeviceBase.h:857
virtual void resizeBuffers()=0
Resize the platform audio buffers. Both platforms use NBuffer-style block pools: block size = max USB...
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *, uint16_t xferred_bytes)> rx_done_cb_
Definition USBAudioDeviceBase.h:842
bool isEpInFlowControlEnabled() const
Returns true if IN endpoint flow control is enabled. When on, the per-frame isochronous packet size i...
Definition USBAudioDeviceBase.h:388
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff)> tud_audio_set_req_itf_cb_
Definition USBAudioDeviceBase.h:864
void end()
Stop audio streaming and clear FIFOs. Does not disconnect USB.
Definition USBAudioDeviceBase.h:700
uint16_t fifoSize() const
Definition USBAudioDeviceBase.h:1085
void openEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, tusb_desc_endpoint_t const *desc_ep, uint8_t const *p_desc_for_params)
Definition USBAudioDeviceBase.h:2030
USBAudioConfig active_config_
Definition USBAudioDeviceBase.h:835
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *)> get_req_itf_cb_
Definition USBAudioDeviceBase.h:846
bool handleEntityRequest(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t entityID)
Definition USBAudioDeviceBase.h:1631
bool isTxXferArmed() const
True if the initial isochronous IN transfer was armed successfully.
Definition USBAudioDeviceBase.h:783
void setSampleRateCallback(std::function< void(uint32_t)> cb)
Register a callback invoked when the host (or device) changes the sample rate.
Definition USBAudioDeviceBase.h:458
uint32_t getTxXferCount() const
Number of times audiod_xfer_cb fired for the IN endpoint.
Definition USBAudioDeviceBase.h:786
USBAudioConfig defaultConfig(RxTxMode mode=RXTX_MODE)
Returns a default configuration pre-filled for the requested direction (RX_MODE, TX_MODE,...
Definition USBAudioDeviceBase.h:236
bool audiod_set_interface(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:2211
void setupFeedback(audiod_function_t *audio, uint8_t func_id, uint8_t alt)
Definition USBAudioDeviceBase.h:2087
std::vector< osal_mutex_def_t > ep_out_ff_mutex_rd_
Definition USBAudioDeviceBase.h:894
void openEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, tusb_desc_endpoint_t const *desc_ep)
Definition USBAudioDeviceBase.h:2051
static USBAudioDeviceBase & activeInstance()
Returns the most-recently-constructed instance (base or subclass).
Definition USBAudioDeviceBase.h:372
bool audiod_get_AS_interface_index(uint8_t itf, audiod_function_t *audio, uint8_t *idxItf, uint8_t const **pp_desc_int)
Definition USBAudioDeviceBase.h:1862
uint16_t getEpOutSwBufSz(uint8_t fn) const
Definition USBAudioDeviceBase.h:1054
uint8_t getTxInterval() const
TX isochronous interval (bInterval) parsed from the descriptor.
Definition USBAudioDeviceBase.h:813
bool handleClockSourceGet(uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1556
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *)> get_req_ep_cb_
Definition USBAudioDeviceBase.h:850
uint16_t audiod_tx_packet_size_fc(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2352
bool audiod_control_complete(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1390
void setAudioInfo(AudioInfo info) override
Change the sample rate and notify the host.
Definition USBAudioDeviceBase.h:258
bool audiod_control_request(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1695
std::vector< uint16_t > ep_in_sw_buf_sz_
Definition USBAudioDeviceBase.h:885
volatile uint32_t tx_xferred_last_
Definition USBAudioDeviceBase.h:825
std::vector< uint16_t > ctrl_buf_sz_
Definition USBAudioDeviceBase.h:889
std::vector< uint16_t > ep_out_sw_buf_sz_
Definition USBAudioDeviceBase.h:882
uint16_t getTxFrameBytesLast() const
Definition USBAudioDeviceBase.h:796
std::function< void(USBAudioDeviceBase *, uint8_t rhport)> int_done_cb_
Definition USBAudioDeviceBase.h:837
uint16_t packetSize() const
Definition USBAudioDeviceBase.h:1082
bool isFeedbackEpEnabled() const
Returns true if the feedback endpoint is enabled. Only meaningful in pure RX (OUT-only) mode: with an...
Definition USBAudioDeviceBase.h:383
bool handleEndpointRequest(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1680
float getVolumeExt(uint8_t channel) const
Returns the effective volume for a per-channel index (1-based). Combines master volume (index 0) with...
Definition USBAudioDeviceBase.h:941
uint16_t tud_audio_n_available(uint8_t func_id)
Definition USBAudioDeviceBase.h:2380
void setGetReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *)> cb)
Register a callback for GET requests on an endpoint.
Definition USBAudioDeviceBase.h:516
std::vector< bool > mute_
Definition USBAudioDeviceBase.h:829
uint8_t int_notify_buf_[6]
Definition USBAudioDeviceBase.h:880
bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1790
bool isStreamingActive() const
Returns true if either IN or OUT streaming endpoint is open.
Definition USBAudioDeviceBase.h:471
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, tusb_control_request_t const *, uint8_t *)> cb)
Register a callback for entity set requests.
Definition USBAudioDeviceBase.h:583
void setAudioFeedbackParamsCallback(std::function< void(USBAudioDeviceBase *, uint8_t, uint8_t, audio_feedback_params_t *)> cb)
Register a callback for audio feedback parameter events.
Definition USBAudioDeviceBase.h:626
void setStreamingStateCallback(std::function< void(bool, bool)> cb)
Register a callback invoked when the streaming state changes. Fires when the host opens or closes a s...
Definition USBAudioDeviceBase.h:466
bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq)
Definition USBAudioDeviceBase.h:2257
bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1763
void notifyStreamingState()
Definition USBAudioDeviceBase.h:1076
std::vector< audiod_function_t > audiod_fct_
Definition USBAudioDeviceBase.h:891
bool activateEndpoint(uint8_t rhport, tusb_desc_endpoint_t const *desc_ep, uint8_t dir=TUSB_DIR_IN)
Definition USBAudioDeviceBase.h:2012
uint16_t getCtrlBufSz(uint8_t fn) const
Definition USBAudioDeviceBase.h:1049
uint32_t getTxXferredLast() const
Definition USBAudioDeviceBase.h:799
uint8_t getTxBytesPerSample() const
TX bytes per sample parsed from the descriptor.
Definition USBAudioDeviceBase.h:809
bool handleInterfaceRequest(uint8_t rhport, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1665
bool isEpOutEnabled() const
Returns true if the OUT endpoint is enabled.
Definition USBAudioDeviceBase.h:378
bool configChanged(const USBAudioConfig &n)
Definition USBAudioDeviceBase.h:1046
size_t readBytes(uint8_t *buffer, size_t bufsize)
Receive audio data from the host (host → device, speaker/playback).
Definition USBAudioDeviceBase.h:673
void processVolume(T *data, size_t sample_count)
Definition USBAudioDeviceBase.h:951
void closeEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, tusb_control_request_t const *p_request)
Definition USBAudioDeviceBase.h:1973
bool setMute(bool m, uint8_t channel=0)
Set the mute state for a channel and notify the host.
Definition USBAudioDeviceBase.h:431
uint8_t getTxChannels() const
TX channel count parsed from the descriptor.
Definition USBAudioDeviceBase.h:805
uint16_t audioPacketSize() const
One isochronous USB packet size in bytes (same formula as the descriptor builder).
Definition USBAudioDeviceBase.h:714
size_t write(const uint8_t *data, size_t len)
Send audio data to the host (device → host, microphone/capture). Silently discards data when the host...
Definition USBAudioDeviceBase.h:645
USBAudio2DescriptorBuilder descr_builder
Definition USBAudioDeviceBase.h:836
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, tusb_control_request_t const *p_request, uint8_t *pBuff)> tud_audio_set_req_ep_cb_
Definition USBAudioDeviceBase.h:868
Supports the setting and getting of the volume.
Definition AudioTypes.h:191
24bit integer which is used for I2S sound processing. The values are really using 3 bytes....
Definition Int24_3bytes_t.h:16
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:30
@ RXTX_MODE
Definition AudioTypes.h:30
@ TX_MODE
Definition AudioTypes.h:30
@ RX_MODE
Definition AudioTypes.h:30
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
static constexpr uint8_t kNumSupportedSampleRates
Definition USBAudioDeviceBase.h:94
static constexpr uint32_t kSupportedSampleRates[]
Definition USBAudioDeviceBase.h:92
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:512
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:55
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:57
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:59
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:61
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:27
bool enable_interrupt_ep
Definition USBAudioConfig.h:77
bool use_linear_buffer_tx
Definition USBAudioConfig.h:91
bool enable_multi_sample_rate
Definition USBAudioConfig.h:72
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:31
uint8_t itf_num_ac
Definition USBAudioConfig.h:46
uint8_t fifo_packets
Definition USBAudioConfig.h:51
bool enable_ep_in_flow_control
Definition USBAudioConfig.h:84
bool volume_active
Definition USBAudioConfig.h:96
bool use_linear_buffer_rx
Definition USBAudioConfig.h:88
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:32