arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioDeviceBase.h
Go to the documentation of this file.
1#pragma once
2
3// Some cores (e.g. Arduino's Renesas UNO R4 core, Arduino.h) #define abs(x)
4// as a function-like macro for AVR-era C compatibility. Since Arduino.h is
5// always included ahead of library headers in a .ino, that macro is already
6// active here and breaks libstdc++'s own templated abs(duration<...>)
7// overload inside <chrono> (pulled in by <mutex>) -- the preprocessor sees
8// the comma inside duration<_Rep, _Period> as a second macro argument.
9// Suppress the macro for just this header's STL includes, then restore it
10// so nothing else in the translation unit is affected.
11#pragma push_macro("abs")
12#undef abs
13
14#include <cmath>
15#include <cstddef>
16#include <cstdint>
17#include <cstring>
18#include <functional>
19#include <mutex>
20#include <vector>
21
22#pragma pop_macro("abs")
23
26
27#ifdef ESP32
28#ifndef ARDUINO_USB_MODE
29#error This Microcontroller has no Native USB interface
30#else
31#if ARDUINO_USB_MODE == 1
32#error This sketch should be used when USB is in OTG mode
33#endif
34#endif
35#else
36#if !defined(USE_TINYUSB) && !defined(ARDUINO_ARCH_STM32) && \
37 !defined(ARDUINO_ARCH_RENESAS) && !defined(IS_ZEPHYR)
38#error This Microcontroller has no Native USB interface
39#endif
40#endif
41
42#include "AudioLogger.h"
48
49#define USB_DESCR_MAX_LEN 512
50
51namespace audio_tools {
52
53
69public:
80
94
102 uint8_t rhport;
103 uint8_t const* p_desc; // Pointer to Standard AC Interface Descriptor
104 // ep_in/ep_out are written from the USB interrupt handler (openEpIn/
105 // openEpOut/closeEpIn/closeEpOut, reached via audiod_set_interface() in
106 // ISR context) and read from normal application code (write()/
107 // readBytes()/isStreamingActiveTx()/isStreamingActiveRx(), called from
108 // loop()). Without a memory-visibility guarantee across that boundary, a
109 // compiler is free to cache a stale read indefinitely -- observed in
110 // practice on the arm-none-eabi-gcc 7.2.1 toolchain used by the Arduino
111 // Renesas UNO R4 core: isStreamingActiveTx() kept reporting false while
112 // real ISO-IN transfers were actively completing, so write() silently
113 // discarded all audio data (see the "disregard data" branch below) while
114 // still reporting success.
115 volatile uint8_t ep_in; // TX audio data EP.
116 uint16_t ep_in_sz; // Current size of TX EP
117 uint8_t
118 ep_in_as_intf_num; // Standard AS Interface Descriptor number for IN
119 volatile uint8_t ep_out; // RX audio data EP.
120 uint16_t ep_out_sz; // Current size of RX EP
121 uint8_t
122 ep_out_as_intf_num; // Standard AS Interface Descriptor number for OUT
123 uint8_t ep_fb; // Feedback EP.
124 uint8_t ep_int; // Audio control interrupt EP.
125 bool mounted; // Device opened
126 uint16_t desc_length; // Length of audio function descriptor
127 struct {
128 uint32_t value;
129 uint32_t min_value;
130 uint32_t max_value;
131 uint8_t frame_shift;
134 union {
135 uint8_t power_of_2;
137 struct {
138 uint32_t sample_freq;
139 uint32_t mclk_freq;
141 struct {
142 uint32_t nom_value;
143 uint32_t fifo_lvl_avg;
144 uint16_t fifo_lvl_thr;
145 uint16_t rate_const[2];
150 uint16_t packet_sz_tx[3];
152 uint8_t interval_tx;
156 // Fractional sample accumulator for IN-endpoint flow control. Carries the
157 // sub-frame remainder (e.g. the 0.1 sample/frame of 44100 Hz) so the
158 // long-term average packet size matches the real sample rate.
160 // Cached OUT endpoint descriptor (populated by audiod_open()'s descriptor
161 // scan) so closeEpOut() can re-activate the endpoint -- resetting its
162 // busy/claimed state for the next SET_INTERFACE -- without needing to
163 // re-walk the descriptor. See closeEpOut() for why this is needed.
165 // From this point, data is not cleared by bus reset
166 uint8_t ctrl_buf_sz;
167 std::vector<uint8_t> ctrl_buf;
168 std::vector<uint8_t> alt_setting;
169 std::vector<uint8_t> lin_buf_out;
170 std::vector<uint8_t> lin_buf_in;
171 std::vector<uint32_t> fb_buf;
172 };
173
181 uint8_t method;
182 uint32_t sample_freq; // sample frequency in Hz
183
184 union {
185 struct {
186 uint32_t mclk_freq; // Main clock frequency in Hz i.e. master clock to
187 // which sample clock is based on
189 };
190 };
191
194
199
206 USBAudioConfig cfg;
207 switch (mode) {
208 case RX_MODE:
209 cfg.enable_ep_out = true;
210 cfg.enable_ep_in = false;
211 break;
212 case TX_MODE:
213 cfg.enable_ep_out = false;
214 cfg.enable_ep_in = true;
215 break;
216 case RXTX_MODE:
217 cfg.enable_ep_out = true;
218 cfg.enable_ep_in = true;
219 break;
220 default:
221 break;
222 }
223 return cfg;
224 }
225
227 void setAudioInfo(AudioInfo info) override {
229 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
231 return;
232 }
233 // full flexibility when not started yet
234 if (!is_started_) {
238 return;
239 }
240
241 // when started only sample rate can be changed on the fly
243 if (config_.channels != info.channels) {
244 LOGE(
245 "Could not change channel count from %d to %d: channel count is "
246 "fixed at startup!",
248 }
250 LOGE(
251 "Could not change bits per sample from %d to %d: bits per sample is "
252 "fixed at startup!",
254 }
255 // notifiy subscribed entities
257 // notify host about sample rate change via control request callback;
259 }
260
269 bool begin(const USBAudioConfig& cfg) {
270 if (!is_started_) {
271 config_ = cfg;
272 } else if (!configChanged(cfg)) {
273 return true; // already running with same config
274 } else {
276 }
277 return begin();
278 }
279
286 bool begin() {
287 if (!is_started_) {
289 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
291 return false;
292 }
293
295 LOGI("USB on core: %d", core);
296
297 // Resize platform buffers — virtual so each platform uses the right API.
299
300 const int n = getAudioCount();
301 // GET_RANGE response is 2 + count*12 bytes; 64 is the floor needed for
302 // everything else sharing this buffer (Feature Unit GET_RANGE, etc.).
303 uint16_t cb_sz = 64u;
305 uint16_t needed =
306 (uint16_t)(2 + config_.supported_sample_rates.size() * 12);
307 if (needed > cb_sz) cb_sz = needed;
308 }
309 ctrl_buf_sz_.assign(n, cb_sz);
310
311 uint8_t desc[USB_DESCR_MAX_LEN];
312 uint16_t desc_len = descr_builder.buildFullDescriptor(desc);
313 desc_len_.assign(n, desc_len);
314
315 // master (index 0) + one per channel
317
318
319 audiod_init();
320
321 if (!beginUSB()) {
322 LOGE("beginUSB failed");
323 return false;
324 }
325 is_started_ = true;
326 rx_primed_ = false; // re-arm the one-shot startup prime for this session
327 }
328
329 // Push current state to the host. sendInterruptNotification()
330 // is a no-op when not yet mounted, so this is harmless on first boot.
332 for (uint8_t ch = 0; ch < (uint8_t)volume_.size(); ch++) {
333 setVolume(volume(ch), ch);
334 setMute(isMute(ch), ch);
335 }
336 is_active_ = true;
337 return true;
338 }
346
347
352
359
360
361
363 bool isStreamingActiveTx() const override {
364 for (const auto& fct : audiod_fct_) {
365 if (fct.ep_in != 0) return true;
366 }
367 return false;
368 }
369
371 bool isStreamingActiveRx() const override {
372 for (const auto& fct : audiod_fct_) {
373 if (fct.ep_out != 0) return true;
374 }
375 return false;
376 }
379
381 uint8_t getAudioCount() const { return 1; }
382
384 bool mounted() override { return backend().mounted(); }
385
390 void setGetReqItfCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
391 UsbSetupPacket const&)>
392 cb) {
393 get_req_itf_cb_ = cb;
394 }
395
400 void setGetReqEpCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
401 UsbSetupPacket const&)>
402 cb) {
403 get_req_ep_cb_ = cb;
404 }
405
410 void setFbDoneCallback(std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
411 fb_done_cb_ = cb;
412 }
413
419 std::function<void(USBAudioDeviceBase*, uint8_t)> cb) {
420 int_done_cb_ = cb;
421 }
422
428 std::function<bool(USBAudioDeviceBase*, uint8_t, audiod_function_t*, uint16_t)>
429 cb) {
430 tx_done_cb_ = cb;
431 }
432
437 void setRxDoneCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
438 audiod_function_t*, uint16_t)>
439 cb) {
440 rx_done_cb_ = cb;
441 }
442
448 std::function<bool(USBAudioDeviceBase*, uint8_t)> cb) {
449 req_entity_cb_ = cb;
450 }
451
457 std::function<bool(USBAudioDeviceBase*, uint8_t,
458 UsbSetupPacket const&)>
459 cb) {
461 }
462
468 std::function<bool(USBAudioDeviceBase*, uint8_t,
469 UsbSetupPacket const&, uint8_t*)>
470 cb) {
472 }
473
479 std::function<bool(USBAudioDeviceBase*, uint8_t,
480 UsbSetupPacket const&, uint8_t*)>
481 cb) {
483 }
484
490 std::function<bool(USBAudioDeviceBase*, uint8_t,
491 UsbSetupPacket const&, uint8_t*)>
492 cb) {
494 }
495
500 void setItfCloseEpCallback(std::function<bool(USBAudioDeviceBase*, uint8_t,
501 UsbSetupPacket const&)>
502 cb) {
504 }
505
511 std::function<void(USBAudioDeviceBase*, uint8_t, uint8_t,
513 cb) {
515 }
516
525
541 void setFeedbackPercent(int percent) {
542 int normalized = percent;
543 if (normalized < 0) normalized = 0;
544 if (normalized > 100) normalized = 100;
545 manual_feedback_percent_ = normalized;
546 }
547
551
552
554 void end() {
555 for (auto& audio : audiod_fct_) {
556 std::fill(audio.lin_buf_in.begin(), audio.lin_buf_in.end(), 0);
557 std::fill(audio.lin_buf_out.begin(), audio.lin_buf_out.end(), 0);
558 }
559 bufferTx().reset();
560 bufferRx().reset();
561 is_started_ = false;
562 }
563
566 uint16_t audioPacketSize() const { return packetSize(); }
567
579 uint16_t getDescriptor(uint8_t* desc) {
580 active_config_ = config_; // save active config
582 }
583
589 uint8_t numInterfaces() const {
590 return (uint8_t)(1 + (config_.enable_ep_out ? 1 : 0) +
591 (config_.enable_ep_in ? 1 : 0));
592 }
593
595 bool isTxXferArmed() const { return tx_xfer_armed_; }
596
598 uint32_t getTxXferCount() const { return xfer_cb_tx_count_; }
600 uint32_t getRxXferCount() const { return xfer_cb_rx_count_; }
602 uint32_t getRxTotalBytes() const { return rx_total_bytes_; }
606 uint32_t getRxDroppedBytes() const { return rx_dropped_bytes_; }
609 uint32_t getTxFifoReadTotal() const { return tx_fifo_read_total_; }
610
613 uint16_t getTxFrameBytesLast() const { return tx_frame_bytes_last_; }
616 uint32_t getTxXferredLast() const { return tx_xferred_last_; }
618 uint32_t getTxSampleRate() const {
619 return audiod_fct_.empty() ? 0 : audiod_fct_[0].sample_rate_tx;
620 }
622 uint8_t getTxChannels() const {
623 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_channels_tx;
624 }
626 uint8_t getTxBytesPerSample() const {
627 return audiod_fct_.empty() ? 0 : audiod_fct_[0].n_bytes_per_sample_tx;
628 }
630 uint8_t getTxInterval() const {
631 return audiod_fct_.empty() ? 0 : audiod_fct_[0].interval_tx;
632 }
633
634 virtual int getActualCore() const { return -1;}
635
636 protected:
637 bool tx_xfer_armed_ = false;
638 volatile uint32_t xfer_cb_tx_count_ = 0;
639 volatile uint32_t tx_fifo_read_total_ = 0;
640 volatile uint32_t xfer_cb_rx_count_ = 0;
641 volatile uint32_t rx_total_bytes_ = 0;
642 volatile uint32_t rx_dropped_bytes_ = 0;
643 volatile uint16_t tx_frame_bytes_last_ = 0;
644 volatile uint32_t tx_xferred_last_ = 0;
647 std::function<void(USBAudioDeviceBase*, uint8_t rhport)> int_done_cb_;
648 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*, uint16_t bytes)>
650 std::function<bool(USBAudioDeviceBase*, uint8_t rhport, audiod_function_t*,
651 uint16_t xferred_bytes)>
653 // Callback for interface GET requests
654 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
655 UsbSetupPacket const&)>
657 // Callback for endpoint GET requests
658 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
659 UsbSetupPacket const&)>
661
662 // Callback for feedback done event
663 std::function<void(USBAudioDeviceBase*, uint8_t func_id)> fb_done_cb_;
664 std::function<bool(USBAudioDeviceBase*, uint8_t func_id)> req_entity_cb_;
665 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
666 UsbSetupPacket const& p_request)>
668 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
669 UsbSetupPacket const& p_request, uint8_t* pBuff)>
671
672 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
673 UsbSetupPacket const& p_request, uint8_t* pBuff)>
675
676 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
677 UsbSetupPacket const& p_request, uint8_t* pBuff)>
679
680 std::function<bool(USBAudioDeviceBase*, uint8_t rhport,
681 UsbSetupPacket const& p_request)>
683
684 std::function<void(USBAudioDeviceBase*, uint8_t func_id, uint8_t alt_itf,
685 audio_feedback_params_t* feedback_param)>
687
688 std::function<bool(USBAudioDeviceBase*, uint8_t func_id)>
690 uint8_t int_notify_buf_[6] = {};
691
692 // Manual feedback override: -1 means "automatic" (use the negotiated
693 // compute_method, e.g. AUDIO_FEEDBACK_METHOD_FIFO_COUNT); 0..100 means
694 // the caller is driving the feedback value directly via
695 // setFeedbackPercent(). See tud_audio_feedback_interval_isr().
697
698 // calculate!
699 std::vector<uint16_t> desc_len_;
700 // 64
701 std::vector<uint16_t> ctrl_buf_sz_;
702
703 std::vector<audiod_function_t> audiod_fct_;
704
705 // s_active_ lets the class-driver trampolines and the static process()
706 // trampoline reach the last-constructed instance without a singleton.
707 inline static USBAudioDeviceBase* s_active_ = nullptr;
708
709
724 void serviceUSB() override = 0;
725
729 virtual USBAudioBackend& backend() = 0;
730
731
740 void setSampleRate(uint32_t rate) {
741 bool rate_updated = rate != config_.sample_rate;
742 config_.sample_rate = rate;
743 LOGW("Sample rate changed to %u Hz", rate);
744 if (rate_updated) {
747 }
748 for (auto& fct : audiod_fct_) fct.sample_rate_tx = rate;
752 }
753
759
765
768 bool isFeatureUnit(uint8_t id) const {
771 }
772
780 void sendInterruptNotification(uint8_t ctrlSel, uint8_t channel,
781 uint8_t entityID) {
782 if (!backend().mounted()) return;
783 for (uint8_t i = 0; i < (uint8_t)audiod_fct_.size(); i++) {
784 if (audiod_fct_[i].ep_int == 0) continue;
785 int_notify_buf_[0] = 0x00; // bInfo: interface, not vendor
786 int_notify_buf_[1] = kAudioCsReqCur; // bAttribute: CUR changed
787 int_notify_buf_[2] = channel; // wValue low = CN
788 int_notify_buf_[3] = ctrlSel; // wValue high = CS
789 int_notify_buf_[4] = config_.itf_num_ac; // wIndex low = interface
790 int_notify_buf_[5] = entityID; // wIndex high = entity ID
791 (void)backend().transfer(0, audiod_fct_[i].ep_int, int_notify_buf_, 6);
792 break;
793 }
794 }
795
796 // Returns the control buffer size for a given function number
797 uint16_t getCtrlBufSz(uint8_t fn) const {
798 return (fn < ctrl_buf_sz_.size()) ? ctrl_buf_sz_[fn] : 64;
799 }
800
801 // Returns the descriptor length for a given function number
802 uint16_t getDescLen(uint8_t fn) const {
803 return (fn < desc_len_.size()) ? desc_len_[fn] : 0;
804 }
805
806 // Max Bytes for one 1 ms isochronous USB packet.
807 uint16_t packetSize() const { return descr_builder.calcMaxPacketSize(); }
808
809 // Returns the reset size for audiod_function_t up to and including
810 // ctrl_buf_sz
811 static constexpr size_t getResetSize() {
812 return offsetof(audiod_function_t, ctrl_buf_sz) +
813 sizeof(((audiod_function_t*)0)->ctrl_buf_sz);
814 }
815
816 // Called by audiod_sof_isr() at the feedback interval.
817 // Computes the current feedback value then claims the EP and sends it.
819 uint32_t /*frame_count*/,
820 uint8_t frame_shift) {
821 audiod_function_t* audio = &audiod_fct_[func_id];
822
823 if (manual_feedback_percent_ >= 0) {
824 // setFeedbackPercent() override: scale linearly across the negotiated
825 // [min_value, max_value] range instead of running the automatic
826 // compute_method below.
827 uint32_t range = audio->feedback.max_value - audio->feedback.min_value;
828 audio->feedback.value =
829 audio->feedback.min_value +
830 (range * (uint32_t)manual_feedback_percent_) / 100;
831 } else {
832 switch (audio->feedback.compute_method) {
834 // In linear-buffer mode, audio data flows lin_buf_out → bufferRx(),
835 // completely bypassing ep_out_ff. Reading ep_out_ff would always
836 // return 0, driving the feedback to min_value and causing the host
837 // to gradually reduce its send rate until the buffer drains
838 // (audible periodic drop every 5-10 s). Use the platform RX ring
839 // buffer level.
840 uint32_t ff_count = (uint32_t)bufferRx().available();
841 // Exponential weighted average keeps the level estimate stable
845 ((uint32_t)ff_count << 8);
846 uint32_t avg = audio->feedback.compute.fifo_count.fifo_lvl_avg >> 8;
847 uint32_t thr = audio->feedback.compute.fifo_count.fifo_lvl_thr;
848 uint32_t nom = audio->feedback.compute.fifo_count.nom_value;
849 if (avg > thr) {
850 // Buffer fuller than the target level: ask the host to slow
851 // down. rate_const[1] is scaled so avg==fifo_depth (full) maps
852 // exactly to min_value.
853 uint32_t drop =
854 (uint32_t)audio->feedback.compute.fifo_count.rate_const[1] *
855 (avg - thr);
856 audio->feedback.value =
857 (nom > drop) ? nom - drop : audio->feedback.min_value;
858 } else {
859 // Buffer emptier than the target level: ask the host to speed
860 // up. rate_const[0] is scaled so avg==0 (empty) maps exactly to
861 // max_value.
862 audio->feedback.value =
863 nom +
864 (uint32_t)audio->feedback.compute.fifo_count.rate_const[0] *
865 (thr - avg);
866 }
867 uint32_t clamped = audio->feedback.value < audio->feedback.min_value
868 ? audio->feedback.min_value
869 : audio->feedback.value;
870 audio->feedback.value = clamped > audio->feedback.max_value
871 ? audio->feedback.max_value
872 : clamped;
873 } break;
874
876 audio->feedback.value = 1UL << audio->feedback.compute.power_of_2;
877 break;
878
880 audio->feedback.value =
881 (uint32_t)(audio->feedback.compute.float_const *
882 (float)(1UL << (16u - (frame_shift - 1u))));
883 break;
884
886 uint32_t frame_div = backend().isFullSpeed() ? 1000u : 8000u;
887 audio->feedback.value =
888 (audio->feedback.compute.fixed.sample_freq << 16) / frame_div;
889 } break;
890
891 default:
892 break;
893 }
894 }
895
896 if (backend().claimEndpoint(audio->rhport, audio->ep_fb)) {
897 audiod_fb_send(audio);
898 }
899 }
900
901 // USBD Driver API — public so a backend's class-driver registration glue
902 // (e.g. USBAudioBackendTinyUSB.h's trampoline into TinyUSB's
903 // usbd_class_driver_t) can call these without being a member of this
904 // class. A native-HAL backend with no such registration concept would
905 // instead call these directly from its own ISR/init code.
906 public:
907 void audiod_init(void) {
908 audiod_fct_.resize(getAudioCount());
909
910 // Initialize control buffers
911 for (uint8_t i = 0; i < getAudioCount(); i++) {
912 audiod_function_t* audio = &audiod_fct_[i];
913 // Initialize control buffers
914 int size = getCtrlBufSz(i);
915 audio->ctrl_buf.resize(size);
916 audio->ctrl_buf_sz = size;
917 // Initialize active alternate interface buffers
919 // Initialize IN EP — lin_buf_in is the DMA staging buffer (one frame).
920 // Audio data flows through bufferTx() (resized in begin()), not ep_in_ff.
921 if (isEpInEnabled()) {
922 // Max packet across all advertised rates (see supported_sample_rates).
923 uint16_t max_pkt = descr_builder.calcPacketSizeForRate(
925 audio->lin_buf_in.resize(max_pkt);
926 }
927 // Initialize OUT EP linear (DMA staging) buffer.
928 if (isEpOutEnabled()) {
929 uint16_t max_pkt = descr_builder.calcPacketSizeForRate(
931 audio->lin_buf_out.resize(max_pkt);
932 }
933 if (isFeedbackEpEnabled()) {
934 audio->fb_buf.resize(1); // one uint32_t = 4 bytes of feedback data
935 }
936 }
937 }
938
939 bool audiod_deinit(void) {
940 return false; // TODO not implemented yet
941 }
942
943 void audiod_reset(uint8_t rhport) {
944 (void)rhport;
945 for (uint8_t i = 0; i < getAudioCount(); i++) {
946 audiod_function_t* audio = &audiod_fct_[i];
947 memset(audio, 0, getResetSize());
948 if (isEpInEnabled()) {
949 bufferTx().reset();
950 }
951 if (isEpOutEnabled()) {
952 bufferRx().reset();
953 }
954 }
955 }
956
957 uint16_t audiod_open(uint8_t rhport, UsbInterfaceDescriptorView const& itf_desc,
958 uint8_t const* raw_desc, uint16_t max_len) {
959 (void)max_len;
960 if (!(kUsbClassAudio == itf_desc.bInterfaceClass &&
962 return 0;
963 if (itf_desc.bInterfaceProtocol != kAudioIntProtocolCodeV2) return 0;
964 if (itf_desc.bNumEndpoints > 1) return 0;
965 if (itf_desc.bNumEndpoints == 1 && !isInterruptEpEnabled()) return 0;
966 if (itf_desc.bAlternateSetting != 0) return 0;
967 uint8_t i;
968 for (i = 0; i < getAudioCount(); i++) {
969 if (!audiod_fct_[i].p_desc) {
970 audiod_fct_[i].p_desc = raw_desc;
971 audiod_fct_[i].rhport = rhport;
972 audiod_fct_[i].desc_length = getDescLen(i);
973 // audiod_reset() zeroes ctrl_buf_sz via memset — restore it so
974 // controlTransfer() receives the correct buffer length.
975 audiod_fct_[i].ctrl_buf_sz = getCtrlBufSz(i);
977 uint8_t ep_in = 0, ep_out = 0, ep_fb = 0;
978 uint16_t ep_in_size = 0, ep_out_size = 0;
979 UsbEndpointDescriptorView desc_ep_out{};
980 bool has_ep_out_view = false;
981 uint8_t const* p_desc = audiod_fct_[i].p_desc;
982 uint8_t const* p_desc_end =
983 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
984 while (p_desc_end - p_desc > 0) {
985 if (descType(p_desc) == kUsbDescTypeEndpoint) {
987 if (desc_ep.xferType == UsbXferType::Isochronous) {
988 if (isFeedbackEpEnabled() && desc_ep.usage == 1) {
989 ep_fb = desc_ep.bEndpointAddress;
990 }
991 if (desc_ep.usage == 0) {
992 if (isEpInEnabled() && desc_ep.direction() == UsbDir::In) {
993 ep_in = desc_ep.bEndpointAddress;
994 uint16_t sz = desc_ep.packetSize();
995 ep_in_size = sz > ep_in_size ? sz : ep_in_size;
996 } else if (isEpOutEnabled() &&
997 desc_ep.direction() == UsbDir::Out) {
998 ep_out = desc_ep.bEndpointAddress;
999 uint16_t sz = desc_ep.packetSize();
1000 ep_out_size = sz > ep_out_size ? sz : ep_out_size;
1001 desc_ep_out = desc_ep;
1002 has_ep_out_view = true;
1003 }
1004 }
1005 }
1006 }
1007 p_desc = descNext(p_desc);
1008 }
1009 if (isEpInEnabled() && ep_in) {
1010 bool alloc_ok = backend().isoAllocEndpoint(rhport, ep_in, ep_in_size);
1011 LOGD("iso_alloc IN ep=0x%02x sz=%u: %s", ep_in, ep_in_size,
1012 alloc_ok ? "OK" : "FAIL");
1013 }
1014 if (isEpOutEnabled() && ep_out) {
1015 bool alloc_ok = backend().isoAllocEndpoint(rhport, ep_out, ep_out_size);
1016 LOGD("iso_alloc OUT ep=0x%02x sz=%u: %s", ep_out, ep_out_size,
1017 alloc_ok ? "OK" : "FAIL");
1018 if (has_ep_out_view) audiod_fct_[i].ep_out_view = desc_ep_out;
1019 if (backend().usesIsoAlloc() && has_ep_out_view) {
1020 // Pre-activate during enumeration (no isochronous traffic).
1021 // Cannot be done in SET_INTERFACE because iso_activate blocks
1022 // on ESP32's DWC2 when the host is already sending. Activation
1023 // itself resets the endpoint's busy/claimed state to a known
1024 // (not busy) baseline, so nothing further is needed here for
1025 // the first open -- see closeEpOut() for why re-activation is
1026 // also needed on every subsequent re-open.
1027 backend().isoActivateEndpoint(rhport, desc_ep_out);
1028 LOGD("iso_activate OUT: done");
1029 }
1030 }
1031 if (isFeedbackEpEnabled() && ep_fb) {
1032 backend().isoAllocEndpoint(rhport, ep_fb, 4);
1033 }
1034 }
1035 // Scan for bclock_id_tx (clock entity referenced by the USB-streaming
1036 // terminal) and interval_tx. Runs in TX, RX, and RXTX mode so that
1037 // clock-validity/frequency GET requests always succeed.
1038 // TX/RXTX: Output Terminal type=USB_STREAMING → bCSourceID at [8]
1039 // RX: Input Terminal type=USB_STREAMING → bCSourceID at [7]
1040 // interval_tx is only meaningful for the ISO IN endpoint (TX/RXTX).
1041 if (isEpInEnabled() || isEpOutEnabled()) {
1042 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1043 uint8_t const* p_desc_end =
1044 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
1045 while (p_desc_end - p_desc > 0) {
1046 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1047 if (isEpInEnabled()) {
1048 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1049 if (desc_ep.xferType == UsbXferType::Isochronous &&
1050 desc_ep.usage == 0 && desc_ep.direction() == UsbDir::In) {
1051 audiod_fct_[i].interval_tx = desc_ep.bInterval;
1052 }
1053 }
1054 } else if (descType(p_desc) == kUsbDescTypeCsInterface) {
1055 if (descSubtype(p_desc) == kAudioCsAcOutputTerminal) {
1056 if (descU16(p_desc + 4) == kAudioTermTypeUsbStreaming) {
1057 audiod_fct_[i].bclock_id_tx = p_desc[8]; // OT bCSourceID
1058 }
1059 } else if (descSubtype(p_desc) == kAudioCsAcInputTerminal) {
1060 if (descU16(p_desc + 4) == kAudioTermTypeUsbStreaming) {
1061 audiod_fct_[i].bclock_id_tx = p_desc[7]; // IT bCSourceID
1062 }
1063 }
1064 }
1065 p_desc = descNext(p_desc);
1066 }
1067 }
1068
1069 if (isInterruptEpEnabled()) {
1070 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1071 uint8_t const* p_desc_end =
1072 p_desc + audiod_fct_[i].desc_length - kAudioDescIadLen;
1073 while (p_desc_end - p_desc > 0) {
1074 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1075 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1076 uint8_t const ep_addr = desc_ep.bEndpointAddress;
1077 if (desc_ep.direction() == UsbDir::In &&
1078 desc_ep.xferType == UsbXferType::Interrupt) {
1079 if (backend().openEndpoint(audiod_fct_[i].rhport, desc_ep)) {
1080 audiod_fct_[i].ep_int = ep_addr;
1081 } else {
1082 LOGE(" UAC2: interrupt EP 0x%02x open failed", ep_addr);
1083
1084 }
1085 }
1086 }
1087 p_desc = descNext(p_desc);
1088 }
1089 }
1090 audiod_fct_[i].mounted = true;
1091 break;
1092 }
1093 }
1094 if (i >= getAudioCount()) return 0;
1095 uint16_t drv_len = audiod_fct_[i].desc_length - kAudioDescIadLen;
1096 return drv_len;
1097 }
1098
1099 bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage,
1100 UsbSetupPacket const& request) {
1101 if (stage == kControlStageSetup) {
1102 return audiod_control_request(rhport, request);
1103 } else if (stage == kControlStageData) {
1104 return audiod_control_complete(rhport, request);
1105 }
1106 return true;
1107 }
1108 // Invoked when class request DATA stage is finished.
1109 // return false to stall control EP (e.g Host send non-sense DATA)
1110 bool audiod_control_complete(uint8_t rhport,
1111 UsbSetupPacket const& p_request) {
1112 // Handle audio class specific set requests
1113 if (p_request.type() == UsbSetupPacket::Type::Class &&
1114 p_request.direction() == UsbDir::Out) {
1115 uint8_t func_id;
1116
1117 switch (p_request.recipient()) {
1119 uint8_t itf = u16Low(p_request.wIndex);
1120 uint8_t entityID = u16High(p_request.wIndex);
1121
1122 if (entityID != 0) {
1123 func_id = 0;
1124 uint8_t ctrlSel = u16High(p_request.wValue);
1125 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1126
1127 // ── Clock Source SET_CUR (sample rate) ──────────────
1129 ctrlSel == kAudioCsCtrlSamFreq &&
1130 p_request.bRequest == kAudioCsReqCur) {
1132 }
1133
1134 // ── Feature Unit SET_CUR (mute / volume) ────────────
1135 if (isFeatureUnit(entityID) &&
1136 p_request.bRequest == kAudioCsReqCur) {
1137 uint8_t channel = u16Low(p_request.wValue);
1138 if (ctrlSel == AUDIO_FU_CTRL_MUTE) {
1139 setMute(cb[0] != 0, channel);
1140 } else if (ctrlSel == AUDIO_FU_CTRL_VOLUME) {
1141 int16_t v;
1142 memcpy(&v, cb, 2);
1143 setVolume(uac2ToFloat(v), channel);
1144 }
1145 }
1146
1147 // Invoke callback
1149 return tud_audio_set_req_entity_cb_(this, rhport, p_request, cb);
1150 }
1151 } else {
1152 // Find index of audio driver structure and verify interface really
1153 // exists
1154 if (!audiod_verify_itf_exists(itf, &func_id)) return false;
1155
1156 // Invoke callback
1159 this, rhport, p_request,
1160 audiod_fct_[func_id].ctrl_buf.data());
1161 }
1162 }
1163 } break;
1164
1166 uint8_t ep = u16Low(p_request.wIndex);
1167
1168 // Check if entity is present and get corresponding driver index
1169 if (!audiod_verify_ep_exists(ep, &func_id)) return false;
1170
1171 // Invoke callback
1174 this, rhport, p_request, audiod_fct_[func_id].ctrl_buf.data());
1175 }
1176 } break;
1177 // Unknown/Unsupported recipient
1178 default:
1179 return false;
1180 }
1181 }
1182 return true;
1183 }
1184
1186 bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, UsbXferResult result,
1187 uint32_t xferred_bytes) {
1188 (void)result;
1189 (void)xferred_bytes;
1190 for (uint8_t func_id = 0; func_id < getAudioCount(); func_id++) {
1191 audiod_function_t* audio = &audiod_fct_[func_id];
1192 if (isInterruptEpEnabled() && audio->ep_int == ep_addr) {
1193 if (int_done_cb_) int_done_cb_(this, rhport);
1194 return true;
1195 }
1196 if (isEpInEnabled() && audio->ep_in == ep_addr &&
1197 audio->alt_setting.size() != 0) {
1199
1200 uint16_t frame_bytes = isEpInFlowControlEnabled()
1202 : audio->ep_in_sz;
1203 if (frame_bytes > audio->ep_in_sz) frame_bytes = audio->ep_in_sz;
1204 if (tx_done_cb_) tx_done_cb_(this, rhport, audio, frame_bytes);
1205
1206 tx_frame_bytes_last_ = frame_bytes;
1207 tx_xferred_last_ = xferred_bytes;
1208
1209 // Drain platform buffer into lin_buf_in, zero-pad, send via DMA.
1210 {
1211 uint8_t* dst = audio->lin_buf_in.data();
1212 uint16_t n = (uint16_t)bufferTx().readArray(dst, frame_bytes);
1214 if (n < frame_bytes) memset(dst + n, 0, frame_bytes - n);
1215 (void)backend().transfer(rhport, audio->ep_in, dst, frame_bytes);
1216 }
1217 return true;
1218 }
1219 if (isEpOutEnabled() && audio->ep_out == ep_addr) {
1221 rx_total_bytes_ += xferred_bytes;
1222 // Copy DMA-received data into the platform buffer, re-arm DMA.
1223 if (xferred_bytes > 0) {
1224 int written = bufferRx().writeArray(audio->lin_buf_out.data(),
1225 (int)xferred_bytes);
1226 if ((uint32_t)written < xferred_bytes)
1227 rx_dropped_bytes_ += xferred_bytes - (uint32_t)written;
1228 }
1229 // Re-arm the OUT endpoint before invoking the callback: rx_done_cb_
1230 // is user code (e.g. it may push the packet through a resampler)
1231 // and its duration is out of our control. If the next transfer
1232 // isn't queued until after it returns, a slow callback delays
1233 // re-arming past the next isochronous OUT window and the host's
1234 // packet for that frame is lost - audible as clicks/dropouts,
1235 // and more likely to happen the larger each packet is (e.g. at
1236 // 44.1/48kHz vs. 32kHz).
1237 (void)backend().transfer(rhport, audio->ep_out,
1238 audio->lin_buf_out.data(), audio->ep_out_sz);
1239 if (rx_done_cb_)
1240 rx_done_cb_(this, rhport, audio, (uint16_t)xferred_bytes);
1241 return true;
1242 }
1243 if (isFeedbackEpEnabled() && audio->ep_fb == ep_addr) {
1244 // SOF ISR owns re-sending; just notify the application.
1245 if (fb_done_cb_) fb_done_cb_(this, func_id);
1246 return true;
1247 }
1248 }
1249 return false;
1250 }
1251
1252 void audiod_sof_isr(uint8_t rhport, uint32_t frame_count) {
1253 (void)rhport;
1254 (void)frame_count;
1256 for (uint8_t i = 0; i < getAudioCount(); i++) {
1257 audiod_function_t* audio = &audiod_fct_[i];
1258 if (audio->ep_fb != 0) {
1259 uint8_t const hs_adjust = backend().isHighSpeed() ? 3 : 0;
1260 uint32_t const interval =
1261 1UL << (audio->feedback.frame_shift - hs_adjust);
1262 if (0 == (frame_count & (interval - 1))) {
1263 tud_audio_feedback_interval_isr(i, frame_count,
1264 audio->feedback.frame_shift);
1265 }
1266 }
1267 }
1268 }
1269 }
1270
1271 protected:
1272 // ── Clock Source GET handler ────────────────────────────────────────────
1273 bool handleClockSourceGet(uint8_t rhport,
1274 UsbSetupPacket const& p_request,
1275 uint8_t* cb) {
1276 uint8_t ctrlSel = u16High(p_request.wValue);
1277 if (ctrlSel == kAudioCsCtrlClkValid &&
1278 p_request.bRequest == kAudioCsReqCur) {
1279 cb[0] = 1;
1280 return backend().controlTransfer(rhport, p_request, cb, 1);
1281 }
1282 if (ctrlSel == kAudioCsCtrlSamFreq) {
1283 uint32_t rate = (uint32_t)config_.sample_rate;
1284 if (p_request.bRequest == kAudioCsReqCur) {
1285 memcpy(cb, &rate, 4);
1286 return backend().controlTransfer(rhport, p_request, cb, 4);
1287 }
1288 if (p_request.bRequest == kAudioCsReqRange) {
1290 // List all supported discrete rates
1291 const auto& rates = config_.supported_sample_rates;
1292 uint16_t cnt = (uint16_t)rates.size();
1293 memcpy(cb, &cnt, 2);
1294 for (size_t i = 0; i < rates.size(); i++) {
1295 uint32_t r = rates[i];
1296 uint32_t z = 0;
1297 memcpy(cb + 2 + i * 12, &r, 4);
1298 memcpy(cb + 2 + i * 12 + 4, &r, 4);
1299 memcpy(cb + 2 + i * 12 + 8, &z, 4);
1300 }
1301 return backend().controlTransfer(
1302 rhport, p_request, cb,
1303 (uint16_t)(2 + rates.size() * 12));
1304 } else {
1305 // Single fixed rate from config
1306 uint16_t cnt = 1;
1307 uint32_t z = 0;
1308 memcpy(cb, &cnt, 2);
1309 memcpy(cb + 2, &rate, 4); // dMIN
1310 memcpy(cb + 6, &rate, 4); // dMAX
1311 memcpy(cb + 10, &z, 4); // dRES = 0 (fixed)
1312 return backend().controlTransfer(rhport, p_request, cb, 14);
1313 }
1314 }
1315 }
1316 return false;
1317 }
1318
1319 // ── Feature Unit GET handler ──────────────────────────────────────────
1320 bool handleFeatureUnitGet(uint8_t rhport,
1321 UsbSetupPacket const& p_request,
1322 uint8_t* cb) {
1323 uint8_t ctrlSel = u16High(p_request.wValue);
1324 uint8_t channel = u16Low(p_request.wValue);
1325 if (ctrlSel == AUDIO_FU_CTRL_MUTE &&
1326 p_request.bRequest == kAudioCsReqCur) {
1327 cb[0] = isMute(channel) ? 1 : 0;
1328 return backend().controlTransfer(rhport, p_request, cb, 1);
1329 }
1330 if (ctrlSel == AUDIO_FU_CTRL_VOLUME) {
1331 if (p_request.bRequest == kAudioCsReqCur) {
1332 int16_t v = floatToUac2(volume(channel));
1333 memcpy(cb, &v, 2);
1334 return backend().controlTransfer(rhport, p_request, cb, 2);
1335 }
1336 if (p_request.bRequest == kAudioCsReqRange) {
1337 uint16_t cnt = 1;
1338 int16_t vmin = -25600, vmax = 0, vres = 256;
1339 memcpy(cb + 0, &cnt, 2);
1340 memcpy(cb + 2, &vmin, 2);
1341 memcpy(cb + 4, &vmax, 2);
1342 memcpy(cb + 6, &vres, 2);
1343 return backend().controlTransfer(rhport, p_request, cb, 8);
1344 }
1345 }
1346 return false;
1347 }
1348
1349 // ── Entity request handler (Clock Source + Feature Unit) ──────────────
1350 bool handleEntityRequest(uint8_t rhport,
1351 UsbSetupPacket const& p_request,
1352 uint8_t entityID) {
1353 uint8_t func_id = 0;
1354 uint8_t* cb = audiod_fct_[func_id].ctrl_buf.data();
1355 bool is_get = (p_request.direction() == UsbDir::In);
1356
1358 if (is_get && handleClockSourceGet(rhport, p_request, cb)) return true;
1359 // SET — schedule data receive for audiod_control_complete()
1360 return backend().controlTransfer(rhport, p_request, cb,
1361 audiod_fct_[func_id].ctrl_buf_sz);
1362 }
1363
1364 if (isFeatureUnit(entityID)) {
1365 if (is_get && handleFeatureUnitGet(rhport, p_request, cb)) return true;
1366 // SET — schedule data receive
1367 return backend().controlTransfer(rhport, p_request, cb,
1368 audiod_fct_[func_id].ctrl_buf_sz);
1369 }
1370
1371 // Unknown entity — try generic verify
1372 uint8_t itf = u16Low(p_request.wIndex);
1373 if (!audiod_verify_entity_exists(itf, entityID, &func_id)) {
1374 backend().controlStatus(rhport, p_request);
1375 return true;
1376 }
1377 if (is_get && req_entity_cb_) return req_entity_cb_(this, func_id);
1378 return backend().controlTransfer(rhport, p_request,
1379 audiod_fct_[func_id].ctrl_buf.data(),
1380 audiod_fct_[func_id].ctrl_buf_sz);
1381 }
1382
1383 // ── Interface request handler (entityID == 0) ─────────────────────────
1384 bool handleInterfaceRequest(uint8_t rhport,
1385 UsbSetupPacket const& p_request) {
1386 uint8_t itf = u16Low(p_request.wIndex);
1387 uint8_t func_id;
1388 if (!audiod_verify_itf_exists(itf, &func_id)) return false;
1389 if (p_request.direction() == UsbDir::In) {
1390 if (get_req_itf_cb_) return get_req_itf_cb_(this, rhport, p_request);
1391 return false;
1392 }
1393 return backend().controlTransfer(rhport, p_request,
1394 audiod_fct_[func_id].ctrl_buf.data(),
1395 audiod_fct_[func_id].ctrl_buf_sz);
1396 }
1397
1398 // ── Endpoint request handler ──────────────────────────────────────────
1399 bool handleEndpointRequest(uint8_t rhport,
1400 UsbSetupPacket const& p_request) {
1401 uint8_t ep = u16Low(p_request.wIndex);
1402 uint8_t func_id;
1403 if (!audiod_verify_ep_exists(ep, &func_id)) return false;
1404 if (p_request.direction() == UsbDir::In) {
1405 if (get_req_ep_cb_) return get_req_ep_cb_(this, rhport, p_request);
1406 return false;
1407 }
1408 return backend().controlTransfer(rhport, p_request,
1409 audiod_fct_[func_id].ctrl_buf.data(),
1410 audiod_fct_[func_id].ctrl_buf_sz);
1411 }
1412
1413 // ── Main control request dispatcher ───────────────────────────────────
1414 bool audiod_control_request(uint8_t rhport,
1415 UsbSetupPacket const& p_request) {
1416 if (p_request.type() == UsbSetupPacket::Type::Standard) {
1417 switch (p_request.bRequest) {
1418 case (uint8_t)UsbStdRequest::GetInterface:
1419 return audiod_get_interface(rhport, p_request);
1420 case (uint8_t)UsbStdRequest::SetInterface:
1421 return audiod_set_interface(rhport, p_request);
1422 case (uint8_t)UsbStdRequest::ClearFeature:
1423 return true;
1424 default:
1425 return false;
1426 }
1427 }
1428
1429 if (p_request.type() == UsbSetupPacket::Type::Class) {
1430 switch (p_request.recipient()) {
1432 uint8_t entityID = u16High(p_request.wIndex);
1433 return (entityID != 0)
1434 ? handleEntityRequest(rhport, p_request, entityID)
1435 : handleInterfaceRequest(rhport, p_request);
1436 }
1438 return handleEndpointRequest(rhport, p_request);
1439 default:
1440 return false;
1441 }
1442 }
1443
1444 return false;
1445 }
1446
1447 // Verify an entity with the given ID exists and returns also the
1448 // corresponding driver index
1449 bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID,
1450 uint8_t* func_id) {
1451 uint8_t i;
1452 for (i = 0; i < getAudioCount(); i++) {
1453 // Look for the correct driver by checking if the unique standard AC
1454 // interface number fits
1455 if (audiod_fct_[i].p_desc &&
1456 decodeInterface(audiod_fct_[i].p_desc).bInterfaceNumber == itf) {
1457 // Get pointers after class specific AC descriptors and end of AC
1458 // descriptors - entities are defined in between
1459 uint8_t const* p_desc =
1460 descNext(audiod_fct_[i].p_desc); // Points to CS AC descriptor
1461 // AC interface header wTotalLength lives at byte offset 6 (UAC2
1462 // Table 4-5: bLength,bDescriptorType,bDescriptorSubType,bcdADC(2),
1463 // bCategory,wTotalLength(2),bmControls).
1464 uint8_t const* p_desc_end = descU16(p_desc + 6) + p_desc;
1465 p_desc = descNext(p_desc); // Get past CS AC descriptor
1466
1467 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1468 // strict-overflow warning
1469 while (p_desc_end - p_desc > 0) {
1470 if (p_desc[3] == entityID) // Entity IDs are always at offset 3
1471 {
1472 *func_id = i;
1473 return true;
1474 }
1475 p_desc = descNext(p_desc);
1476 }
1477 }
1478 }
1479 return false;
1480 }
1481
1482 bool audiod_verify_ep_exists(uint8_t ep, uint8_t* func_id) {
1483 uint8_t i;
1484 for (i = 0; i < getAudioCount(); i++) {
1485 if (audiod_fct_[i].p_desc) {
1486 // Get pointer at end
1487 uint8_t const* p_desc_end =
1488 audiod_fct_[i].p_desc + audiod_fct_[i].desc_length;
1489
1490 // Advance past AC descriptors - EP we look for are streaming EPs
1491 uint8_t const* p_desc = descNext(audiod_fct_[i].p_desc);
1492 p_desc += descU16(p_desc + 6); // AC header wTotalLength, see above
1493
1494 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1495 // strict-overflow warning
1496 while (p_desc_end - p_desc > 0) {
1497 if (descType(p_desc) == kUsbDescTypeEndpoint &&
1498 decodeEndpoint(p_desc).bEndpointAddress == ep) {
1499 *func_id = i;
1500 return true;
1501 }
1502 p_desc = descNext(p_desc);
1503 }
1504 }
1505 }
1506 return false;
1507 }
1508
1509 bool audiod_verify_itf_exists(uint8_t itf, uint8_t* func_id) {
1510 uint8_t i;
1511 for (i = 0; i < getAudioCount(); i++) {
1512 if (audiod_fct_[i].p_desc) {
1513 // Get pointer at beginning and end
1514 uint8_t const* p_desc = audiod_fct_[i].p_desc;
1515 uint8_t const* p_desc_end = audiod_fct_[i].p_desc +
1516 audiod_fct_[i].desc_length -
1518 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1519 // strict-overflow warning
1520 while (p_desc_end - p_desc > 0) {
1521 if (descType(p_desc) == kUsbDescTypeInterface &&
1522 decodeInterface(audiod_fct_[i].p_desc).bInterfaceNumber == itf) {
1523 *func_id = i;
1524 return true;
1525 }
1526 p_desc = descNext(p_desc);
1527 }
1528 }
1529 }
1530 return false;
1531 }
1532
1534 uint8_t const* p_desc) {
1535 // Seed the TX sample rate from the configured AudioInfo so packet-size
1536 // calculation works even when the host never issues a SET_CUR(SAM_FREQ)
1537 // request (typical for single-frequency clock sources). The host may still
1538 // override this later via audiod_control_complete().
1539 if (audio->sample_rate_tx == 0)
1540 audio->sample_rate_tx = (uint32_t)config_.sample_rate;
1541
1542 p_desc = descNext(p_desc); // Exclude standard AS interface descriptor
1543 // of current alternate interface descriptor
1544
1545 // Look for a Class-Specific AS Interface Descriptor(4.9.2) to verify format
1546 // type and format and also to get number of physical channels.
1547 // Layout (UAC2 Table 4-27): bLength,bDescriptorType,bDescriptorSubType,
1548 // bTerminalLink,bmControls,bFormatType[5],bmFormats(4),bNrChannels[10],...
1549 if (descType(p_desc) == kUsbDescTypeCsInterface &&
1550 descSubtype(p_desc) == kAudioCsAsGeneral) {
1551 audio->n_channels_tx = p_desc[10];
1552 audio->format_type_tx = (audio_format_type_t)p_desc[5];
1553 // Look for a Type I Format Type Descriptor(2.3.1.6 - Audio Formats)
1554 // Layout: bLength,bDescriptorType,bDescriptorSubType,bFormatType,
1555 // bSubslotSize[4],bBitResolution.
1556 p_desc = descNext(p_desc);
1557 if (descType(p_desc) == kUsbDescTypeCsInterface &&
1558 descSubtype(p_desc) == kAudioCsAsFormatType &&
1559 p_desc[3] == AUDIO_FORMAT_TYPE_I) {
1560 audio->n_bytes_per_sample_tx = p_desc[4];
1561 }
1562 }
1563
1564 // Fallback from config if descriptor parsing missed any field.
1565 // The descriptor struct layout may differ across TinyUSB versions.
1566 if (audio->n_channels_tx == 0) audio->n_channels_tx = config_.channels;
1567 if (audio->n_bytes_per_sample_tx == 0)
1569 if (audio->format_type_tx == 0) audio->format_type_tx = AUDIO_FORMAT_TYPE_I;
1570 }
1571
1572 // This helper function finds for a given audio function and AS interface
1573 // number the index of the attached driver structure, the index of the
1574 // interface in the audio function
1575 // (e.g. the std. AS interface with interface number 15 is the first AS
1576 // interface for the given audio function and thus gets index zero), and
1577 // finally a pointer to the std. AS interface, where the pointer always points
1578 // to the first alternate setting i.e. alternate interface zero.
1580 uint8_t* idxItf,
1581 uint8_t const** pp_desc_int) {
1582 if (audio->p_desc) {
1583 // Get pointer at end
1584 uint8_t const* p_desc_end =
1585 audio->p_desc + audio->desc_length - kAudioDescIadLen;
1586
1587 // Advance past AC descriptors
1588 uint8_t const* p_desc = descNext(audio->p_desc);
1589 p_desc += descU16(p_desc + 6); // AC header wTotalLength
1590
1591 uint8_t tmp = 0;
1592 // Condition modified from p_desc < p_desc_end to prevent gcc>=12
1593 // strict-overflow warning
1594 while (p_desc_end - p_desc > 0) {
1595 // We assume the number of alternate settings is increasing thus we
1596 // return the index of alternate setting zero!
1597 if (descType(p_desc) == kUsbDescTypeInterface &&
1598 decodeInterface(p_desc).bAlternateSetting == 0) {
1599 if (decodeInterface(p_desc).bInterfaceNumber == itf) {
1600 *idxItf = tmp;
1601 *pp_desc_int = p_desc;
1602 return true;
1603 }
1604 // Increase index, bytes read, and pointer
1605 tmp++;
1606 }
1607 p_desc = descNext(p_desc);
1608 }
1609 }
1610 return false;
1611 }
1612
1613 // This helper function finds for a given AS interface number the index of the
1614 // attached driver structure, the index of the interface in the audio function
1615 // (e.g. the std. AS interface with interface number 15 is the first AS
1616 // interface for the given audio function and thus gets index zero), and
1617 // finally a pointer to the std. AS interface, where the pointer always points
1618 // to the first alternate setting i.e. alternate interface zero.
1619 bool audiod_get_AS_interface_index_global(uint8_t itf, uint8_t* func_id,
1620 uint8_t* idxItf,
1621 uint8_t const** pp_desc_int) {
1622 // Loop over audio driver interfaces
1623 uint8_t i;
1624 for (i = 0; i < getAudioCount(); i++) {
1625 if (audiod_get_AS_interface_index(itf, &audiod_fct_[i], idxItf,
1626 pp_desc_int)) {
1627 *func_id = i;
1628 return true;
1629 }
1630 }
1631
1632 return false;
1633 }
1634
1635 bool audiod_get_interface(uint8_t rhport,
1636 UsbSetupPacket const& p_request) {
1637 uint8_t const itf = u16Low(p_request.wIndex);
1638
1639 // Find index of audio streaming interface
1640 uint8_t func_id, idxItf;
1641 uint8_t const* dummy;
1642
1643 if (!audiod_get_AS_interface_index_global(itf, &func_id, &idxItf, &dummy))
1644 return false;
1645 if (!backend().controlTransfer(
1646 rhport, p_request, &audiod_fct_[func_id].alt_setting[idxItf], 1))
1647 return false;
1648
1649 LOGI(" Get itf: %u - current alt: %u", itf,
1650 audiod_fct_[func_id].alt_setting[idxItf]);
1651
1652 return true;
1653 }
1654
1656 bool apply_correction =
1658 // Format the feedback value
1659 if (apply_correction) {
1660 uint8_t* fb = (uint8_t*)audio->fb_buf.data();
1661
1662 // For FS format is 10.14
1663 *(fb++) = (audio->feedback.value >> 2) & 0xFF;
1664 *(fb++) = (audio->feedback.value >> 10) & 0xFF;
1665 *(fb++) = (audio->feedback.value >> 18) & 0xFF;
1666 *fb = 0;
1667 } else {
1668 audio->fb_buf[0] = audio->feedback.value;
1669 }
1670
1671 // About feedback format on FS
1672 //
1673 // 3 variables: Format | packetSize | sendSize | Working OS:
1674 // 16.16 4 4 Linux, Windows
1675 // 16.16 4 3 Linux
1676 // 16.16 3 4 Linux
1677 // 16.16 3 3 Linux
1678 // 10.14 4 4 Linux
1679 // 10.14 4 3 Linux
1680 // 10.14 3 4 Linux, OSX
1681 // 10.14 3 3 Linux, OSX
1682 //
1683 // We send 3 bytes since sending packet larger than wMaxPacketSize is pretty
1684 // ugly
1685 return backend().transfer(audio->rhport, audio->ep_fb,
1686 (uint8_t*)audio->fb_buf.data(),
1687 apply_correction ? 3 : 4);
1688 }
1689
1690 // ── Close existing EPs for this interface ──────────────────────────────
1691 void closeEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1692 UsbSetupPacket const& p_request) {
1693 if (!isEpInEnabled() || audio->ep_in_as_intf_num != itf) return;
1694 audio->ep_in_as_intf_num = 0;
1695 if (!backend().usesIsoAlloc()) backend().closeEndpoint(rhport, audio->ep_in);
1696 bufferTx().reset();
1698 tud_audio_set_itf_close_EP_cb_(this, rhport, p_request);
1699 audio->ep_in = 0;
1701 audio->packet_sz_tx[0] = 0;
1702 audio->packet_sz_tx[1] = 0;
1703 audio->packet_sz_tx[2] = 0;
1704 }
1706 }
1707
1708 void closeEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1709 UsbSetupPacket const& p_request) {
1710 if (!isEpOutEnabled() || audio->ep_out_as_intf_num != itf) return;
1711 audio->ep_out_as_intf_num = 0;
1712 if (!backend().usesIsoAlloc()) {
1713 backend().closeEndpoint(rhport, audio->ep_out);
1714 } else {
1715 // usesIsoAlloc() backends never close the OUT endpoint between
1716 // sessions (DPRAM stays allocated), so nothing resets the DCD's
1717 // internal busy/claimed bookkeeping for it. If the transfer armed by
1718 // the *previous* openEpOut() never completed (no host data arrived,
1719 // or the session was torn down mid-flight), that endpoint stays
1720 // "busy" forever and every later usbd_edpt_xfer() call in openEpOut()
1721 // silently fails, permanently breaking RX after the first
1722 // open/close cycle. Re-activating here -- while the host is quiet
1723 // (it just asked to close the stream) rather than right as a new
1724 // session starts -- resets busy/claimed to a known-good state for
1725 // the next open, matching the reasoning in audiod_open() for why
1726 // this can't be done at SET_INTERFACE(alt=1) time instead.
1727 backend().isoActivateEndpoint(rhport, audio->ep_out_view);
1728 }
1730 tud_audio_set_itf_close_EP_cb_(this, rhport, p_request);
1731 audio->ep_out = 0;
1732 if (isFeedbackEpEnabled()) {
1733 audio->ep_fb = 0;
1734 memset(&audio->feedback, 0, sizeof(audio->feedback));
1735 }
1737 }
1738
1739 // ── Activate a single endpoint found in the descriptor ────────────────
1740 bool activateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView& desc_ep,
1741 UsbDir dir = UsbDir::In) {
1742 if (backend().usesIsoAlloc()) {
1743 // Skip iso_activate for isochronous OUT — on ESP32's DWC2 it blocks
1744 // for the entire playback duration. The endpoint DPRAM was already
1745 // allocated by iso_alloc in audiod_open(). The XFER call in
1746 // openEpOut will configure the DCD to receive.
1747 if (dir == UsbDir::Out && desc_ep.xferType == UsbXferType::Isochronous)
1748 return true;
1749 return backend().isoActivateEndpoint(rhport, desc_ep);
1750 }
1751 (void)dir;
1752 return backend().openEndpoint(rhport, desc_ep);
1753 }
1754
1755 // ── Open the IN (TX) data endpoint ────────────────────────────────────
1756 void openEpIn(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1757 const UsbEndpointDescriptorView& desc_ep,
1758 uint8_t const* p_desc_for_params) {
1759 audio->ep_in = desc_ep.bEndpointAddress;
1760 audio->ep_in_as_intf_num = itf;
1761 audio->ep_in_sz = desc_ep.packetSize();
1762 if (audio->ep_in_sz == 0) return;
1763
1765 audiod_parse_flow_control_params(audio, p_desc_for_params);
1766
1767 // Arm initial transfer (silence — copier fills the buffer).
1768 uint16_t first_pkt = packetSize();
1769 if (first_pkt > audio->ep_in_sz) first_pkt = audio->ep_in_sz;
1770 audio->lin_buf_in.assign(audio->ep_in_sz, 0);
1771 tx_xfer_armed_ = backend().transfer(rhport, audio->ep_in,
1772 audio->lin_buf_in.data(), first_pkt);
1774 }
1775
1776 // ── Open the OUT (RX) data endpoint ───────────────────────────────────
1777 void openEpOut(uint8_t rhport, audiod_function_t* audio, uint8_t itf,
1778 const UsbEndpointDescriptorView& desc_ep) {
1779 audio->ep_out = desc_ep.bEndpointAddress;
1780 audio->ep_out_as_intf_num = itf;
1781 audio->ep_out_sz = desc_ep.packetSize();
1782 if (audio->ep_out_sz == 0) return;
1783
1784 // iso_activate was done in audiod_open() (no traffic, instant).
1785 // Just arm the transfer here.
1786 if (audio->lin_buf_out.size() < audio->ep_out_sz)
1787 audio->lin_buf_out.assign(audio->ep_out_sz, 0);
1788 backend().transfer(rhport, audio->ep_out, audio->lin_buf_out.data(),
1789 audio->ep_out_sz);
1791 }
1792
1793 // ── Open the explicit feedback endpoint ───────────────────────────────
1795 const UsbEndpointDescriptorView& desc_ep) {
1796 audio->ep_fb = desc_ep.bEndpointAddress;
1797 audio->feedback.frame_shift = desc_ep.bInterval - 1;
1798 }
1799
1800 // ── Configure feedback computation parameters ─────────────────────────
1801 void setupFeedback(audiod_function_t* audio, uint8_t func_id, uint8_t alt) {
1802 if (!isFeedbackEpEnabled() || audio->ep_fb == 0) return;
1803
1804 audio_feedback_params_t fb_param = {};
1806 fb_param.sample_freq = config_.sample_rate;
1808 tud_audio_feedback_params_cb_(this, func_id, alt, &fb_param);
1809 audio->feedback.compute_method = fb_param.method;
1810
1814
1815 uint32_t const frame_div = backend().isFullSpeed() ? 1000 : 8000;
1816 audio->feedback.min_value = ((fb_param.sample_freq - 1) / frame_div) << 16;
1817 audio->feedback.max_value = (fb_param.sample_freq / frame_div + 1) << 16;
1818
1819 switch (fb_param.method) {
1823 audiod_set_fb_params_freq(audio, fb_param.sample_freq,
1824 fb_param.frequency.mclk_freq);
1825 break;
1827 // Use bufferRx() size — ep_out_ff may be uninitialized in linear buffer mode
1828 uint16_t fifo_depth = bufferRx().size();
1829 if (fifo_depth == 0) fifo_depth = 1; // guard against div-by-zero
1830 uint16_t fifo_lvl_thr = fifo_depth / 2;
1831 audio->feedback.compute.fifo_count.fifo_lvl_thr = fifo_lvl_thr;
1832 // fifo_lvl_avg is a Q8 exponential moving average (see the update
1833 // formula in tud_audio_feedback_interval_isr: `avg = fifo_lvl_avg
1834 // >> 8`, fed by `ff_count << 8` each tick) -- seed it in the same
1835 // Q8 scale. It was previously seeded as `fifo_lvl_thr << 16` (Q16,
1836 // 256x too large), which pinned the very first ~1400 feedback
1837 // updates (~1.4s at 1kHz) at feedback.max_value before decaying
1838 // into a sane range.
1840 ((uint32_t)fifo_lvl_thr) << 8;
1841 uint32_t nominal =
1842 ((fb_param.sample_freq / 100) << 16) / (frame_div / 100);
1843 audio->feedback.compute.fifo_count.nom_value = nominal;
1845 (uint16_t)((audio->feedback.max_value - nominal) / fifo_lvl_thr);
1847 (uint16_t)((nominal - audio->feedback.min_value) / fifo_lvl_thr);
1848 if (backend().isHighSpeed()) {
1849 audio->feedback.compute.fifo_count.rate_const[0] /= 8;
1850 audio->feedback.compute.fifo_count.rate_const[1] /= 8;
1851 }
1852 } break;
1853 default:
1854 break;
1855 }
1856 }
1857
1858 // ── Scan descriptor for endpoints and open them ───────────────────────
1860 uint8_t func_id, uint8_t itf, uint8_t alt) {
1861 uint8_t const* p_desc = audio->p_desc;
1862 uint8_t const* p_desc_end =
1863 p_desc + audio->desc_length - kAudioDescIadLen;
1864 LOGD(" openEPs: p_desc=%p end=%p len=%u itf=%u alt=%u",
1865 p_desc, p_desc_end, audio->desc_length, itf, alt);
1866
1867 while (p_desc_end - p_desc > 0) {
1868 if (descType(p_desc) == kUsbDescTypeInterface &&
1869 decodeInterface(p_desc).bInterfaceNumber == itf &&
1870 decodeInterface(p_desc).bAlternateSetting == alt) {
1871 uint8_t const* p_desc_for_params =
1872 (isEpInEnabled() && isEpInFlowControlEnabled()) ? p_desc : nullptr;
1873 uint8_t foundEPs = 0;
1874 uint8_t nEps = decodeInterface(p_desc).bNumEndpoints;
1875 LOGD(" matched itf=%u alt=%u nEps=%u", itf, alt, nEps);
1876
1877 while (foundEPs < nEps && (p_desc_end - p_desc > 0)) {
1878 LOGD(" scan: type=0x%02x len=%u offset=%d",
1879 p_desc[1], p_desc[0], (int)(p_desc - audio->p_desc));
1880 if (descType(p_desc) == kUsbDescTypeEndpoint) {
1881 UsbEndpointDescriptorView desc_ep = decodeEndpoint(p_desc);
1882
1883 LOGD(" activating ep=0x%02x type=%u...",
1884 desc_ep.bEndpointAddress, (unsigned)desc_ep.xferType);
1885 if (!activateEndpoint(rhport, desc_ep, desc_ep.direction())) {
1886 LOGD(" activateEndpoint FAILED");
1887 p_desc = descNext(p_desc);
1888 continue;
1889 }
1890 LOGD(" activated OK");
1891 // Skip clear_stall for isochronous OUT (iso_activate was also
1892 // skipped). For other endpoints, clear the stall as usual.
1893 if (!(desc_ep.direction() == UsbDir::Out &&
1895 backend().clearStall(rhport, desc_ep.bEndpointAddress);
1896
1897 uint8_t ep_addr = desc_ep.bEndpointAddress;
1898 if (isEpInEnabled() && desc_ep.direction() == UsbDir::In &&
1899 desc_ep.usage == 0x00)
1900 openEpIn(rhport, audio, itf, desc_ep, p_desc_for_params);
1901
1902 if (isEpOutEnabled()) {
1903 if (desc_ep.direction() == UsbDir::Out)
1904 openEpOut(rhport, audio, itf, desc_ep);
1905 if (isFeedbackEpEnabled() &&
1906 desc_ep.direction() == UsbDir::In && desc_ep.usage == 1)
1907 openEpFeedback(audio, desc_ep);
1908 }
1909 foundEPs += 1;
1910 }
1911 p_desc = descNext(p_desc);
1912 }
1913
1914 if (foundEPs != nEps) return true; // ZLP already sent
1915
1917 tud_audio_set_itf_cb_(this, rhport, UsbSetupPacket{});
1918
1919 setupFeedback(audio, func_id, alt);
1920 return true;
1921 }
1922 p_desc = descNext(p_desc);
1923 }
1924 return true;
1925 }
1926
1927 // ── Main SET_INTERFACE handler ────────────────────────────────────────
1928 bool audiod_set_interface(uint8_t rhport,
1929 UsbSetupPacket const& p_request) {
1930 uint8_t const itf = u16Low(p_request.wIndex);
1931 uint8_t const alt = u16Low(p_request.wValue);
1932 LOGD("SET_ITF itf=%u alt=%u [start]", itf, alt);
1933
1934 uint8_t func_id, idxItf;
1935 uint8_t const* p_desc;
1936 if (!audiod_get_AS_interface_index_global(itf, &func_id, &idxItf,
1937 &p_desc)) {
1938 LOGD(" AS interface %u not found", itf);
1939 backend().controlStatus(rhport, p_request);
1940 return true;
1941 }
1942 LOGD(" found func=%u idx=%u", func_id, idxItf);
1943
1944 audiod_function_t* audio = &audiod_fct_[func_id];
1945
1946 // 1. Close existing EPs
1947 LOGD(" close EPs");
1948 closeEpIn(rhport, audio, itf, p_request);
1949 closeEpOut(rhport, audio, itf, p_request);
1950
1951 // 2. Save alt setting and acknowledge
1952 audio->alt_setting[idxItf] = alt;
1953
1954 backend().controlStatus(rhport, p_request);
1955 openEndpointsForAltSetting(rhport, audio, func_id, itf, alt);
1956
1957 // 4. Update SOF and flow control
1958 if (isFeedbackEpEnabled()) {
1959 bool enable_sof = false;
1960 for (uint8_t i = 0; i < getAudioCount(); i++) {
1961 if (audiod_fct_[i].ep_fb != 0) {
1962 enable_sof = true;
1963 break;
1964 }
1965 }
1966 backend().enableSof(rhport, enable_sof);
1967 }
1970
1971 return true;
1972 }
1973
1974 static bool isPowerOfTwo(uint32_t value) {
1975 return value != 0 && (value & (value - 1)) == 0;
1976 }
1977
1978 static uint8_t log2Floor(uint32_t value) {
1979 uint8_t result = 0;
1980 while ((value >>= 1u) != 0u) result++;
1981 return result;
1982 }
1983
1984 bool audiod_set_fb_params_freq(audiod_function_t* audio, uint32_t sample_freq,
1985 uint32_t mclk_freq) {
1986 // Check if frame interval is within sane limits
1987 // The interval value n_frames was taken from the descriptors within
1988
1989 // n_frames_min is ceil(2^10 * f_s / f_m) for full speed and ceil(2^13 * f_s
1990 // / f_m) for high speed this lower limit ensures the measures feedback
1991 // value has sufficient precision
1992 uint32_t const k = backend().isFullSpeed() ? 10 : 13;
1993 uint32_t const n_frame = (1UL << audio->feedback.frame_shift);
1994
1995 if ((((1UL << k) * sample_freq / mclk_freq) + 1) > n_frame) {
1996 LOGE(" UAC2 feedback interval too small");
1997 return false;
1998 }
1999
2000 // Check if parameters really allow for a power of two division
2001 if ((mclk_freq % sample_freq) == 0 &&
2002 isPowerOfTwo(mclk_freq / sample_freq)) {
2003 audio->feedback.compute_method =
2005 audio->feedback.compute.power_of_2 =
2006 (uint8_t)(16 - (audio->feedback.frame_shift - 1) -
2007 log2Floor(mclk_freq / sample_freq));
2008 } else if (audio->feedback.compute_method ==
2011 (float)sample_freq / (float)mclk_freq *
2012 (1UL << (16 - (audio->feedback.frame_shift - 1)));
2013 } else {
2014 audio->feedback.compute.fixed.sample_freq = sample_freq;
2015 audio->feedback.compute.fixed.mclk_freq = mclk_freq;
2016 }
2017
2018 return true;
2019 }
2020
2022 if (audio->format_type_tx != AUDIO_FORMAT_TYPE_I) return false;
2023 if (!audio->n_channels_tx) return false;
2024 if (!audio->n_bytes_per_sample_tx) return false;
2025 if (!audio->interval_tx) return false;
2026 if (!audio->sample_rate_tx) return false;
2027
2028 // Restart the fractional accumulator for this streaming session.
2029 audio->tx_sample_acc = 0;
2030
2031 bool full_speed = backend().isFullSpeed();
2032 const uint8_t interval =
2033 full_speed ? audio->interval_tx : 1 << (audio->interval_tx - 1);
2034
2035 const uint16_t sample_normimal = (uint16_t)(audio->sample_rate_tx *
2036 interval / (full_speed ? 1000 : 8000));
2037 const uint16_t sample_reminder = (uint16_t)(audio->sample_rate_tx *
2038 interval % (full_speed ? 1000 : 8000));
2039
2040 const uint16_t packet_sz_tx_min =
2041 (uint16_t)((sample_normimal - 1) * audio->n_channels_tx *
2042 audio->n_bytes_per_sample_tx);
2043 const uint16_t packet_sz_tx_norm =
2044 (uint16_t)(sample_normimal * audio->n_channels_tx *
2045 audio->n_bytes_per_sample_tx);
2046 const uint16_t packet_sz_tx_max =
2047 (uint16_t)((sample_normimal + 1) * audio->n_channels_tx *
2048 audio->n_bytes_per_sample_tx);
2049
2050 // Endpoint size must larger than packet size
2051 if (packet_sz_tx_max > audio->ep_in_sz) return false;
2052
2053 // Frmt20.pdf 2.3.1.1 USB Packets
2054 if (sample_reminder) {
2055 // All virtual frame packets must either contain INT(nav) audio slots
2056 // (small VFP) or INT(nav)+1 (large VFP) audio slots
2057 audio->packet_sz_tx[0] = packet_sz_tx_norm;
2058 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2059 audio->packet_sz_tx[2] = packet_sz_tx_max;
2060 } else {
2061 // In the case where nav = INT(nav), ni may vary between INT(nav)-1 (small
2062 // VFP), INT(nav) (medium VFP) and INT(nav)+1 (large VFP).
2063 audio->packet_sz_tx[0] = packet_sz_tx_min;
2064 audio->packet_sz_tx[1] = packet_sz_tx_norm;
2065 audio->packet_sz_tx[2] = packet_sz_tx_max;
2066 }
2067
2068 return true;
2069 }
2070
2071 // Number of audio bytes to transmit in the current (micro)frame when IN
2072 // flow control is enabled. A fractional accumulator distributes the
2073 // sub-frame sample remainder over successive frames so the long-term average
2074 // matches the configured sample rate (e.g. alternating 176/180 bytes for
2075 // 44100 Hz stereo 16-bit, averaging 176.4 bytes = 44.1 samples per frame).
2077 if (audio->sample_rate_tx == 0 || audio->n_channels_tx == 0 ||
2078 audio->n_bytes_per_sample_tx == 0) {
2079 // Not enough info to size precisely: fall back to the max packet.
2080 return audio->ep_in_sz;
2081 }
2082 bool full_speed = backend().isFullSpeed();
2083 const uint32_t denom = full_speed ? 1000u : 8000u;
2084 const uint8_t iv = audio->interval_tx ? audio->interval_tx : 1;
2085 const uint32_t interval = full_speed ? iv : (1u << (iv - 1));
2086
2087 audio->tx_sample_acc += audio->sample_rate_tx * interval;
2088 const uint32_t samples = audio->tx_sample_acc / denom;
2089 audio->tx_sample_acc -= samples * denom;
2090
2091 uint32_t bytes =
2092 samples * audio->n_channels_tx * audio->n_bytes_per_sample_tx;
2093 if (bytes > audio->ep_in_sz) bytes = audio->ep_in_sz;
2094 return (uint16_t)bytes;
2095 }
2096
2097};
2098
2099} // namespace audio_tools
#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 USB_DESCR_MAX_LEN
Definition USBAudioDeviceBase.h:49
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:180
AudioInfo info
Definition BaseStream.h:171
virtual void setAudioInfo(AudioInfo newInfo) override
Defines the input AudioInfo.
Definition BaseStream.h:128
virtual int readArray(T data[], int len)
reads multiple values
Definition Buffers.h:34
virtual void reset()=0
clears the buffer
virtual int writeArray(const T data[], int len)
Fills the buffer data.
Definition Buffers.h:56
virtual size_t size()=0
virtual int available()=0
provides the number of entries that are available to read
USB Audio Class 2.0 descriptor generator.
Definition USBAudio2DescriptorBuilder.h:29
const uint16_t buildFullDescriptor(uint8_t *desc)
Definition USBAudio2DescriptorBuilder.h:53
static constexpr uint8_t ENTITY_FU2
second Feature Unit (RXTX)
Definition USBAudio2DescriptorBuilder.h:39
static constexpr uint8_t ENTITY_FU1
first Feature Unit
Definition USBAudio2DescriptorBuilder.h:36
int audioFunctionsCount() const
Definition USBAudio2DescriptorBuilder.h:144
static constexpr uint8_t ENTITY_CLOCK
Definition USBAudio2DescriptorBuilder.h:34
uint16_t calcPacketSizeForRate(uint32_t rate) const
Definition USBAudio2DescriptorBuilder.h:162
uint16_t calcMaxPacketSize() const
Definition USBAudio2DescriptorBuilder.h:174
bool enableFeedbackEp() const
Definition USBAudio2DescriptorBuilder.h:153
Abstract seam for every raw USB-stack call the UAC2 driver (USBAudioDeviceBase) needs....
Definition USBAudioBackend.h:160
virtual bool isoActivateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep)=0
virtual bool controlTransfer(uint8_t rhport, const UsbSetupPacket &request, uint8_t *buffer, uint16_t length)=0
Complete a GET/SET control transfer's data stage with buffer.
bool isFullSpeed() const
Convenience: speed() == UsbSpeed::Full.
Definition USBAudioBackend.h:218
virtual bool claimEndpoint(uint8_t rhport, uint8_t ep_addr)=0
virtual bool closeEndpoint(uint8_t rhport, uint8_t ep_addr)=0
virtual bool openEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &ep)=0
virtual bool controlStatus(uint8_t rhport, const UsbSetupPacket &request)=0
Send a zero-length-packet status acknowledgement.
virtual bool usesIsoAlloc() const =0
virtual bool transfer(uint8_t rhport, uint8_t ep_addr, uint8_t *buffer, uint16_t length)=0
bool isHighSpeed() const
Convenience: speed() == UsbSpeed::High.
Definition USBAudioBackend.h:220
virtual bool clearStall(uint8_t rhport, uint8_t ep_addr)=0
Clear a halted/stalled condition on an endpoint.
virtual bool mounted() const =0
True once the device has completed USB enumeration.
virtual void enableSof(uint8_t rhport, bool enable)=0
virtual bool isoAllocEndpoint(uint8_t rhport, uint8_t ep_addr, uint16_t max_packet_size)=0
USB Audio Device class for audio streaming over USB.
Definition USBAudioDeviceBase.h:68
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:686
bool begin(const USBAudioConfig &cfg)
Apply a config and start the USB audio device.
Definition USBAudioDeviceBase.h:269
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request)> tud_audio_set_itf_cb_
Definition USBAudioDeviceBase.h:667
void setRxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for RX done events.
Definition USBAudioDeviceBase.h:437
volatile uint32_t xfer_cb_rx_count_
Definition USBAudioDeviceBase.h:640
volatile uint32_t xfer_cb_tx_count_
Definition USBAudioDeviceBase.h:638
USBAudioDeviceBase(USBAudioConfig cfg)
Constructor which provides configuration at construction time.
Definition USBAudioDeviceBase.h:196
bool audiod_control_xfer_cb(uint8_t rhport, uint8_t stage, UsbSetupPacket const &request)
Definition USBAudioDeviceBase.h:1099
bool handleInterfaceRequest(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1384
void audiod_sof_isr(uint8_t rhport, uint32_t frame_count)
Definition USBAudioDeviceBase.h:1252
bool audiod_fb_send(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:1655
bool audiod_set_interface(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1928
void onVolumeChanged(uint8_t channel) override
Notify the host about a device-initiated volume change.
Definition USBAudioDeviceBase.h:755
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for entity requests.
Definition USBAudioDeviceBase.h:447
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_itf_cb_
Definition USBAudioDeviceBase.h:674
bool isFeatureUnit(uint8_t id) const
Returns true if the given entity ID is a Feature Unit (FU1 or FU2).
Definition USBAudioDeviceBase.h:768
void setAudioFeedbackFormatCorrectionCallback(std::function< bool(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for audio feedback format correction events.
Definition USBAudioDeviceBase.h:521
uint16_t audiod_open(uint8_t rhport, UsbInterfaceDescriptorView const &itf_desc, uint8_t const *raw_desc, uint16_t max_len)
Definition USBAudioDeviceBase.h:957
uint32_t getTxSampleRate() const
TX sample rate parsed from the descriptor (must be non-zero for flow control).
Definition USBAudioDeviceBase.h:618
std::function< void(USBAudioDeviceBase *, uint8_t func_id)> fb_done_cb_
Definition USBAudioDeviceBase.h:663
void openEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, const UsbEndpointDescriptorView &desc_ep, uint8_t const *p_desc_for_params)
Definition USBAudioDeviceBase.h:1756
void closeEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1708
void setGetReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for GET requests on an endpoint.
Definition USBAudioDeviceBase.h:400
void setTxDoneCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, audiod_function_t *, uint16_t)> cb)
Register a callback for TX done events.
Definition USBAudioDeviceBase.h:427
bool isStreamingActiveTx() const override
Returns true if the host has opened the IN (capture) stream.
Definition USBAudioDeviceBase.h:363
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:1619
static USBAudioDeviceBase * s_active_
Definition USBAudioDeviceBase.h:707
virtual USBAudioBackend & backend()=0
Returns the backend used for all raw USB-stack calls (TinyUSB today, or a future native-HAL backend)....
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &)> get_req_ep_cb_
Definition USBAudioDeviceBase.h:660
int16_t manual_feedback_percent_
Definition USBAudioDeviceBase.h:696
uint16_t getDescLen(uint8_t fn) const
Definition USBAudioDeviceBase.h:802
void audiod_parse_flow_control_params(audiod_function_t *audio, uint8_t const *p_desc)
Definition USBAudioDeviceBase.h:1533
void setIntDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for interrupt done events.
Definition USBAudioDeviceBase.h:418
volatile uint32_t tx_fifo_read_total_
Definition USBAudioDeviceBase.h:639
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request)> tud_audio_set_itf_close_EP_cb_
Definition USBAudioDeviceBase.h:682
uint32_t getRxXferCount() const
Number of times audiod_xfer_cb fired for the OUT endpoint.
Definition USBAudioDeviceBase.h:600
void serviceUSB() override=0
Process pending USB events on platforms where the application drives the stack (RP2040,...
void setItfCloseEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for interface close endpoint events.
Definition USBAudioDeviceBase.h:500
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *, uint16_t bytes)> tx_done_cb_
Definition USBAudioDeviceBase.h:649
void audiod_init(void)
Definition USBAudioDeviceBase.h:907
volatile uint32_t rx_total_bytes_
Definition USBAudioDeviceBase.h:641
void onMuteChanged(uint8_t channel) override
Notify the host about a device-initiated mute change.
Definition USBAudioDeviceBase.h:761
bool activateEndpoint(uint8_t rhport, const UsbEndpointDescriptorView &desc_ep, UsbDir dir=UsbDir::In)
Definition USBAudioDeviceBase.h:1740
bool tx_xfer_armed_
Definition USBAudioDeviceBase.h:637
void setGetReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for GET requests on the interface.
Definition USBAudioDeviceBase.h:390
bool handleClockSourceGet(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1273
volatile uint16_t tx_frame_bytes_last_
Definition USBAudioDeviceBase.h:643
uint32_t getRxTotalBytes() const
Total bytes received from host via OUT endpoint.
Definition USBAudioDeviceBase.h:602
bool begin()
(Re-)start the USB audio device with the current config.
Definition USBAudioDeviceBase.h:286
void setFeedbackPercent(int percent)
Overrides the automatic (FIFO_COUNT) feedback computation with a fixed value driven by the caller,...
Definition USBAudioDeviceBase.h:541
bool handleEndpointRequest(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1399
uint8_t getAudioCount() const
Returns the number of audio functions (always 1).
Definition USBAudioDeviceBase.h:381
bool audiod_deinit(void)
Definition USBAudioDeviceBase.h:939
void setFeedbackAutomatic()
Reverts setFeedbackPercent() and restores the automatic (FIFO_COUNT) feedback computation.
Definition USBAudioDeviceBase.h:550
bool mounted() override
Returns true if the device is mounted by the USB host.
Definition USBAudioDeviceBase.h:384
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:780
bool audiod_calc_tx_packet_sz(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2021
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> req_entity_cb_
Definition USBAudioDeviceBase.h:664
void openEpOut(uint8_t rhport, audiod_function_t *audio, uint8_t itf, const UsbEndpointDescriptorView &desc_ep)
Definition USBAudioDeviceBase.h:1777
void setFbDoneCallback(std::function< void(USBAudioDeviceBase *, uint8_t)> cb)
Register a callback for feedback done events.
Definition USBAudioDeviceBase.h:410
volatile uint32_t rx_dropped_bytes_
Definition USBAudioDeviceBase.h:642
void setTudAudioSetItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:456
bool audiod_control_complete(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1110
USBAudioDeviceBase()
Default Constructor.
Definition USBAudioDeviceBase.h:193
void setReqEpCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for endpoint set requests.
Definition USBAudioDeviceBase.h:489
bool audiod_verify_entity_exists(uint8_t itf, uint8_t entityID, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1449
audio_feedback_method_t
Methods for USB audio feedback endpoint operation.
Definition USBAudioDeviceBase.h:87
@ AUDIO_FEEDBACK_METHOD_FIFO_COUNT
Definition USBAudioDeviceBase.h:92
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED
Definition USBAudioDeviceBase.h:89
@ AUDIO_FEEDBACK_METHOD_DISABLED
Definition USBAudioDeviceBase.h:88
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2
Definition USBAudioDeviceBase.h:91
@ AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT
Definition USBAudioDeviceBase.h:90
uint32_t getRxDroppedBytes() const
Definition USBAudioDeviceBase.h:606
uint16_t getDescriptor(uint8_t *desc)
Returns the audio-function descriptor block for use in tud_descriptor_configuration_cb().
Definition USBAudioDeviceBase.h:579
static constexpr size_t getResetSize()
Definition USBAudioDeviceBase.h:811
void setSampleRate(uint32_t rate)
Device-initiated sample rate change.
Definition USBAudioDeviceBase.h:740
void tud_audio_feedback_interval_isr(uint8_t func_id, uint32_t, uint8_t frame_shift)
Definition USBAudioDeviceBase.h:818
void setReqEntityCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for entity set requests.
Definition USBAudioDeviceBase.h:467
void audiod_reset(uint8_t rhport)
Definition USBAudioDeviceBase.h:943
std::function< bool(USBAudioDeviceBase *, uint8_t func_id)> tud_audio_feedback_format_correction_cb_
Definition USBAudioDeviceBase.h:689
bool openEndpointsForAltSetting(uint8_t rhport, audiod_function_t *audio, uint8_t func_id, uint8_t itf, uint8_t alt)
Definition USBAudioDeviceBase.h:1859
bool isInterruptEpEnabled() const
Returns true if the interrupt endpoint is enabled.
Definition USBAudioDeviceBase.h:378
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_entity_cb_
Definition USBAudioDeviceBase.h:670
uint32_t getTxFifoReadTotal() const
Definition USBAudioDeviceBase.h:609
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:589
std::vector< uint16_t > desc_len_
Definition USBAudioDeviceBase.h:699
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, audiod_function_t *, uint16_t xferred_bytes)> rx_done_cb_
Definition USBAudioDeviceBase.h:652
bool isEpInFlowControlEnabled() const
Returns true if IN endpoint flow control is enabled. When on, the per-frame isochronous packet size i...
Definition USBAudioDeviceBase.h:356
void end()
Stop audio streaming and clear buffers. Does not disconnect USB.
Definition USBAudioDeviceBase.h:554
USBAudioConfig active_config_
Definition USBAudioDeviceBase.h:645
bool isTxXferArmed() const
True if the initial isochronous IN transfer was armed successfully.
Definition USBAudioDeviceBase.h:595
bool audiod_control_request(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1414
uint32_t getTxXferCount() const
Number of times audiod_xfer_cb fired for the IN endpoint.
Definition USBAudioDeviceBase.h:598
USBAudioConfig defaultConfig(RxTxMode mode=RXTX_MODE)
Returns a default configuration pre-filled for the requested direction (RX_MODE, TX_MODE,...
Definition USBAudioDeviceBase.h:205
bool audiod_get_interface(uint8_t rhport, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1635
void setupFeedback(audiod_function_t *audio, uint8_t func_id, uint8_t alt)
Definition USBAudioDeviceBase.h:1801
void closeEpIn(uint8_t rhport, audiod_function_t *audio, uint8_t itf, UsbSetupPacket const &p_request)
Definition USBAudioDeviceBase.h:1691
virtual int getActualCore() const
Returns the actual core on which the call is running (for RP2040)
Definition USBAudioDeviceBase.h:634
static USBAudioDeviceBase & activeInstance()
Returns the most-recently-constructed instance (base or subclass).
Definition USBAudioDeviceBase.h:345
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:1579
uint8_t getTxInterval() const
TX isochronous interval (bInterval) parsed from the descriptor.
Definition USBAudioDeviceBase.h:630
uint16_t audiod_tx_packet_size_fc(audiod_function_t *audio)
Definition USBAudioDeviceBase.h:2076
void setAudioInfo(AudioInfo info) override
Change the sample rate and notify the host.
Definition USBAudioDeviceBase.h:227
bool handleEntityRequest(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t entityID)
Definition USBAudioDeviceBase.h:1350
volatile uint32_t tx_xferred_last_
Definition USBAudioDeviceBase.h:644
std::vector< uint16_t > ctrl_buf_sz_
Definition USBAudioDeviceBase.h:701
uint16_t getTxFrameBytesLast() const
Definition USBAudioDeviceBase.h:613
std::function< void(USBAudioDeviceBase *, uint8_t rhport)> int_done_cb_
Definition USBAudioDeviceBase.h:647
uint16_t packetSize() const
Definition USBAudioDeviceBase.h:807
bool isFeedbackEpEnabled() const
Returns true if the feedback endpoint is enabled. Only meaningful in pure RX (OUT-only) mode: with an...
Definition USBAudioDeviceBase.h:351
void setReqItfCallback(std::function< bool(USBAudioDeviceBase *, uint8_t, UsbSetupPacket const &, uint8_t *)> cb)
Register a callback for interface set requests.
Definition USBAudioDeviceBase.h:478
void openEpFeedback(audiod_function_t *audio, const UsbEndpointDescriptorView &desc_ep)
Definition USBAudioDeviceBase.h:1794
audio_format_type_t
Supported USB audio format types.
Definition USBAudioDeviceBase.h:75
@ AUDIO_FORMAT_TYPE_III
Definition USBAudioDeviceBase.h:78
@ AUDIO_FORMAT_TYPE_II
Definition USBAudioDeviceBase.h:77
@ AUDIO_FORMAT_TYPE_I
Definition USBAudioDeviceBase.h:76
uint8_t int_notify_buf_[6]
Definition USBAudioDeviceBase.h:690
bool audiod_verify_itf_exists(uint8_t itf, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1509
bool isStreamingActiveRx() const override
Returns true if the host has opened the OUT (playback) stream.
Definition USBAudioDeviceBase.h:371
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:510
bool audiod_set_fb_params_freq(audiod_function_t *audio, uint32_t sample_freq, uint32_t mclk_freq)
Definition USBAudioDeviceBase.h:1984
bool audiod_verify_ep_exists(uint8_t ep, uint8_t *func_id)
Definition USBAudioDeviceBase.h:1482
static uint8_t log2Floor(uint32_t value)
Definition USBAudioDeviceBase.h:1978
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &)> get_req_itf_cb_
Definition USBAudioDeviceBase.h:656
std::vector< audiod_function_t > audiod_fct_
Definition USBAudioDeviceBase.h:703
std::function< bool(USBAudioDeviceBase *, uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *pBuff)> tud_audio_set_req_ep_cb_
Definition USBAudioDeviceBase.h:678
uint16_t getCtrlBufSz(uint8_t fn) const
Definition USBAudioDeviceBase.h:797
uint32_t getTxXferredLast() const
Definition USBAudioDeviceBase.h:616
uint8_t getTxBytesPerSample() const
TX bytes per sample parsed from the descriptor.
Definition USBAudioDeviceBase.h:626
bool audiod_xfer_cb(uint8_t rhport, uint8_t ep_addr, UsbXferResult result, uint32_t xferred_bytes)
TODO refactor control request handling to separate function and reduce nesting.
Definition USBAudioDeviceBase.h:1186
static bool isPowerOfTwo(uint32_t value)
Definition USBAudioDeviceBase.h:1974
uint8_t getTxChannels() const
TX channel count parsed from the descriptor.
Definition USBAudioDeviceBase.h:622
uint16_t audioPacketSize() const
One isochronous USB packet size in bytes (same formula as the descriptor builder).
Definition USBAudioDeviceBase.h:566
bool handleFeatureUnitGet(uint8_t rhport, UsbSetupPacket const &p_request, uint8_t *cb)
Definition USBAudioDeviceBase.h:1320
USBAudio2DescriptorBuilder descr_builder
Definition USBAudioDeviceBase.h:646
Role-independent front end shared by USB Audio devices (USBAudioDeviceBase, aliased as USBAudioStream...
Definition USBAudioStreamBase.h:39
std::vector< float > volume_
Definition USBAudioStreamBase.h:229
bool isEpInEnabled() const
Returns true if the IN endpoint is enabled.
Definition USBAudioStreamBase.h:42
int core
Definition USBAudioStreamBase.h:224
bool isMute(uint8_t channel=0) const
Returns the current mute state for the given channel.
Definition USBAudioStreamBase.h:80
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 USBAudioStreamBase.h:311
virtual BaseBuffer< uint8_t > & bufferTx()=0
Returns the TX audio buffer. Must be overridden by subclasses.
USBAudioConfig config_
Definition USBAudioStreamBase.h:235
bool is_started_
Definition USBAudioStreamBase.h:221
bool rx_primed_
Definition USBAudioStreamBase.h:225
bool setVolume(float volume) override
sets the volume for the master channel (channel 0)
Definition USBAudioStreamBase.h:56
bool is_active_
Definition USBAudioStreamBase.h:226
void setupVolumeState()
(Re)sizes the volume and mute state: master (index 0) + one per channel
Definition USBAudioStreamBase.h:257
float volume() override
gets the volume for the master channel (channel 0)
Definition USBAudioStreamBase.h:53
static bool isValidBitsPerSample(uint8_t bps)
Definition USBAudioStreamBase.h:326
virtual BaseBuffer< uint8_t > & bufferRx()=0
Returns the RX audio buffer. Must be overridden by subclasses.
std::function< void(uint32_t)> sample_rate_cb_
Definition USBAudioStreamBase.h:233
virtual bool beginUSB()=0
Override in platform subclasses to start the USB stack.
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 USBAudioStreamBase.h:319
virtual void resizeBuffers()=0
Resize the platform audio buffers.
void notifyStreamingState()
Definition USBAudioStreamBase.h:330
bool isEpOutEnabled() const
Returns true if the OUT endpoint is enabled.
Definition USBAudioStreamBase.h:45
bool configChanged(const USBAudioConfig &n)
Definition USBAudioStreamBase.h:254
bool setMute(bool m, uint8_t channel=0)
Set the mute state for a channel and notify the USB peer.
Definition USBAudioStreamBase.h:88
RxTxMode
The Microcontroller is the Audio Source (TX_MODE) or Audio Sink (RX_MODE). RXTX_MODE is Source and Si...
Definition AudioTypes.h:26
@ RXTX_MODE
Definition AudioTypes.h:26
@ TX_MODE
Definition AudioTypes.h:26
@ RX_MODE
Definition AudioTypes.h:26
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition LMSEchoCancellationStream.h:6
static constexpr uint8_t kAudioSubclassControl
Definition USBAudioConstants.h:22
uint8_t descSubtype(uint8_t const *p)
Definition USBAudioBackend.h:107
static constexpr uint8_t kAudioCsCtrlSamFreq
Definition USBAudioConstants.h:45
uint32_t descU32(uint8_t const *p)
Definition USBAudioBackend.h:113
static constexpr uint16_t kAudioDescIadLen
Definition USBAudioConstants.h:55
UsbDir
USB transfer direction, mirrors TinyUSB's TUSB_DIR_OUT/TUSB_DIR_IN (0/1).
Definition USBAudioBackend.h:8
static constexpr uint8_t kUsbClassAudio
Definition USBAudioConstants.h:21
static constexpr uint8_t kAudioIntProtocolCodeV2
Definition USBAudioConstants.h:24
UsbEndpointDescriptorView decodeEndpoint(uint8_t const *p)
Definition USBAudioBackend.h:121
static constexpr uint8_t kAudioCsReqCur
Definition USBAudioConstants.h:39
UsbInterfaceDescriptorView decodeInterface(uint8_t const *p)
Definition USBAudioBackend.h:136
static constexpr uint8_t kAudioCsAcOutputTerminal
Definition USBAudioConstants.h:29
UsbXferResult
Definition USBAudioBackend.h:24
static constexpr uint8_t kControlStageData
Definition USBAudioConstants.h:63
static constexpr uint8_t AUDIO_FU_CTRL_VOLUME
Definition USBAudioConstants.h:51
static uint8_t u16Low(uint16_t v)
Definition USBAudioConstants.h:65
static constexpr uint8_t kUsbDescTypeEndpoint
Definition USBAudioConstants.h:15
uint16_t descU16(uint8_t const *p)
Definition USBAudioBackend.h:109
uint8_t descType(uint8_t const *p)
Definition USBAudioBackend.h:106
static uint8_t u16High(uint16_t v)
Definition USBAudioConstants.h:66
uint8_t const * descNext(uint8_t const *p)
Definition USBAudioBackend.h:105
static constexpr uint8_t kAudioCsCtrlClkValid
Definition USBAudioConstants.h:46
static constexpr uint8_t AUDIO_FU_CTRL_MUTE
Definition USBAudioConstants.h:50
static constexpr uint16_t kAudioTermTypeUsbStreaming
Definition USBAudioConstants.h:53
static constexpr uint8_t kAudioCsAcInputTerminal
Definition USBAudioConstants.h:28
uint32_t highestSupportedSampleRate(const USBAudioConfig &cfg)
Definition USBAudioConfig.h:305
static constexpr uint8_t kUsbDescTypeCsInterface
Definition USBAudioConstants.h:17
static constexpr uint8_t kUsbDescTypeInterface
Definition USBAudioConstants.h:14
static constexpr uint8_t kAudioCsAsGeneral
Definition USBAudioConstants.h:33
static constexpr uint8_t kAudioCsAsFormatType
Definition USBAudioConstants.h:34
static constexpr uint8_t kControlStageSetup
Definition USBAudioConstants.h:62
static constexpr uint8_t kAudioCsReqRange
Definition USBAudioConstants.h:40
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:56
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:58
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:60
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:62
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:117
bool enable_interrupt_ep
Definition USBAudioConfig.h:197
bool enable_multi_sample_rate
Definition USBAudioConfig.h:177
std::vector< uint32_t > supported_sample_rates
Definition USBAudioConfig.h:191
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:120
uint8_t itf_num_ac
Definition USBAudioConfig.h:140
bool enable_ep_in_flow_control
Definition USBAudioConfig.h:204
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:121
Parameters for audio feedback endpoint configuration.
Definition USBAudioDeviceBase.h:180
uint8_t method
Definition USBAudioDeviceBase.h:181
struct audio_tools::USBAudioDeviceBase::audio_feedback_params_t::@6::@8 frequency
uint32_t mclk_freq
Definition USBAudioDeviceBase.h:186
uint32_t sample_freq
Definition USBAudioDeviceBase.h:182
Internal structure representing a USB audio function instance.
Definition USBAudioDeviceBase.h:101
uint8_t compute_method
Definition USBAudioDeviceBase.h:132
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3::@4 fixed
uint8_t ep_fb
Definition USBAudioDeviceBase.h:123
bool mounted
Definition USBAudioDeviceBase.h:125
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2 feedback
uint8_t ep_out_as_intf_num
Definition USBAudioDeviceBase.h:122
uint16_t rate_const[2]
Definition USBAudioDeviceBase.h:145
uint8_t ep_in_as_intf_num
Definition USBAudioDeviceBase.h:118
uint32_t tx_sample_acc
Definition USBAudioDeviceBase.h:159
std::vector< uint8_t > ctrl_buf
Definition USBAudioDeviceBase.h:167
uint32_t fifo_lvl_avg
Definition USBAudioDeviceBase.h:143
uint8_t interval_tx
Definition USBAudioDeviceBase.h:152
float float_const
Definition USBAudioDeviceBase.h:136
uint16_t ep_in_sz
Definition USBAudioDeviceBase.h:116
uint16_t ep_out_sz
Definition USBAudioDeviceBase.h:120
uint8_t bclock_id_tx
Definition USBAudioDeviceBase.h:151
uint8_t ctrl_buf_sz
Definition USBAudioDeviceBase.h:166
std::vector< uint32_t > fb_buf
Definition USBAudioDeviceBase.h:171
uint16_t fifo_lvl_thr
Definition USBAudioDeviceBase.h:144
uint8_t n_bytes_per_sample_tx
Definition USBAudioDeviceBase.h:155
struct audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3::@5 fifo_count
uint8_t ep_int
Definition USBAudioDeviceBase.h:124
uint16_t desc_length
Definition USBAudioDeviceBase.h:126
uint32_t max_value
Definition USBAudioDeviceBase.h:130
std::vector< uint8_t > lin_buf_in
Definition USBAudioDeviceBase.h:170
uint32_t min_value
Definition USBAudioDeviceBase.h:129
uint32_t sample_rate_tx
Definition USBAudioDeviceBase.h:149
bool format_correction
Definition USBAudioDeviceBase.h:133
audio_format_type_t format_type_tx
Definition USBAudioDeviceBase.h:153
uint8_t frame_shift
Definition USBAudioDeviceBase.h:131
uint32_t nom_value
Definition USBAudioDeviceBase.h:142
uint32_t mclk_freq
Definition USBAudioDeviceBase.h:139
std::vector< uint8_t > lin_buf_out
Definition USBAudioDeviceBase.h:169
union audio_tools::USBAudioDeviceBase::audiod_function_t::@2::@3 compute
uint8_t power_of_2
Definition USBAudioDeviceBase.h:135
volatile uint8_t ep_out
Definition USBAudioDeviceBase.h:119
uint8_t rhport
Definition USBAudioDeviceBase.h:102
uint8_t n_channels_tx
Definition USBAudioDeviceBase.h:154
uint32_t sample_freq
Definition USBAudioDeviceBase.h:138
uint8_t const * p_desc
Definition USBAudioDeviceBase.h:103
uint16_t packet_sz_tx[3]
Definition USBAudioDeviceBase.h:150
uint32_t value
Definition USBAudioDeviceBase.h:128
UsbEndpointDescriptorView ep_out_view
Definition USBAudioDeviceBase.h:164
std::vector< uint8_t > alt_setting
Definition USBAudioDeviceBase.h:168
volatile uint8_t ep_in
Definition USBAudioDeviceBase.h:115
Backend-agnostic mirror of tusb_desc_endpoint_t (USB 2.0 spec Table 9-13). wMaxPacketSize is kept in ...
Definition USBAudioBackend.h:86
uint8_t bEndpointAddress
Definition USBAudioBackend.h:87
UsbXferType xferType
Definition USBAudioBackend.h:88
UsbDir direction() const
Definition USBAudioBackend.h:94
uint16_t packetSize() const
Definition USBAudioBackend.h:97
uint8_t usage
Definition USBAudioBackend.h:90
uint8_t bInterval
Definition USBAudioBackend.h:91
Backend-agnostic mirror of tusb_desc_interface_t — only the fields the UAC2 driver reads (USB 2....
Definition USBAudioBackend.h:66
uint8_t bInterfaceSubClass
Definition USBAudioBackend.h:71
uint8_t bInterfaceNumber
Definition USBAudioBackend.h:67
uint8_t bAlternateSetting
Definition USBAudioBackend.h:68
uint8_t bInterfaceProtocol
Definition USBAudioBackend.h:72
uint8_t bNumEndpoints
Definition USBAudioBackend.h:69
uint8_t bInterfaceClass
Definition USBAudioBackend.h:70
Backend-agnostic mirror of the standard 8-byte USB SETUP packet (TinyUSB's tusb_control_request_t)....
Definition USBAudioBackend.h:34
uint8_t bRequest
Definition USBAudioBackend.h:36
Recipient recipient() const
Definition USBAudioBackend.h:51
uint16_t wValue
Definition USBAudioBackend.h:37
UsbDir direction() const
Definition USBAudioBackend.h:50
uint16_t wIndex
Definition USBAudioBackend.h:38
Type type() const
Definition USBAudioBackend.h:49