arduino-audio-tools
Loading...
Searching...
No Matches
USBAudioDeviceZephyr.h
Go to the documentation of this file.
1#pragma once
2#include <zephyr/device.h>
3#include <zephyr/kernel.h>
4#include <zephyr/logging/log.h>
5#include <zephyr/usb/class/usbd_uac2.h>
6#include <zephyr/usb/usbd.h>
7
8#include <cstring>
9#include <functional>
10#include <vector>
11
13
14LOG_MODULE_DECLARE(usb_audio_device_zephyr, LOG_LEVEL_INF);
15
16namespace audio_tools {
17
38 public:
40 USBAudioDeviceZephyr(USBAudioConfig cfg) : config_(cfg) {}
41
42 // ── Duck-typing interface required by USBAudioStream<Device> ───────────────
43
46 cfg.sample_rate = 48000;
47 cfg.channels = 2;
48 cfg.bits_per_sample = 16;
49 cfg.enable_ep_in = (mode == TX_MODE || mode == RXTX_MODE);
50 cfg.enable_ep_out = (mode == RX_MODE || mode == RXTX_MODE);
51 cfg.enable_feedback_ep = (mode == RX_MODE);
52 cfg.terminal_id = 1;
53 return cfg;
54 }
55
56 bool begin(const USBAudioConfig& cfg) {
57 config_ = cfg;
58 return begin();
59 }
60
61 bool begin() {
62 uint16_t frame = audioPacketSize();
63 block_sz_ = (uint16_t)((frame + 3u) & ~3u); // round up to 4-byte boundary
64 uint8_t count = (uint8_t)(kPipelineDepth * 2u);
65
66 slab_buf_.resize((size_t)block_sz_ * count);
67 if (k_mem_slab_init(&slab_, slab_buf_.data(), block_sz_, count) != 0) {
68 LOG_ERR("USBAudioDeviceZephyr: k_mem_slab_init failed");
69 slab_buf_.resize(0);
70 return false;
71 }
72
73 uac2_dev_ = DEVICE_DT_GET(DT_ALIAS(uac2_dev));
74 if (!device_is_ready(uac2_dev_)) {
75 LOG_ERR("USBAudioDeviceZephyr: UAC2 device not ready");
76 slab_buf_.resize(0);
77 return false;
78 }
79
80 static const struct uac2_ops ops = {
81 .terminal_update_cb = s_terminal_update,
82 .get_recv_buf = s_get_recv_buf,
83 .data_recv_cb = s_data_recv,
84 .buf_release_cb = s_buf_release,
85 .get_explicit_feedback = s_get_feedback,
86 };
87 usbd_uac2_set_ops(uac2_dev_, &ops, this);
88
89 is_active_ = true;
90 LOG_INF("USBAudioDeviceZephyr: started (%u Hz, %u ch, %u bit)",
91 config_.sample_rate, config_.channels, config_.bits_per_sample);
92 return true;
93 }
94
95 void end() {
96 is_active_ = false;
97 streaming_ = false;
98 slab_buf_.resize(0);
99 LOG_INF("USBAudioDeviceZephyr: stopped");
100 }
101
102 void setRxCallback(std::function<void(const uint8_t*, uint16_t)> cb) {
103 rx_cb_ = std::move(cb);
104 }
105
106 void setTxCallback(std::function<uint16_t(uint8_t*, uint16_t)> cb) {
107 tx_cb_ = std::move(cb);
108 }
109
110 void process() { k_yield(); }
111
113 return (uint16_t)((config_.sample_rate / 1000U) * config_.channels *
114 (config_.bits_per_sample / 8U));
115 }
116
121 if (cfg.sample_rate != config_.sample_rate ||
122 cfg.channels != config_.channels ||
123 cfg.bits_per_sample != config_.bits_per_sample) {
124 LOG_WRN(
125 "USBAudioDeviceZephyr: audio format change ignored — "
126 "DTS descriptor is fixed at build time");
127 }
128 }
129
131 bool mounted() const { return streaming_; }
132
133 private:
135 BaseBuffer<uint8_t>& bufferTx() override { return default_buffer_tx_; }
136
138 BaseBuffer<uint8_t>& bufferRx() override { return default_buffer_rx_; }
139
140 // ── Default audio buffers (RingBuffer, suitable for single-core platforms) ─
141 RingBuffer<uint8_t> default_buffer_tx_{1};
142 RingBuffer<uint8_t> default_buffer_rx_{1};
143
144 // ── Static trampolines (user_data == this) ────────────────────────────────
145
146 static void s_terminal_update(const struct device* /*dev*/,
147 uint8_t /*terminal*/, bool enabled,
148 bool /*microframes*/, void* user_data) {
149 auto* self = static_cast<USBAudioDeviceZephyr*>(user_data);
150 self->streaming_ = enabled;
151 if (enabled && self->config_.enable_ep_in) {
152 self->primeTxPipeline();
153 }
154 LOG_INF("USBAudioDeviceZephyr: streaming %s",
155 enabled ? "started" : "stopped");
156 }
157
158 static void* s_get_recv_buf(const struct device* /*dev*/,
159 uint8_t /*terminal*/, uint16_t /*size*/,
160 void* user_data) {
161 auto* self = static_cast<USBAudioDeviceZephyr*>(user_data);
162 if (!self->is_active_ || !self->config_.enable_ep_out) return nullptr;
163 void* buf = nullptr;
164 if (k_mem_slab_alloc(&self->slab_, &buf, K_NO_WAIT) != 0) {
165 LOG_WRN("USBAudioDeviceZephyr: slab alloc failed (overrun)");
166 return nullptr;
167 }
168 return buf;
169 }
170
171 static void s_data_recv(const struct device* /*dev*/, uint8_t /*terminal*/,
172 void* buf, uint16_t size, void* user_data) {
173 auto* self = static_cast<USBAudioDeviceZephyr*>(user_data);
174 if (!self->is_active_ || !buf) {
175 if (buf) k_mem_slab_free(&self->slab_, buf);
176 return;
177 }
178 if (self->rx_cb_) {
179 self->rx_cb_(static_cast<const uint8_t*>(buf), size);
180 }
181 k_mem_slab_free(&self->slab_, buf);
182 }
183
184 static void s_buf_release(const struct device* /*dev*/, uint8_t /*terminal*/,
185 void* buf, void* user_data) {
186 auto* self = static_cast<USBAudioDeviceZephyr*>(user_data);
187 if (!buf) return;
188 k_mem_slab_free(&self->slab_, buf);
189
190 if (!self->is_active_ || !self->streaming_ || !self->tx_cb_) return;
191
192 void* next = nullptr;
193 if (k_mem_slab_alloc(&self->slab_, &next, K_NO_WAIT) != 0) return;
194
196 self->tx_cb_(static_cast<uint8_t*>(next), self->block_sz_);
197 if (filled < self->block_sz_) {
198 memset(static_cast<uint8_t*>(next) + filled, 0,
199 (size_t)(self->block_sz_ - filled));
200 }
201 if (usbd_uac2_send(self->uac2_dev_, self->config_.terminal_id, next,
202 self->block_sz_) != 0) {
203 k_mem_slab_free(&self->slab_, next);
204 }
205 }
206
207 static uint32_t s_get_feedback(const struct device* /*dev*/,
208 uint8_t /*terminal*/, void* user_data) {
209 auto* self = static_cast<USBAudioDeviceZephyr*>(user_data);
210 uint32_t spf = self->config_.sample_rate / 1000U;
211 return (spf << 14) & 0x00FFFFFFu;
212 }
213
214 // ── TX pipeline priming ───────────────────────────────────────────────────
215
216 void primeTxPipeline() {
217 if (!tx_cb_) return;
218 for (uint8_t i = 0; i < kPipelineDepth; ++i) {
219 void* buf = nullptr;
220 if (k_mem_slab_alloc(&slab_, &buf, K_NO_WAIT) != 0) break;
221 uint16_t filled = tx_cb_(static_cast<uint8_t*>(buf), block_sz_);
222 if (filled < block_sz_) {
223 memset(static_cast<uint8_t*>(buf) + filled, 0,
224 (size_t)(block_sz_ - filled));
225 }
226 if (usbd_uac2_send(uac2_dev_, config_.terminal_id, buf, block_sz_) != 0) {
227 k_mem_slab_free(&slab_, buf);
228 break;
229 }
230 }
231 }
232
233 // ── Members ───────────────────────────────────────────────────────────────
234
235 USBAudioConfig config_;
236 const struct device* uac2_dev_ = nullptr;
237 struct k_mem_slab slab_{};
238 std::vector<uint8_t> slab_buf_;
239 bool is_active_ = false;
240 bool streaming_ = false;
241 uint16_t block_sz_ = 0;
242
243 std::function<void(const uint8_t*, uint16_t)> rx_cb_;
244 std::function<uint16_t(uint8_t*, uint16_t)> tx_cb_;
245
246 static constexpr uint8_t kPipelineDepth = 3;
247};
248
256using USBAudioStream = USBAudioDeviceZephyr;
257
258} // namespace audio_tools
LOG_MODULE_DECLARE(usb_audio_device_zephyr, LOG_LEVEL_INF)
Shared functionality of all buffers.
Definition Buffers.h:23
USB Audio 2.0 device backend for Zephyr RTOS.
Definition USBAudioDeviceZephyr.h:37
bool begin(const USBAudioConfig &cfg)
Definition USBAudioDeviceZephyr.h:56
void setTxCallback(std::function< uint16_t(uint8_t *, uint16_t)> cb)
Definition USBAudioDeviceZephyr.h:106
void process()
Definition USBAudioDeviceZephyr.h:110
void reenumerateUSBOnChange(const USBAudioConfig &cfg)
Definition USBAudioDeviceZephyr.h:120
bool begin()
Definition USBAudioDeviceZephyr.h:61
bool mounted() const
True while the USB host is actively streaming.
Definition USBAudioDeviceZephyr.h:131
void end()
Definition USBAudioDeviceZephyr.h:95
USBAudioConfig defaultConfig(RxTxMode mode=RXTX_MODE)
Definition USBAudioDeviceZephyr.h:44
void setRxCallback(std::function< void(const uint8_t *, uint16_t)> cb)
Definition USBAudioDeviceZephyr.h:102
USBAudioDeviceZephyr(USBAudioConfig cfg)
Definition USBAudioDeviceZephyr.h:40
uint16_t audioPacketSize() const
Definition USBAudioDeviceZephyr.h:112
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
USBAudioDeviceESP32 USBAudioStream
USBAudioStream type alias for ESP32.
Definition USBAudioDeviceESP32.h:195
Generic Implementation of sound input and output for desktop environments using portaudio.
Definition AudioCodecsBase.h:10
size_t writeData(Print *p_out, T *data, int samples, int maxSamples=512)
Definition AudioTypes.h:508
sample_rate_t sample_rate
Sample Rate: e.g 44100.
Definition AudioTypes.h:53
uint16_t channels
Number of channels: 2=stereo, 1=mono.
Definition AudioTypes.h:55
uint8_t bits_per_sample
Number of bits per sample (int16_t = 16 bits)
Definition AudioTypes.h:57
Configuration for USB Audio (inherits sample_rate / channels / bits_per_sample from AudioInfo).
Definition USBAudioConfig.h:27
bool enable_feedback_ep
Enable isochronous feedback endpoint so the host can adjust its clock.
Definition USBAudioConfig.h:64
bool enable_ep_in
device → host (capture / microphone)
Definition USBAudioConfig.h:31
uint8_t terminal_id
Definition USBAudioConfig.h:109
bool enable_ep_out
host → device (playback / speaker)
Definition USBAudioConfig.h:32