arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioHostBase.h
Go to the documentation of this file.
1#pragma once
2
3#include <cstdint>
4#include <cstring>
5#include <vector>
6
11
12namespace audio_tools {
13
49 public:
50 USBAudioHostBase() = default;
51
54
60 switch (mode) {
61 case RX_MODE:
62 cfg.enable_ep_in = true;
63 cfg.enable_ep_out = false;
64 break;
65 case TX_MODE:
66 cfg.enable_ep_in = false;
67 cfg.enable_ep_out = true;
68 break;
69 case RXTX_MODE:
70 cfg.enable_ep_in = true;
71 cfg.enable_ep_out = true;
72 break;
73 default:
74 break;
75 }
76 // feedback is handled automatically on the host side
77 cfg.enable_feedback_ep = false;
78 return cfg;
79 }
80
84 void setAudioInfo(AudioInfo info) override {
86 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
88 return;
89 }
90 if (!is_started_) {
92 return;
93 }
96 LOGE("Only the sample rate can be changed after begin()");
97 }
100 // before the mount the new rate is used by the format negotiation
101 if (mounted_) reconfigure_pending_ = true;
102 }
103 }
104
107 bool begin(const USBAudioConfig& cfg) {
108 if (!is_started_) {
109 config_ = cfg;
110 } else if (!configChanged(cfg)) {
111 return true;
112 } else {
113 // only the sample rate can be changed at runtime
117 return true;
118 }
119 return begin();
120 }
121
124 bool begin() {
125 if (is_started_) return true;
127 LOGE("Unsupported bits_per_sample: %d (must be 16, 24, or 32)",
129 return false;
130 }
132 LOGE("Neither RX (enable_ep_in) nor TX (enable_ep_out) is enabled");
133 return false;
134 }
136 info = config_;
142 rx_primed_ = false;
143 if (!beginUSB()) {
144 LOGE("beginUSB failed");
145 return false;
146 }
147 if (restart_pending_) {
148 restart_pending_ = false;
149 reconfiguring_ = false;
151 }
152 is_started_ = true;
153 is_active_ = true;
154 return true;
155 }
156
159 void end() {
160 is_started_ = false;
161 bool was_streaming = isStreamingActive();
163 if (tx_.active) {
165 if (tx_.alt().has_fb)
167 }
168 backend().end();
169 rx_.active = false;
170 tx_.active = false;
171 mounted_ = false;
173 ctrl_busy_ = false;
174 // a still attached device is reopened by the next begin()
176 bufferTx().reset();
177 bufferRx().reset();
178 if (was_streaming) notifyStreamingState();
179 }
180
182 bool mounted() override { return mounted_; }
183
185 bool isStreamingActiveTx() const override { return tx_.active; }
186
189 bool isStreamingActiveRx() const override { return rx_.active; }
190
192 bool hasMicrophone() const { return mounted_ && rx_.present(); }
193
195 bool hasSpeaker() const { return mounted_ && tx_.present(); }
196
198 AudioInfo audioInfoRx() const { return rx_info_; }
199
201 AudioInfo audioInfoTx() const { return tx_info_; }
202
204 uint16_t vid() const { return vid_; }
205
207 uint16_t pid() const { return pid_; }
208
210 uint8_t uacVersion() const { return uac_version_; }
211
213 uint32_t getTxXferCount() const { return tx_.xfer_count; }
216 uint32_t getRxXferCount() const { return rx_.xfer_count; }
218 uint32_t getRxTotalBytes() const { return rx_.total_bytes; }
220 uint32_t getRxDroppedBytes() const { return rx_.dropped_bytes; }
222 uint32_t getTxUnderflowBytes() const { return tx_.dropped_bytes; }
223
226 uint16_t audioPacketSize() const { return buffer_bytes_; }
227
228 protected:
229 static constexpr uint8_t kMaxRates = 16;
230 static constexpr uint8_t kMaxChannels = 8;
231 static constexpr uint16_t kCtrlBufSize = 2 + 12 * 24;
232
234 struct AltSetting {
235 uint8_t itf = 0;
236 uint8_t alt = 0;
237 uint8_t terminal_link = 0;
238 bool is_pcm = true;
239 uint8_t channels = 0;
240 uint8_t subslot = 0; // bytes per sample container
241 uint8_t bits = 0; // bit resolution
242 bool has_ep = false;
244 bool has_fb = false;
246 // UAC1: rates of the format descriptor; UAC2: from the clock source
247 bool continuous = false;
248 uint32_t rates[kMaxRates] = {};
249 uint8_t rate_count = 0;
250 };
251
253 struct RateRange {
254 uint32_t min = 0;
255 uint32_t max = 0;
256 uint32_t res = 0;
257 };
258
260 struct Stream {
261 std::vector<AltSetting> alts;
262 int selected = -1;
263 uint32_t sample_rate = 0;
264 uint8_t fu_id = 0;
265 uint8_t clock_id = 0;
266 // Feature Unit capabilities: bit n = control available for channel n
267 uint16_t fu_mute_mask = 0;
268 uint16_t fu_volume_mask = 0;
270 int16_t vol_max = 0;
271 bool active = false;
272 // TX pacing: exact accumulator (sample_rate per packets_per_sec) for the
273 // nominal rate, 16.16 samples per packet for explicit feedback
274 uint32_t acc = 0;
275 uint32_t acc_fb = 0;
276 uint32_t nominal = 0; // 16.16 samples per packet
277 volatile uint32_t feedback = 0;
278 uint16_t packets_per_sec = 1000;
279 bool primed = false;
280 // statistics
281 uint32_t xfer_count = 0;
282 uint32_t total_bytes = 0;
283 uint32_t dropped_bytes = 0;
284
285 bool present() const { return !alts.empty(); }
286 AltSetting& alt() { return alts[selected < 0 ? 0 : selected]; }
287 const AltSetting& alt() const { return alts[selected < 0 ? 0 : selected]; }
288 uint16_t frameBytes() const {
289 return selected < 0 ? 0
290 : (uint16_t)alt().channels * (uint16_t)alt().subslot;
291 }
292 void reset() {
293 alts.clear();
294 selected = -1;
295 sample_rate = 0;
296 fu_id = clock_id = 0;
299 vol_max = 0;
300 active = false;
301 acc = acc_fb = nominal = 0;
302 feedback = 0;
303 primed = false;
304 }
305 };
306
308 struct Entity {
309 uint8_t id = 0;
310 uint8_t subtype = 0;
311 uint16_t terminal_type = 0;
312 uint8_t clock_source = 0; // bCSourceID (UAC2 terminals)
313 uint8_t sources[4] = {};
314 uint8_t source_count = 0;
315 uint16_t mute_mask = 0;
316 uint16_t volume_mask = 0;
317 };
318
320 enum class Step : uint8_t {
321 Idle,
322 StopRx,
323 StopTx,
328 Select,
331 OpenRx,
337 OpenTx,
341 Done
342 };
343
344 uint8_t daddr_ = 0;
345 uint16_t vid_ = 0;
346 uint16_t pid_ = 0;
347 uint8_t uac_version_ = 0;
348 uint8_t itf_num_ac_ = 0;
349 volatile bool attached_ = false;
350 volatile bool mounted_ = false;
351 volatile bool configured_ = false;
354 std::vector<Entity> entities_;
355 std::vector<RateRange> clock_ranges_rx_;
356 std::vector<RateRange> clock_ranges_tx_;
359 uint16_t buffer_bytes_ = 0;
360
361 // state machine
363 bool ctrl_busy_ = false;
364 bool ctrl_ok_ = false;
365 uint16_t ctrl_len_ = 0;
366 bool step_done_ = false;
367 bool in_run_ = false;
368 bool run_again_ = false;
369 bool reconfiguring_ = false;
370 uint32_t ctrl_start_ms_ = 0;
371 uint8_t ctrl_buf_[kCtrlBufSize] = {};
373 volatile bool reconfigure_pending_ = false;
374 bool restart_pending_ = false;
375 // runtime volume / mute requests (one flag per channel)
376 std::vector<uint8_t> volume_pending_;
377 std::vector<uint8_t> mute_pending_;
378 uint8_t pending_channel_ = 0;
379 bool pending_is_volume_ = false;
381
384
385 bool beginUSB() override { return backend().begin(this, config_); }
386
389 void processUSB() {
390 backend().task();
391 poll();
392 }
393
394 // ── USBAudioHostEvents ───────────────────────────────────────────────────
395
396 uint16_t onAttach(uint8_t daddr, uint16_t vid, uint16_t pid,
397 const uint8_t* desc, uint16_t len) override {
398 if (attached_) return 0; // we support only one audio function
399 if (len < 9 || descType(desc) != kUsbDescTypeInterface) return 0;
403 return 0;
404 if (config_.host_vid != 0 && config_.host_vid != vid) return 0;
405 if (config_.host_pid != 0 && config_.host_pid != pid) return 0;
406
407 resetDevice();
408 uint16_t consumed = parseConfiguration(desc, len);
409 if (!rx_.present() && !tx_.present()) {
410 LOGW("USB audio device %04x:%04x has no PCM streaming interface", vid,
411 pid);
412 resetDevice();
413 return consumed;
414 }
415 daddr_ = daddr;
416 vid_ = vid;
417 pid_ = pid;
418 attached_ = true;
419 LOGI("USB audio device %04x:%04x (UAC%d) attached: mic=%d speaker=%d",
421 return consumed;
422 }
423
424 void onConfigured(uint8_t daddr) override {
425 if (!attached_ || daddr != daddr_) return;
426 configured_ = true;
427 // the sequence is started from poll() so that we never issue a control
428 // request while the stack is still configuring other interfaces
429 reconfiguring_ = false;
431 }
432
433 void onDetach(uint8_t daddr) override {
434 if (!attached_ || daddr != daddr_) return;
435 bool was_streaming = isStreamingActive();
436 LOGI("USB audio device detached");
437 resetDevice();
438 if (was_streaming) notifyStreamingState();
439 }
440
441 void onControlComplete(bool ok, uint16_t len) override {
442 if (!ctrl_busy_) return; // late completion after a timeout
443 ctrl_busy_ = false;
444 ctrl_ok_ = ok;
445 ctrl_len_ = len;
446 step_done_ = true;
447 if (step_ != Step::Idle) {
448 run();
449 } else {
451 }
452 }
453
454 void onIsoIn(UsbHostStreamRole role, const uint8_t* data,
455 uint16_t len) override {
456 if (role == UsbHostStreamRole::Feedback) {
457 onFeedback(data, len);
458 return;
459 }
460 if (!rx_.active) return;
461 rx_.xfer_count++;
462 if (len == 0) return;
463 rx_.total_bytes += len;
464 int written = bufferRx().writeArray(data, len);
465 if (written < len) rx_.dropped_bytes += (len - written);
466 }
467
468 uint16_t onIsoOut(uint8_t* data, uint16_t max) override {
469 if (!tx_.active) return 0;
470 tx_.xfer_count++;
471 uint16_t frame = tx_.frameBytes();
472 if (frame == 0) return 0;
473 uint32_t samples = 0;
474 uint32_t fb = tx_.feedback;
475 if (fb != 0) {
476 tx_.acc_fb += fb;
477 samples = tx_.acc_fb >> 16;
478 tx_.acc_fb &= 0xFFFF;
479 } else {
481 samples = tx_.acc / tx_.packets_per_sec;
482 tx_.acc -= samples * tx_.packets_per_sec;
483 }
484 uint16_t bytes = (uint16_t)(samples * frame);
485 uint16_t max_aligned = (uint16_t)(max - (max % frame));
486 if (bytes > max_aligned) bytes = max_aligned;
487
488 // one-shot prime: wait until half of the buffer is filled
489 if (!tx_.primed) {
490 if (bufferTx().levelPercent() < 50.0f) {
491 memset(data, 0, bytes);
492 return bytes;
493 }
494 tx_.primed = true;
495 }
496
497 int avail = bufferTx().available();
498 uint16_t to_read = bytes;
499 if (avail < to_read) to_read = (uint16_t)(avail - (avail % frame));
500 int read = to_read > 0 ? bufferTx().readArray(data, to_read) : 0;
501 if (read < bytes) {
502 memset(data + read, 0, bytes - read);
503 tx_.dropped_bytes += (bytes - read);
504 }
505 return bytes;
506 }
507
508 // ── Volume / Mute propagation ────────────────────────────────────────────
509
510 void onVolumeChanged(uint8_t channel) override {
511 if (channel < volume_pending_.size()) volume_pending_[channel] = 1;
512 }
513
514 void onMuteChanged(uint8_t channel) override {
515 if (channel < mute_pending_.size()) mute_pending_[channel] = 1;
516 }
517
518 // ── Buffer sizing ────────────────────────────────────────────────────────
519
521 static uint16_t packetSizeFor(const AudioInfo& info) {
522 uint32_t samples = (info.sample_rate + 999) / 1000 + 1;
523 return (uint16_t)(samples * info.channels * (info.bits_per_sample / 8));
524 }
525
529 uint16_t needed = 0;
530 if (rx_.selected >= 0) needed = packetSizeFor(rx_info_);
531 if (tx_.selected >= 0) {
532 uint16_t tx = packetSizeFor(tx_info_);
533 if (tx > needed) needed = tx;
534 }
535 if (needed > buffer_bytes_) {
536 buffer_bytes_ = needed;
538 }
539 }
540
541 // ── Descriptor parsing ───────────────────────────────────────────────────
542
543 Entity* findEntity(uint8_t id) {
544 for (auto& e : entities_) {
545 if (e.id == id) return &e;
546 }
547 return nullptr;
548 }
549
552 uint16_t parseConfiguration(const uint8_t* desc, uint16_t len) {
553 const uint8_t* p = desc;
554 const uint8_t* end = desc + len;
556 uac_version_ = 0;
557 bool in_ac = true;
558 AltSetting* cur = nullptr;
559 AltSetting pending;
560 bool has_pending = false;
561 std::vector<AltSetting> all;
562
563 auto flush = [&]() {
564 if (has_pending && pending.has_ep && pending.is_pcm) all.push_back(pending);
565 has_pending = false;
566 cur = nullptr;
567 };
568
569 p = descNext(p);
570 while (p + 2 <= end && p[0] >= 2 && p + p[0] <= end) {
571 uint8_t type = descType(p);
572 if (type == kUsbDescTypeIad) break; // next function
573 if (type == kUsbDescTypeInterface && p[0] >= 9) {
575 if (itf.bInterfaceClass != kUsbClassAudio) break;
577 if (itf.bInterfaceNumber != itf_num_ac_) break; // next function
579 in_ac = false;
580 flush();
581 if (itf.bNumEndpoints > 0) {
582 pending = AltSetting();
583 pending.itf = itf.bInterfaceNumber;
584 pending.alt = itf.bAlternateSetting;
585 has_pending = true;
586 cur = &pending;
587 }
588 } else {
589 break; // e.g. MIDI streaming: not handled by us
590 }
591 } else if (type == kUsbDescTypeCsInterface) {
592 if (in_ac) {
594 } else if (cur) {
595 parseAsDescriptor(*cur, p);
596 }
597 } else if (type == kUsbDescTypeEndpoint && cur && p[0] >= 7) {
600 bool is_feedback = ep.usage == 1 ||
601 (cur->has_ep && ep.direction() == UsbDir::In &&
602 cur->ep.direction() == UsbDir::Out);
603 if (is_feedback) {
604 cur->has_fb = true;
605 cur->fb_ep = ep;
606 } else if (!cur->has_ep) {
607 cur->has_ep = true;
608 cur->ep = ep;
609 }
610 }
611 }
612 p = descNext(p);
613 }
614 flush();
615
616 for (auto& a : all) {
617 if (!isValidBitsPerSample(a.subslot * 8) || a.channels == 0) {
618 LOGI("Ignoring alt %d of itf %d: %d channels, %d bytes/sample", a.alt,
619 a.itf, a.channels, a.subslot);
620 continue;
621 }
622 bool is_rx = a.ep.direction() == UsbDir::In;
623 if (is_rx && !config_.enable_ep_in) continue;
624 if (!is_rx && !config_.enable_ep_out) continue;
625 Stream& s = is_rx ? rx_ : tx_;
626 // only one streaming interface per direction
627 if (s.present() && s.alts[0].itf != a.itf) continue;
628 s.alts.push_back(a);
629 }
630 resolveTopology(rx_, true);
631 resolveTopology(tx_, false);
632 return (uint16_t)(p - desc);
633 }
634
636 void parseAcDescriptor(const uint8_t* p) {
637 uint8_t subtype = descSubtype(p);
638 uint8_t n = p[0];
639 if (subtype == kAudioCsAcHeader && n >= 5) {
640 uac_version_ = descU16(p + 3) >= 0x0200 ? 2 : 1;
641 return;
642 }
643 if (n < 4) return;
644 bool v2 = uac_version_ == 2;
645 Entity e;
646 e.id = p[3];
647 e.subtype = subtype;
648 auto addSources = [&](uint8_t count_pos) {
649 if (n <= count_pos) return;
650 uint8_t cnt = p[count_pos];
651 for (uint8_t j = 0; j < cnt && e.source_count < 4 &&
652 (uint16_t)(count_pos + 1 + j) < n;
653 j++) {
654 e.sources[e.source_count++] = p[count_pos + 1 + j];
655 }
656 };
657 switch (subtype) {
658 case 0x02: // input terminal
659 if (n >= 8) {
660 e.terminal_type = descU16(p + 4);
661 if (v2) e.clock_source = p[7];
662 }
663 break;
664 case 0x03: // output terminal
665 if (n >= 8) {
666 e.terminal_type = descU16(p + 4);
667 e.sources[0] = p[7];
668 e.source_count = 1;
669 if (v2 && n >= 9) e.clock_source = p[8];
670 }
671 break;
672 case 0x04: // mixer unit
673 case 0x05: // selector unit
674 addSources(4);
675 break;
677 if (n >= 6) {
678 e.sources[0] = p[4];
679 e.source_count = 1;
680 parseFeatureUnitControls(e, p, v2);
681 }
682 break;
683 case 0x07: // UAC1 processing unit / UAC2 effect unit
684 if (v2) {
685 if (n >= 7) {
686 e.sources[0] = p[6];
687 e.source_count = 1;
688 }
689 } else {
690 addSources(6);
691 }
692 break;
693 case 0x08: // UAC1 extension unit / UAC2 processing unit
694 case 0x09: // UAC2 extension unit
695 addSources(6);
696 break;
697 case kAudioCsAcClockSource: // UAC2 clock source
698 break;
699 case 0x0B: // UAC2 clock selector
700 addSources(4);
701 break;
702 case 0x0C: // UAC2 clock multiplier
703 if (n >= 5) {
704 e.sources[0] = p[4];
705 e.source_count = 1;
706 }
707 break;
708 default:
709 return;
710 }
711 entities_.push_back(e);
712 }
713
715 void parseFeatureUnitControls(Entity& e, const uint8_t* p, bool v2) {
716 uint8_t n = p[0];
717 if (v2) {
718 // 4 bytes per channel starting at 5, 2 bits per control
719 for (uint8_t ch = 0; ch <= kMaxChannels && 5 + ch * 4 + 4 <= n - 1;
720 ch++) {
721 uint32_t ctrl = descU32(p + 5 + ch * 4);
722 if ((ctrl & 0x03) == 0x03) e.mute_mask |= (1u << ch);
723 if (((ctrl >> 2) & 0x03) == 0x03) e.volume_mask |= (1u << ch);
724 }
725 } else {
726 uint8_t size = p[5];
727 if (size == 0 || n < 7) return;
728 for (uint8_t ch = 0; ch <= kMaxChannels && 6 + (ch + 1) * size <= n - 1;
729 ch++) {
730 uint8_t ctrl = p[6 + ch * size];
731 if (ctrl & 0x01) e.mute_mask |= (1u << ch);
732 if (ctrl & 0x02) e.volume_mask |= (1u << ch);
733 }
734 }
735 }
736
738 void parseAsDescriptor(AltSetting& a, const uint8_t* p) {
739 uint8_t subtype = descSubtype(p);
740 uint8_t n = p[0];
741 if (uac_version_ == 2) {
742 if (subtype == kAudioCsAsGeneral && n >= 11) {
743 a.terminal_link = p[3];
744 // bFormatType == 1 and bmFormats bit 0 (PCM)
745 a.is_pcm = p[5] == kAudioFormatTypeI && (descU32(p + 6) & 0x01);
746 a.channels = p[10];
747 } else if (subtype == kAudioCsAsFormatType && n >= 6) {
748 if (p[3] != kAudioFormatTypeI) a.is_pcm = false;
749 a.subslot = p[4];
750 a.bits = p[5];
751 }
752 } else {
753 if (subtype == kAudioCsAsGeneral && n >= 7) {
754 a.terminal_link = p[3];
755 a.is_pcm = descU16(p + 5) == 0x0001; // wFormatTag: PCM
756 } else if (subtype == kAudioCsAsFormatType && n >= 8) {
757 if (p[3] != kAudioFormatTypeI) {
758 a.is_pcm = false;
759 return;
760 }
761 a.channels = p[4];
762 a.subslot = p[5];
763 a.bits = p[6];
764 uint8_t freq_type = p[7];
765 if (freq_type == 0 && n >= 14) {
766 a.continuous = true;
767 a.rates[0] = descU24(p + 8);
768 a.rates[1] = descU24(p + 11);
769 a.rate_count = 2;
770 } else {
771 for (uint8_t j = 0; j < freq_type && j < kMaxRates &&
772 8 + (j + 1) * 3 <= n;
773 j++) {
774 a.rates[a.rate_count++] = descU24(p + 8 + j * 3);
775 }
776 }
777 }
778 }
779 }
780
781 static uint32_t descU24(const uint8_t* p) {
782 return (uint32_t)p[0] | ((uint32_t)p[1] << 8) | ((uint32_t)p[2] << 16);
783 }
784
787 void resolveTopology(Stream& s, bool is_rx) {
788 if (!s.present()) return;
789 uint8_t link = s.alts[0].terminal_link;
790 Entity* term = findEntity(link);
791 if (term == nullptr) return;
792
793 if (uac_version_ == 2) s.clock_id = resolveClock(term->clock_source);
794
795 Entity* fu = nullptr;
796 if (is_rx) {
797 // microphone: walk upstream from the USB streaming output terminal
798 Entity* e = term;
799 for (int depth = 0; depth < 8 && e && e->source_count > 0; depth++) {
800 e = findEntity(e->sources[0]);
801 if (e && e->subtype == kAudioCsAcFeatureUnit) {
802 fu = e;
803 break;
804 }
805 }
806 } else {
807 // speaker: walk downstream from the USB streaming input terminal
808 uint8_t id = term->id;
809 for (int depth = 0; depth < 8 && fu == nullptr; depth++) {
810 Entity* next = nullptr;
811 for (auto& e : entities_) {
812 if (e.subtype == kAudioCsAcClockSource || e.subtype == 0x0B ||
813 e.subtype == 0x0C)
814 continue;
815 for (uint8_t j = 0; j < e.source_count; j++) {
816 if (e.sources[j] == id) next = &e;
817 }
818 if (next) break;
819 }
820 if (next == nullptr) break;
821 if (next->subtype == kAudioCsAcFeatureUnit) fu = next;
822 id = next->id;
823 }
824 }
825 if (fu) {
826 s.fu_id = fu->id;
827 s.fu_mute_mask = fu->mute_mask;
829 }
830 LOGI("%s: itf %d, %d alt settings, feature unit %d, clock %d",
831 is_rx ? "mic" : "speaker", s.alts[0].itf, (int)s.alts.size(),
832 s.fu_id, s.clock_id);
833 }
834
836 uint8_t resolveClock(uint8_t id) {
837 for (int depth = 0; depth < 4 && id != 0; depth++) {
838 Entity* e = findEntity(id);
839 if (e == nullptr) return id;
840 if (e->subtype == kAudioCsAcClockSource) return id;
841 if (e->source_count == 0) return 0;
842 id = e->sources[0];
843 }
844 return id;
845 }
846
847 // ── Format negotiation ───────────────────────────────────────────────────
848
851 const std::vector<RateRange>& clock, uint32_t rate) {
852 if (!fitsBandwidth(a, rate)) return false;
853 if (uac_version_ == 2) {
854 if (clock.empty()) return true; // unknown: assume that it works
855 for (auto& r : clock) {
856 if (rate < r.min || rate > r.max) continue;
857 if (r.res == 0 || (rate - r.min) % r.res == 0) return true;
858 }
859 return false;
860 }
861 if (a.rate_count == 0) return true;
862 if (a.continuous) return rate >= a.rates[0] && rate <= a.rates[1];
863 for (uint8_t j = 0; j < a.rate_count; j++) {
864 if (a.rates[j] == rate) return true;
865 }
866 return false;
867 }
868
869 bool fitsBandwidth(const AltSetting& a, uint32_t rate) {
870 uint32_t pps = packetsPerSecond(a);
871 uint32_t samples = (rate + pps - 1) / pps;
872 return samples * a.channels * a.subslot <= a.ep.packetSize();
873 }
874
875 uint16_t packetsPerSecond(const AltSetting& a) {
876 uint8_t interval = a.ep.bInterval;
877 if (uac_version_ == 1 || interval == 0) interval = 1;
878 if (interval > 4) interval = 4;
879 uint16_t base = backend().speed() == UsbSpeed::High ? 8000 : 1000;
880 return (uint16_t)(base >> (interval - 1));
881 }
882
884 uint32_t closestRate(const AltSetting& a,
885 const std::vector<RateRange>& clock,
886 uint32_t wanted) {
887 if (supportsRate(a, clock, wanted)) return wanted;
888 static const uint32_t common[] = {48000, 44100, 32000, 24000, 22050,
889 16000, 11025, 8000, 96000, 88200};
890 uint32_t best = 0;
891 uint32_t best_diff = UINT32_MAX;
892 auto consider = [&](uint32_t r) {
893 if (r == 0 || !supportsRate(a, clock, r)) return;
894 uint32_t diff = r > wanted ? r - wanted : wanted - r;
895 if (diff < best_diff) {
896 best = r;
897 best_diff = diff;
898 }
899 };
900 if (uac_version_ == 2) {
901 for (auto& r : clock) {
902 consider(r.min);
903 consider(r.max);
904 }
905 } else {
906 for (uint8_t j = 0; j < a.rate_count; j++) consider(a.rates[j]);
907 }
908 for (auto r : common) consider(r);
909 return best;
910 }
911
913 bool selectAlt(Stream& s, const std::vector<RateRange>& clock,
914 AudioInfo& negotiated) {
915 s.selected = -1;
916 if (!s.present()) return false;
917 int best = -1;
918 int best_score = -1;
919 uint32_t best_rate = 0;
920 for (int j = 0; j < (int)s.alts.size(); j++) {
921 const AltSetting& a = s.alts[j];
922 uint32_t rate = closestRate(a, clock, config_.sample_rate);
923 if (rate == 0) continue;
924 int score = 0;
925 if (rate == config_.sample_rate) score += 4;
926 if (a.channels == config_.channels) score += 2;
927 if (a.subslot * 8 == config_.bits_per_sample) score += 1;
928 // prefer the smaller packet size on a tie
929 if (score > best_score ||
930 (score == best_score &&
931 a.ep.packetSize() < s.alts[best].ep.packetSize())) {
932 best = j;
933 best_score = score;
934 best_rate = rate;
935 }
936 }
937 if (best < 0) return false;
938 if (best_score != 7 && !config_.host_apply_device_format) {
939 LOGE("The attached device does not support %d Hz, %d channels, %d bits",
941 return false;
942 }
943 s.selected = best;
944 s.sample_rate = best_rate;
945 negotiated.sample_rate = best_rate;
946 negotiated.channels = s.alts[best].channels;
947 negotiated.bits_per_sample = s.alts[best].subslot * 8;
948 LOGI("Selected itf %d alt %d: %d Hz, %d channels, %d bits",
949 s.alts[best].itf, s.alts[best].alt, (int)best_rate,
950 negotiated.channels, negotiated.bits_per_sample);
951 return true;
952 }
953
954 // ── Control request helpers ──────────────────────────────────────────────
955
956 bool issueControl(uint8_t request_type, uint8_t request, uint16_t value,
957 uint16_t index, uint16_t length) {
958 setup_.bmRequestType = request_type;
959 setup_.bRequest = request;
960 setup_.wValue = value;
961 setup_.wIndex = index;
962 setup_.wLength = length;
963 ctrl_busy_ = true;
965 if (!backend().control(daddr_, setup_, ctrl_buf_)) {
966 ctrl_busy_ = false;
967 return false;
968 }
969 return true;
970 }
971
972 bool issueSetInterface(uint8_t itf, uint8_t alt) {
973 ctrl_busy_ = true;
975 if (!backend().setInterface(daddr_, itf, alt)) {
976 ctrl_busy_ = false;
977 return false;
978 }
979 return true;
980 }
981
982 // class request to an interface entity, host to device
983 static constexpr uint8_t kReqOutItf = 0x21;
984 // class request to an interface entity, device to host
985 static constexpr uint8_t kReqInItf = 0xA1;
986 // class request to an endpoint, host to device
987 static constexpr uint8_t kReqOutEp = 0x22;
988
989 bool requestClockRange(Stream& s, uint16_t len) {
991 (uint16_t)(kAudioCsCtrlSamFreq << 8),
992 (uint16_t)((s.clock_id << 8) | itf_num_ac_), len);
993 }
994
996 uint32_t rate = s.sample_rate;
997 for (int j = 0; j < 4; j++) ctrl_buf_[j] = (uint8_t)(rate >> (8 * j));
999 (uint16_t)(kAudioCsCtrlSamFreq << 8),
1000 (uint16_t)((s.clock_id << 8) | itf_num_ac_), 4);
1001 }
1002
1004 uint32_t rate = s.sample_rate;
1005 for (int j = 0; j < 3; j++) ctrl_buf_[j] = (uint8_t)(rate >> (8 * j));
1007 (uint16_t)(kAudio1EpCtrlSamplingFreq << 8),
1008 s.alt().ep.bEndpointAddress, 3);
1009 }
1010
1012 static uint8_t volumeRangeChannel(const Stream& s) {
1013 if (s.fu_volume_mask & 0x01) return 0;
1014 for (uint8_t ch = 1; ch <= kMaxChannels; ch++) {
1015 if (s.fu_volume_mask & (1u << ch)) return ch;
1016 }
1017 return 0;
1018 }
1019
1020 bool requestVolumeRange(Stream& s, bool max) {
1021 uint8_t ch = volumeRangeChannel(s);
1022 uint16_t value = (uint16_t)((AUDIO_FU_CTRL_VOLUME << 8) | ch);
1023 uint16_t index = (uint16_t)((s.fu_id << 8) | itf_num_ac_);
1024 if (uac_version_ == 2) {
1025 return issueControl(kReqInItf, kAudioCsReqRange, value, index, 8);
1026 }
1028 value, index, 2);
1029 }
1030
1031 bool requestFeature(Stream& s, uint8_t selector, uint8_t ch, int16_t value) {
1032 uint16_t len = 1;
1033 if (selector == AUDIO_FU_CTRL_VOLUME) {
1034 ctrl_buf_[0] = (uint8_t)(value & 0xFF);
1035 ctrl_buf_[1] = (uint8_t)((uint16_t)value >> 8);
1036 len = 2;
1037 } else {
1038 ctrl_buf_[0] = value ? 1 : 0;
1039 }
1041 (uint16_t)((selector << 8) | ch),
1042 (uint16_t)((s.fu_id << 8) | itf_num_ac_), len);
1043 }
1044
1046 void parseClockRange(std::vector<RateRange>& ranges) {
1047 ranges.clear();
1048 if (ctrl_len_ < 2) return;
1049 uint16_t count = descU16(ctrl_buf_);
1050 for (uint16_t j = 0; j < count && 2 + (j + 1) * 12 <= ctrl_len_; j++) {
1051 RateRange r;
1052 r.min = descU32(ctrl_buf_ + 2 + j * 12);
1053 r.max = descU32(ctrl_buf_ + 6 + j * 12);
1054 r.res = descU32(ctrl_buf_ + 10 + j * 12);
1055 ranges.push_back(r);
1056 }
1057 }
1058
1060 static int16_t toDeviceVolume(const Stream& s, float vol) {
1061 if (vol <= 0.0f) return s.vol_min;
1062 if (vol >= 1.0f) return s.vol_max;
1063 return (int16_t)(s.vol_min + vol * (float)(s.vol_max - s.vol_min));
1064 }
1065
1066 // ── State machine ────────────────────────────────────────────────────────
1067
1069 void poll() {
1070 if (!is_started_ || !attached_ || !configured_) return;
1071 if (ctrl_busy_) {
1073 LOGW("USB audio control request timed out");
1074 onControlComplete(false, 0);
1075 }
1076 return;
1077 }
1078 if (step_ != Step::Idle) {
1079 run();
1080 return;
1081 }
1082 if (!mounted_) return;
1084 reconfigure_pending_ = false;
1085 reconfiguring_ = true;
1087 run();
1088 return;
1089 }
1091 }
1092
1094 void run() {
1095 if (in_run_) {
1096 run_again_ = true;
1097 return;
1098 }
1099 in_run_ = true;
1100 do {
1101 run_again_ = false;
1102 while (step_ != Step::Idle && !ctrl_busy_ && attached_) {
1103 if (!executeStep()) break; // retry in the next poll()
1104 }
1105 } while (run_again_);
1106 in_run_ = false;
1107 }
1108
1110 void next(Step s) {
1111 step_ = s;
1112 step_done_ = false;
1113 }
1114
1118 // Steps which issue a control request are entered twice: first to send
1119 // the request (step_done_ == false), then with the result.
1120 switch (step_) {
1121 case Step::StopRx:
1122 return stopStream(rx_, Step::StopTx);
1123 case Step::StopTx:
1125
1129 case Step::ClockRangeRx:
1132 if (tx_.present() && rx_.present() && tx_.clock_id == rx_.clock_id &&
1133 uac_version_ == 2) {
1136 return true;
1137 }
1139 Step::Select);
1140 case Step::ClockRangeTx:
1142
1143 case Step::Select: {
1144 bool rx_ok = selectAlt(rx_, clock_ranges_rx_, rx_info_);
1145 bool tx_ok = selectAlt(tx_, clock_ranges_tx_, tx_info_);
1146 if (!rx_ok && !tx_ok) {
1147 LOGE("No usable audio stream on the attached device");
1149 return true;
1150 }
1151 if (rx_.present() && !rx_ok) rx_.alts.clear();
1152 if (tx_.present() && !tx_ok) tx_.alts.clear();
1153 // a common clock can only run at one rate
1154 if (uac_version_ == 2 && rx_ok && tx_ok &&
1156 LOGW("Shared clock: using %d Hz for both streams",
1157 (int)rx_.sample_rate);
1160 }
1163 return true;
1164 }
1165
1166 case Step::SetClockRx:
1170 case Step::OpenRx:
1171 openStream(rx_, true);
1173 return true;
1174 case Step::SetEpRateRx:
1178 case Step::VolumeMaxRx:
1180
1181 case Step::SetClockTx:
1182 if (tx_.selected >= 0 && rx_.selected >= 0 &&
1183 tx_.clock_id == rx_.clock_id) {
1184 next(Step::SetInterfaceTx); // shared clock was already set
1185 return true;
1186 }
1190 case Step::OpenTx:
1191 openStream(tx_, false);
1193 return true;
1194 case Step::SetEpRateTx:
1198 case Step::VolumeMaxTx:
1199 return volumeRange(tx_, true, Step::Done, Step::Done);
1200
1201 case Step::Done:
1202 finishMount();
1204 return true;
1205
1206 default:
1208 return true;
1209 }
1210 }
1211
1213 bool stopStream(Stream& s, Step following) {
1214 if (s.selected < 0 || !s.active) {
1215 next(following);
1216 return true;
1217 }
1218 if (!step_done_) {
1219 s.active = false;
1221 if (s.alt().has_fb)
1223 return issueSetInterface(s.alt().itf, 0);
1224 }
1225 next(following);
1226 return true;
1227 }
1228
1229 bool clockRangeHeader(Stream& s, std::vector<RateRange>& ranges,
1230 Step on_success, Step skip) {
1231 if (uac_version_ != 2 || !s.present() || s.clock_id == 0 ||
1233 next(skip);
1234 return true;
1235 }
1236 if (!step_done_) return requestClockRange(s, 2);
1237 if (!ctrl_ok_ || ctrl_len_ < 2) {
1238 ranges.clear();
1239 next(skip);
1240 return true;
1241 }
1242 next(on_success);
1243 return true;
1244 }
1245
1246 bool clockRange(Stream& s, std::vector<RateRange>& ranges, Step following) {
1247 if (!step_done_) {
1248 uint16_t count = descU16(ctrl_buf_);
1249 uint32_t len = 2 + 12u * count;
1250 if (len > kCtrlBufSize) len = kCtrlBufSize;
1251 return requestClockRange(s, (uint16_t)len);
1252 }
1253 if (ctrl_ok_) parseClockRange(ranges);
1254 for (auto& r : ranges) {
1255 LOGI("Clock %d: %d - %d Hz (res %d)", s.clock_id, (int)r.min,
1256 (int)r.max, (int)r.res);
1257 }
1258 next(following);
1259 return true;
1260 }
1261
1262 bool setClock(Stream& s, Step following) {
1263 if (uac_version_ != 2 || s.selected < 0 || s.clock_id == 0) {
1264 next(following);
1265 return true;
1266 }
1267 if (!step_done_) return requestSetClock(s);
1268 if (!ctrl_ok_) LOGW("Setting the sample rate to %d failed", (int)s.sample_rate);
1269 next(following);
1270 return true;
1271 }
1272
1273 bool setInterface(Stream& s, Step on_success, Step skip) {
1274 if (s.selected < 0) {
1275 next(skip);
1276 return true;
1277 }
1278 if (!step_done_) return issueSetInterface(s.alt().itf, s.alt().alt);
1279 if (!ctrl_ok_) {
1280 LOGE("SET_INTERFACE %d/%d failed", s.alt().itf, s.alt().alt);
1281 s.selected = -1;
1282 next(skip);
1283 return true;
1284 }
1285 next(on_success);
1286 return true;
1287 }
1288
1289 void openStream(Stream& s, bool is_rx) {
1290 AltSetting& a = s.alt();
1292 s.nominal = (uint32_t)(((uint64_t)s.sample_rate << 16) / s.packets_per_sec);
1293 s.acc = 0;
1294 s.acc_fb = 0;
1295 s.feedback = 0;
1296 s.primed = false;
1297 s.active = true;
1298 bool ok = backend().openStream(
1299 daddr_, a.ep,
1301 if (ok && !is_rx && a.has_fb) {
1303 }
1304 if (!ok) {
1305 LOGE("Opening the endpoint 0x%x failed", a.ep.bEndpointAddress);
1306 s.active = false;
1307 }
1308 }
1309
1310 bool setEpRate(Stream& s, Step following) {
1311 // UAC1: the sampling frequency control is on the endpoint
1312 if (uac_version_ != 1 || s.selected < 0 || s.alt().rate_count < 2) {
1313 next(following);
1314 return true;
1315 }
1316 if (!step_done_) return requestSetEpRate(s);
1317 if (!ctrl_ok_) LOGW("Setting the sample rate to %d failed", (int)s.sample_rate);
1318 next(following);
1319 return true;
1320 }
1321
1322 bool volumeRange(Stream& s, bool max, Step on_success, Step skip) {
1323 if (s.selected < 0 || s.fu_id == 0 || s.fu_volume_mask == 0 ||
1325 next(skip);
1326 return true;
1327 }
1328 if (uac_version_ == 2 && max) {
1329 next(on_success);
1330 return true;
1331 }
1332 if (!step_done_) return requestVolumeRange(s, max);
1333 if (!ctrl_ok_) {
1334 next(skip);
1335 return true;
1336 }
1337 if (uac_version_ == 2) {
1338 if (ctrl_len_ >= 6) {
1339 s.vol_min = (int16_t)descU16(ctrl_buf_ + 2);
1340 s.vol_max = (int16_t)descU16(ctrl_buf_ + 4);
1341 }
1342 } else if (ctrl_len_ >= 2) {
1343 int16_t v = (int16_t)descU16(ctrl_buf_);
1344 if (max)
1345 s.vol_max = v;
1346 else
1347 s.vol_min = v;
1348 }
1349 if (s.vol_min == (int16_t)0x8000) s.vol_min++; // 0x8000 = silence
1350 if (s.vol_max <= s.vol_min) s.vol_max = s.vol_min + 1;
1351 next(on_success);
1352 return true;
1353 }
1354
1357 reconfiguring_ = false;
1358 if (!mounted_) {
1359 LOGE("USB audio device could not be started");
1360 return;
1361 }
1362 // report the negotiated format
1363 AudioInfo negotiated = rx_.active ? rx_info_ : tx_info_;
1364 if (!(negotiated == info)) {
1365 info = negotiated;
1366 config_.copyFrom(negotiated);
1367 if (volume_.size() != (size_t)negotiated.channels + 1) setupVolumeState();
1368 notifyAudioChange(negotiated);
1369 }
1371 // apply the current volume and mute state to the device
1372 volume_pending_.assign(volume_.size(), 1);
1373 mute_pending_.assign(mute_.size(), 1);
1374 LOGI("USB audio streaming started (rx=%d, tx=%d)", rx_.active, tx_.active);
1376 }
1377
1380 for (uint8_t ch = 0; ch < mute_pending_.size(); ch++) {
1381 if (mute_pending_[ch]) {
1382 mute_pending_[ch] = 0;
1383 if (sendFeature(false, ch)) return;
1384 }
1385 }
1386 for (uint8_t ch = 0; ch < volume_pending_.size(); ch++) {
1387 if (volume_pending_[ch]) {
1388 volume_pending_[ch] = 0;
1389 if (sendFeature(true, ch)) return;
1390 }
1391 }
1392 }
1393
1396 bool sendFeature(bool is_volume, uint8_t ch) {
1397 // the first call starts with rx; finishRuntimeRequest continues with tx
1398 pending_is_volume_ = is_volume;
1399 pending_channel_ = ch;
1402 }
1403
1405 if (pending_stream_ == &rx_) {
1407 if (sendFeatureFor(tx_)) return true;
1408 }
1409 pending_stream_ = nullptr;
1410 return false;
1411 }
1412
1414 if (!s.active || s.fu_id == 0) return false;
1415 uint8_t ch = pending_channel_;
1416 if (ch > kMaxChannels) return false;
1417 uint16_t mask = pending_is_volume_ ? s.fu_volume_mask : s.fu_mute_mask;
1418 if ((mask & (1u << ch)) == 0) {
1419 // no master control: apply the master to the individual channels
1420 if (ch == 0) {
1421 for (uint8_t c = 1; c < volume_.size() && c <= kMaxChannels; c++) {
1422 if (mask & (1u << c)) {
1424 volume_pending_[c] = 1;
1425 else
1426 mute_pending_[c] = 1;
1427 }
1428 }
1429 }
1430 return false;
1431 }
1432 if (pending_is_volume_) {
1433 float vol = volume(ch);
1434 if (ch != 0 && (s.fu_volume_mask & 0x01) == 0) vol *= volume(0);
1435 return requestFeature(s, AUDIO_FU_CTRL_VOLUME, ch, toDeviceVolume(s, vol));
1436 }
1437 bool m = isMute(ch);
1438 if (ch != 0 && (s.fu_mute_mask & 0x01) == 0) m = m || isMute(0);
1439 return requestFeature(s, AUDIO_FU_CTRL_MUTE, ch, m ? 1 : 0);
1440 }
1441
1443 if (!ctrl_ok_) LOGW("USB audio feature unit request failed");
1444 if (pending_stream_ != nullptr) sendFeatureNextStream();
1445 }
1446
1448 void onFeedback(const uint8_t* data, uint16_t len) {
1449 uint32_t value = 0;
1450 if (len == 3) {
1451 value = descU24(data) << 2; // 10.14 -> 16.16
1452 } else if (len >= 4) {
1453 value = descU32(data); // 16.16
1454 } else {
1455 return;
1456 }
1457 // accept only plausible values (+/- 1/8 of nominal); some full speed
1458 // devices send the 10.14 format in 4 bytes
1459 uint32_t nom = tx_.nominal;
1460 if (nom == 0) return;
1461 uint32_t delta = nom >> 3;
1462 auto plausible = [&](uint32_t v) { return v > nom - delta && v < nom + delta; };
1463 if (plausible(value)) {
1464 tx_.feedback = value;
1465 } else if (len >= 4 && plausible(value << 2)) {
1466 tx_.feedback = value << 2;
1467 }
1468 }
1469
1471 attached_ = false;
1472 configured_ = false;
1473 mounted_ = false;
1474 daddr_ = 0;
1475 vid_ = pid_ = 0;
1476 uac_version_ = 0;
1477 step_ = Step::Idle;
1478 ctrl_busy_ = false;
1479 reconfiguring_ = false;
1480 reconfigure_pending_ = false;
1481 restart_pending_ = false;
1482 pending_stream_ = nullptr;
1483 rx_.reset();
1484 tx_.reset();
1485 entities_.clear();
1486 clock_ranges_rx_.clear();
1487 clock_ranges_tx_.clear();
1488 volume_pending_.clear();
1489 mute_pending_.clear();
1490 }
1491};
1492
1493} // namespace audio_tools
#define LOGW(...)
Definition AudioLoggerIDF.h:29
#define LOGI(...)
Definition AudioLoggerIDF.h:28
#define LOGE(...)
Definition AudioLoggerIDF.h:30
void notifyAudioChange(AudioInfo info)
Definition AudioTypes.h:180
AudioInfo info
Definition BaseStream.h:171
virtual AudioInfo audioInfo() override
provides the actual input AudioInfo
Definition BaseStream.h:151
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 float levelPercent()
Returns the level of the buffer in %.
Definition Buffers.h:114
virtual int available()=0
provides the number of entries that are available to read
virtual void flush() override
Definition BaseStream.h:59
Abstract seam for every raw USB host stack call the UAC host class logic (USBAudioHostBase) needs....
Definition USBAudioHostBackend.h:73
virtual void task()=0
Pump the host stack on platforms where the application drives it.
virtual bool openStream(uint8_t daddr, const UsbEndpointDescriptorView &ep, UsbHostStreamRole role)=0
virtual UsbSpeed speed() const
Bus speed of the attached device.
Definition USBAudioHostBackend.h:105
virtual uint32_t millis() const =0
Milliseconds since start (for control request timeouts).
virtual void end()=0
Stop all streams (the host stack itself keeps running).
virtual bool begin(USBAudioHostEvents *events, const USBAudioConfig &cfg)=0
Start the host stack and register the event receiver.
virtual void closeStream(uint8_t daddr, uint8_t ep_addr)=0
Stop streaming the indicated endpoint.
USB Audio Class (UAC1 and UAC2) host: reads PCM audio from an attached USB microphone and/or writes P...
Definition USBAudioHostBase.h:48
bool begin(const USBAudioConfig &cfg)
Apply a config and start the USB audio host.
Definition USBAudioHostBase.h:107
AudioInfo audioInfoRx() const
Negotiated format of the microphone stream (readBytes())
Definition USBAudioHostBase.h:198
uint8_t uac_version_
Definition USBAudioHostBase.h:347
bool step_done_
Definition USBAudioHostBase.h:366
void run()
Runs the state machine until a control request is in flight.
Definition USBAudioHostBase.h:1094
bool sendFeatureFor(Stream &s)
Definition USBAudioHostBase.h:1413
void parseAsDescriptor(AltSetting &a, const uint8_t *p)
Audio Streaming interface: AS_GENERAL and FORMAT_TYPE descriptors.
Definition USBAudioHostBase.h:738
Entity * findEntity(uint8_t id)
Definition USBAudioHostBase.h:543
void onVolumeChanged(uint8_t channel) override
Definition USBAudioHostBase.h:510
bool setEpRate(Stream &s, Step following)
Definition USBAudioHostBase.h:1310
std::vector< uint8_t > volume_pending_
Definition USBAudioHostBase.h:376
bool requestSetEpRate(Stream &s)
Definition USBAudioHostBase.h:1003
bool issueControl(uint8_t request_type, uint8_t request, uint16_t value, uint16_t index, uint16_t length)
Definition USBAudioHostBase.h:956
uint8_t resolveClock(uint8_t id)
Follow clock selectors and multipliers to the clock source.
Definition USBAudioHostBase.h:836
bool executeStep()
Definition USBAudioHostBase.h:1117
bool requestFeature(Stream &s, uint8_t selector, uint8_t ch, int16_t value)
Definition USBAudioHostBase.h:1031
virtual USBAudioHostBackend & backend()=0
Returns the backend used for all USB host stack calls.
uint16_t pid() const
Product id of the attached device (0 if none)
Definition USBAudioHostBase.h:207
uint16_t onIsoOut(uint8_t *data, uint16_t max) override
Definition USBAudioHostBase.h:468
void openStream(Stream &s, bool is_rx)
Definition USBAudioHostBase.h:1289
bool stopStream(Stream &s, Step following)
Stop streaming before a reconfiguration: select alt 0.
Definition USBAudioHostBase.h:1213
static constexpr uint8_t kReqOutItf
Definition USBAudioHostBase.h:983
bool ctrl_busy_
Definition USBAudioHostBase.h:363
bool isStreamingActiveTx() const override
Returns true if the speaker stream (used by write()) is open.
Definition USBAudioHostBase.h:185
static constexpr uint8_t kReqInItf
Definition USBAudioHostBase.h:985
static constexpr uint8_t kMaxRates
Definition USBAudioHostBase.h:229
void onIsoIn(UsbHostStreamRole role, const uint8_t *data, uint16_t len) override
One isochronous IN packet was received (RxData or Feedback).
Definition USBAudioHostBase.h:454
Step step_
Definition USBAudioHostBase.h:362
uint32_t getRxXferCount() const
Definition USBAudioHostBase.h:216
void finishMount()
Definition USBAudioHostBase.h:1355
void resetDevice()
Definition USBAudioHostBase.h:1470
void onControlComplete(bool ok, uint16_t len) override
Definition USBAudioHostBase.h:441
void onMuteChanged(uint8_t channel) override
Definition USBAudioHostBase.h:514
bool supportsRate(const AltSetting &a, const std::vector< RateRange > &clock, uint32_t rate)
True if the alternate setting supports the sample rate.
Definition USBAudioHostBase.h:850
void onConfigured(uint8_t daddr) override
Definition USBAudioHostBase.h:424
bool fitsBandwidth(const AltSetting &a, uint32_t rate)
Definition USBAudioHostBase.h:869
static uint16_t packetSizeFor(const AudioInfo &info)
Bytes of one 1 ms packet for the indicated format (+1 sample margin)
Definition USBAudioHostBase.h:521
AudioInfo rx_info_
Definition USBAudioHostBase.h:357
bool restart_pending_
Definition USBAudioHostBase.h:374
volatile bool reconfigure_pending_
Definition USBAudioHostBase.h:373
uint32_t getRxTotalBytes() const
Total bytes received from the microphone.
Definition USBAudioHostBase.h:218
bool begin()
Start the USB host stack with the current config. The attached device is opened as soon as it has bee...
Definition USBAudioHostBase.h:124
bool sendFeatureNextStream()
Definition USBAudioHostBase.h:1404
std::vector< Entity > entities_
Definition USBAudioHostBase.h:354
void updateBufferSize()
Definition USBAudioHostBase.h:528
bool clockRangeHeader(Stream &s, std::vector< RateRange > &ranges, Step on_success, Step skip)
Definition USBAudioHostBase.h:1229
bool beginUSB() override
Override in platform subclasses to start the USB stack.
Definition USBAudioHostBase.h:385
AudioInfo tx_info_
Definition USBAudioHostBase.h:358
bool hasSpeaker() const
True if the attached device provides a speaker (TX)
Definition USBAudioHostBase.h:195
void onDetach(uint8_t daddr) override
The device was removed.
Definition USBAudioHostBase.h:433
bool mounted() override
Returns true if an audio device is attached and configured.
Definition USBAudioHostBase.h:182
Step
Steps of the (re)configuration state machine.
Definition USBAudioHostBase.h:320
uint16_t onAttach(uint8_t daddr, uint16_t vid, uint16_t pid, const uint8_t *desc, uint16_t len) override
Definition USBAudioHostBase.h:396
bool volumeRange(Stream &s, bool max, Step on_success, Step skip)
Definition USBAudioHostBase.h:1322
static constexpr uint8_t kReqOutEp
Definition USBAudioHostBase.h:987
Stream rx_
Definition USBAudioHostBase.h:352
bool reconfiguring_
Definition USBAudioHostBase.h:369
static constexpr uint8_t kMaxChannels
Definition USBAudioHostBase.h:230
std::vector< RateRange > clock_ranges_tx_
Definition USBAudioHostBase.h:356
void resolveTopology(Stream &s, bool is_rx)
Definition USBAudioHostBase.h:787
void next(Step s)
Advance to the next step.
Definition USBAudioHostBase.h:1110
uint32_t getRxDroppedBytes() const
Bytes discarded because bufferRx() was full.
Definition USBAudioHostBase.h:220
Stream * pending_stream_
Definition USBAudioHostBase.h:380
volatile bool attached_
Definition USBAudioHostBase.h:349
uint32_t closestRate(const AltSetting &a, const std::vector< RateRange > &clock, uint32_t wanted)
Supported sample rate which is closest to the wanted rate (0 if none)
Definition USBAudioHostBase.h:884
volatile bool configured_
Definition USBAudioHostBase.h:351
bool issueSetInterface(uint8_t itf, uint8_t alt)
Definition USBAudioHostBase.h:972
uint8_t ctrl_buf_[kCtrlBufSize]
Definition USBAudioHostBase.h:371
bool requestSetClock(Stream &s)
Definition USBAudioHostBase.h:995
bool setInterface(Stream &s, Step on_success, Step skip)
Definition USBAudioHostBase.h:1273
bool hasMicrophone() const
True if the attached device provides a microphone (RX)
Definition USBAudioHostBase.h:192
static uint32_t descU24(const uint8_t *p)
Definition USBAudioHostBase.h:781
void poll()
Called regularly from the USB stack context.
Definition USBAudioHostBase.h:1069
std::vector< uint8_t > mute_pending_
Definition USBAudioHostBase.h:377
uint16_t vid_
Definition USBAudioHostBase.h:345
uint16_t parseConfiguration(const uint8_t *desc, uint16_t len)
Definition USBAudioHostBase.h:552
UsbSetupPacket setup_
Definition USBAudioHostBase.h:372
std::vector< RateRange > clock_ranges_rx_
Definition USBAudioHostBase.h:355
uint32_t getTxUnderflowBytes() const
Bytes of silence which were sent because bufferTx() was empty.
Definition USBAudioHostBase.h:222
void startRuntimeRequest()
Sends the next queued volume or mute change.
Definition USBAudioHostBase.h:1379
void end()
Stop the streams and clear the buffers. The host stack keeps running, so the device is opened again w...
Definition USBAudioHostBase.h:159
bool ctrl_ok_
Definition USBAudioHostBase.h:364
uint16_t buffer_bytes_
Definition USBAudioHostBase.h:359
uint16_t packetsPerSecond(const AltSetting &a)
Definition USBAudioHostBase.h:875
Stream tx_
Definition USBAudioHostBase.h:353
bool in_run_
Definition USBAudioHostBase.h:367
uint32_t getTxXferCount() const
Number of isochronous OUT packets which were provided to the speaker.
Definition USBAudioHostBase.h:213
AudioInfo audioInfoTx() const
Negotiated format of the speaker stream (write())
Definition USBAudioHostBase.h:201
USBAudioConfig defaultConfig(RxTxMode mode=RXTX_MODE)
Returns a default configuration for the requested direction. RX_MODE reads from a USB microphone,...
Definition USBAudioHostBase.h:58
bool clockRange(Stream &s, std::vector< RateRange > &ranges, Step following)
Definition USBAudioHostBase.h:1246
static int16_t toDeviceVolume(const Stream &s, float vol)
Linear mapping of 0.0..1.0 to the volume range of the device.
Definition USBAudioHostBase.h:1060
bool pending_is_volume_
Definition USBAudioHostBase.h:379
uint8_t pending_channel_
Definition USBAudioHostBase.h:378
uint8_t daddr_
Definition USBAudioHostBase.h:344
void processUSB()
Definition USBAudioHostBase.h:389
void setAudioInfo(AudioInfo info) override
Defines the audio format. Before begin() all fields can be changed; once started, a new sample rate i...
Definition USBAudioHostBase.h:84
void onFeedback(const uint8_t *data, uint16_t len)
Explicit feedback from the speaker: samples per packet.
Definition USBAudioHostBase.h:1448
void parseClockRange(std::vector< RateRange > &ranges)
Parses a UAC2 GET_RANGE (layout 3: 32 bit) response for the clock.
Definition USBAudioHostBase.h:1046
uint8_t itf_num_ac_
Definition USBAudioHostBase.h:348
USBAudioHostBase(USBAudioConfig cfg)
Constructor which provides the configuration at construction time.
Definition USBAudioHostBase.h:53
bool requestClockRange(Stream &s, uint16_t len)
Definition USBAudioHostBase.h:989
volatile bool mounted_
Definition USBAudioHostBase.h:350
bool setClock(Stream &s, Step following)
Definition USBAudioHostBase.h:1262
bool selectAlt(Stream &s, const std::vector< RateRange > &clock, AudioInfo &negotiated)
Selects the alternate setting which matches the requested format best.
Definition USBAudioHostBase.h:913
bool isStreamingActiveRx() const override
Returns true if the microphone stream (used by readBytes()) is open.
Definition USBAudioHostBase.h:189
uint32_t ctrl_start_ms_
Definition USBAudioHostBase.h:370
uint16_t ctrl_len_
Definition USBAudioHostBase.h:365
uint8_t uacVersion() const
USB Audio Class version of the attached device: 1 or 2 (0 if none)
Definition USBAudioHostBase.h:210
static uint8_t volumeRangeChannel(const Stream &s)
Channel used to determine the volume range.
Definition USBAudioHostBase.h:1012
uint16_t pid_
Definition USBAudioHostBase.h:346
bool sendFeature(bool is_volume, uint8_t ch)
Definition USBAudioHostBase.h:1396
bool requestVolumeRange(Stream &s, bool max)
Definition USBAudioHostBase.h:1020
static constexpr uint16_t kCtrlBufSize
Definition USBAudioHostBase.h:231
uint16_t audioPacketSize() const
Definition USBAudioHostBase.h:226
void parseAcDescriptor(const uint8_t *p)
Audio Control interface: header, terminals, units and clocks.
Definition USBAudioHostBase.h:636
void finishRuntimeRequest()
Definition USBAudioHostBase.h:1442
void parseFeatureUnitControls(Entity &e, const uint8_t *p, bool v2)
Determines which channels provide a host programmable mute / volume.
Definition USBAudioHostBase.h:715
bool run_again_
Definition USBAudioHostBase.h:368
uint16_t vid() const
Vendor id of the attached device (0 if none)
Definition USBAudioHostBase.h:204
Upcalls from a USB host backend into the stack independent UAC host class logic (USBAudioHostBase)....
Definition USBAudioHostBackend.h:26
Role-independent front end shared by USB Audio devices (USBAudioDeviceBase, aliased as USBAudioStream...
Definition USBAudioStreamBase.h:39
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 USBAudioStreamBase.h:307
std::vector< float > volume_
Definition USBAudioStreamBase.h:229
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
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 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 void resizeBuffers()=0
Resize the platform audio buffers.
virtual int getActualCore() const
Returns the actual core on which the call is running (for RP2040)
Definition USBAudioStreamBase.h:218
std::vector< bool > mute_
Definition USBAudioStreamBase.h:230
bool isStreamingActive() const
Returns true if either the TX or RX stream is open.
Definition USBAudioStreamBase.h:123
void notifyStreamingState()
Definition USBAudioStreamBase.h:330
bool configChanged(const USBAudioConfig &n)
Definition USBAudioStreamBase.h:254
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 kAudio1ReqGetMin
Definition USBAudioConstants.h:42
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 uint8_t kUsbClassAudio
Definition USBAudioConstants.h:21
UsbEndpointDescriptorView decodeEndpoint(uint8_t const *p)
Definition USBAudioBackend.h:121
static constexpr uint8_t kAudioCsReqCur
Definition USBAudioConstants.h:39
static constexpr uint8_t kAudioFormatTypeI
Definition USBAudioConstants.h:36
UsbInterfaceDescriptorView decodeInterface(uint8_t const *p)
Definition USBAudioBackend.h:136
UsbHostStreamRole
Role of an isochronous endpoint which is streamed by a host backend.
Definition USBAudioHostBackend.h:11
@ TxData
Audio data to the attached device (speaker): ISO OUT.
@ RxData
Audio data from the attached device (microphone): ISO IN.
@ Feedback
Explicit feedback from the attached speaker: ISO IN.
static constexpr uint8_t kAudioSubclassStreaming
Definition USBAudioConstants.h:23
static constexpr uint8_t AUDIO_FU_CTRL_VOLUME
Definition USBAudioConstants.h:51
static constexpr uint8_t kAudio1ReqGetMax
Definition USBAudioConstants.h:43
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
uint8_t const * descNext(uint8_t const *p)
Definition USBAudioBackend.h:105
static constexpr uint8_t AUDIO_FU_CTRL_MUTE
Definition USBAudioConstants.h:50
static constexpr uint8_t kAudioCsAcFeatureUnit
Definition USBAudioConstants.h:30
static constexpr uint8_t kAudioCsAcHeader
Definition USBAudioConstants.h:27
static constexpr uint8_t kUsbDescTypeIad
Definition USBAudioConstants.h:16
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 kAudio1EpCtrlSamplingFreq
Definition USBAudioConstants.h:48
uint32_t millis()
Returns the milliseconds since the start.
Definition Arduino.h:260
static constexpr uint8_t kAudioCsAcClockSource
Definition USBAudioConstants.h:31
static constexpr uint8_t kAudioCsReqRange
Definition USBAudioConstants.h:40
Basic Audio information which drives e.g. I2S.
Definition AudioTypes.h:56
void copyFrom(AudioInfo info)
Same as set.
Definition AudioTypes.h:106
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
uint16_t host_vid
Only accept an attached audio device with this vendor id (0 = any).
Definition USBAudioConfig.h:240
bool host_apply_device_format
Definition USBAudioConfig.h:252
bool enable_feedback_ep
Enable isochronous feedback endpoint so the host can adjust its clock.
Definition USBAudioConfig.h:168
uint16_t host_pid
Only accept an attached audio device with this product id (0 = any).
Definition USBAudioConfig.h:242
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:120
uint16_t host_ctrl_timeout_ms
Timeout for class specific control requests in ms.
Definition USBAudioConfig.h:246
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:121
One alternate setting of an audio streaming interface.
Definition USBAudioHostBase.h:234
uint8_t channels
Definition USBAudioHostBase.h:239
uint8_t terminal_link
Definition USBAudioHostBase.h:237
uint8_t alt
Definition USBAudioHostBase.h:236
bool continuous
Definition USBAudioHostBase.h:247
uint32_t rates[kMaxRates]
Definition USBAudioHostBase.h:248
uint8_t bits
Definition USBAudioHostBase.h:241
bool has_ep
Definition USBAudioHostBase.h:242
uint8_t itf
Definition USBAudioHostBase.h:235
UsbEndpointDescriptorView fb_ep
Definition USBAudioHostBase.h:245
bool has_fb
Definition USBAudioHostBase.h:244
bool is_pcm
Definition USBAudioHostBase.h:238
UsbEndpointDescriptorView ep
Definition USBAudioHostBase.h:243
uint8_t subslot
Definition USBAudioHostBase.h:240
uint8_t rate_count
Definition USBAudioHostBase.h:249
Audio Control entity (terminal, unit or clock) for the topology walk.
Definition USBAudioHostBase.h:308
uint8_t id
Definition USBAudioHostBase.h:309
uint16_t volume_mask
Definition USBAudioHostBase.h:316
uint16_t terminal_type
Definition USBAudioHostBase.h:311
uint8_t clock_source
Definition USBAudioHostBase.h:312
uint8_t source_count
Definition USBAudioHostBase.h:314
uint8_t subtype
Definition USBAudioHostBase.h:310
uint16_t mute_mask
Definition USBAudioHostBase.h:315
uint8_t sources[4]
Definition USBAudioHostBase.h:313
Range of supported sample rates (UAC2 clock source GET_RANGE)
Definition USBAudioHostBase.h:253
uint32_t min
Definition USBAudioHostBase.h:254
uint32_t max
Definition USBAudioHostBase.h:255
uint32_t res
Definition USBAudioHostBase.h:256
State of one direction (microphone or speaker)
Definition USBAudioHostBase.h:260
bool active
Definition USBAudioHostBase.h:271
uint8_t fu_id
Definition USBAudioHostBase.h:264
AltSetting & alt()
Definition USBAudioHostBase.h:286
bool present() const
Definition USBAudioHostBase.h:285
bool primed
Definition USBAudioHostBase.h:279
uint32_t dropped_bytes
Definition USBAudioHostBase.h:283
uint32_t sample_rate
Definition USBAudioHostBase.h:263
uint32_t acc
Definition USBAudioHostBase.h:274
int16_t vol_max
Definition USBAudioHostBase.h:270
uint16_t packets_per_sec
Definition USBAudioHostBase.h:278
uint32_t nominal
Definition USBAudioHostBase.h:276
volatile uint32_t feedback
Definition USBAudioHostBase.h:277
int16_t vol_min
Definition USBAudioHostBase.h:269
uint8_t clock_id
Definition USBAudioHostBase.h:265
uint16_t fu_mute_mask
Definition USBAudioHostBase.h:267
uint32_t xfer_count
Definition USBAudioHostBase.h:281
const AltSetting & alt() const
Definition USBAudioHostBase.h:287
void reset()
Definition USBAudioHostBase.h:292
int selected
Definition USBAudioHostBase.h:262
uint16_t frameBytes() const
Definition USBAudioHostBase.h:288
uint16_t fu_volume_mask
Definition USBAudioHostBase.h:268
uint32_t total_bytes
Definition USBAudioHostBase.h:282
std::vector< AltSetting > alts
Definition USBAudioHostBase.h:261
uint32_t acc_fb
Definition USBAudioHostBase.h:275
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 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
uint16_t wLength
Definition USBAudioBackend.h:39
uint16_t wValue
Definition USBAudioBackend.h:37
uint8_t bmRequestType
Definition USBAudioBackend.h:35
uint16_t wIndex
Definition USBAudioBackend.h:38