arduino-audio-tools
All Classes Namespaces Files Functions Variables Typedefs Enumerations Friends Macros Modules Pages
AudioDevice.h
1#pragma once
2
3#include <cstdint>
4#include <cstring>
5#include <functional>
6
7#include "USBAudio2DescriptorBuilder.h"
8#include "AudioTools/CoreAudio/AudioTypes.h"
9#include "tusb.h"
10
11namespace tinyusb {
12// Forward declaration
13// class USBAudio2DescriptorBuilder;
14
15// AudioDevice with Audio Class 2.0 and dynamic descriptor support
16template <uint8_t ItfNum = 0, uint8_t EPOut = 0x01, uint8_t EPIn = 0x81>
18 public:
21 AUDIO_FU_CTRL_UNDEF = 0x00,
22 AUDIO_FU_CTRL_MUTE = 0x01,
23 AUDIO_FU_CTRL_VOLUME = 0x02,
24 AUDIO_FU_CTRL_BASS = 0x03,
25 AUDIO_FU_CTRL_MID = 0x04,
26 AUDIO_FU_CTRL_TREBLE = 0x05,
27 AUDIO_FU_CTRL_GRAPHIC_EQUALIZER = 0x06,
28 AUDIO_FU_CTRL_AGC = 0x07,
29 AUDIO_FU_CTRL_DELAY = 0x08,
30 AUDIO_FU_CTRL_BASS_BOOST = 0x09,
31 AUDIO_FU_CTRL_LOUDNESS = 0x0A,
32 AUDIO_FU_CTRL_INPUT_GAIN = 0x0B,
33 AUDIO_FU_CTRL_GAIN_PAD = 0x0C,
34 AUDIO_FU_CTRL_INVERTER = 0x0D,
35 AUDIO_FU_CTRL_UNDERFLOW = 0x0E,
36 AUDIO_FU_CTRL_OVERVLOW = 0x0F,
37 AUDIO_FU_CTRL_LATENCY = 0x10,
38 };
39
40 enum audio_feedback_method_t {
41 AUDIO_FEEDBACK_METHOD_DISABLED,
42 AUDIO_FEEDBACK_METHOD_FREQUENCY_FIXED,
43 AUDIO_FEEDBACK_METHOD_FREQUENCY_FLOAT,
44 AUDIO_FEEDBACK_METHOD_FREQUENCY_POWER_OF_2, // For driver internal use only
45 AUDIO_FEEDBACK_METHOD_FIFO_COUNT
46};
47
48 using RxCallback =
49 std::function<void(AudioDevice&, const uint8_t* data, size_t len)>;
50 using TxCallback =
51 std::function<void(AudioDevice&, uint8_t* buffer, size_t& len)>;
52 using VolumeGetCallback = std::function<int16_t(AudioDevice&)>;
53 using VolumeSetCallback = std::function<void(AudioDevice&, int16_t)>;
54 using MuteGetCallback = std::function<bool(AudioDevice&)>;
55 using MuteSetCallback = std::function<void(AudioDevice&, bool)>;
56
57 static AudioDevice& instance() {
58 static AudioDevice inst;
59 return inst;
60 }
61
62 void setOutput(Print& out) { volumeStream_.setOutput(out); }
63
64 void setInput(Stream& io) {
65 volumeStream_.setOutput(io);
66 volumeStream_.setStream(io);
67 }
68
69 bool begin(AudioInfo info) {
70 setAudioInfo(info);
71 return begin();
72 }
73
74 bool begin() {
75 if (muteGetCallback_ == nullptr)
76 setMuteGetCallback([](AudioDevice<ItfNum, EPOut, EPIn>& dev) -> bool {
77 return dev.current_mute;
78 });
79
80 if (muteSetCallback_ == nullptr)
81 setMuteSetCallback([](AudioDevice<ItfNum, EPOut, EPIn>& dev, bool m) {
82 dev.current_mute = m; /* apply mute */
83 if (m) {
84 dev.volume_before_mute =
85 dev.current_volume; /* store volume before mute */
86 dev.current_volume = 0; /* set volume to 0 when muted */
87 } else {
88 dev.current_volume =
89 dev.volume_before_mute; /* restore volume after unmute */
90 }
91 dev.volumeStream_.setVolume(dev.toFloatVolume(dev.current_volume));
92 });
93
94 if (volumeGetCallback_ == nullptr)
95 setVolumeGetCallback(
96 [](AudioDevice<ItfNum, EPOut, EPIn>& dev) -> int16_t {
97 return dev.current_volume;
98 });
99
100 if (volumeSetCallback_ == nullptr)
101 setVolumeSetCallback(
102 [](AudioDevice<ItfNum, EPOut, EPIn>& dev, int16_t v) {
103 dev.current_volume = v; /* apply volume */
104 dev.volumeStream_.setVolume(dev.toFloatVolume(dev.current_volume));
105 });
106
107 if (rxCallback_ == nullptr) {
108 setRxCallback([](AudioDevice<ItfNum, EPOut, EPIn>& dev,
109 const uint8_t* data,
110 size_t len) { dev.volumeStream_.write(data, len); });
111 }
112
113 if (txCallback_ == nullptr) {
114 setTxCallback([](AudioDevice<ItfNum, EPOut, EPIn>& dev, uint8_t* data,
115 size_t len) { dev.volumeStream_.readBytes(data, len); });
116 }
117
118 return volumeStream_.begin();
119 }
120
121 void setRxCallback(RxCallback cb) { rxCallback_ = cb; }
122 void setTxCallback(TxCallback cb) { txCallback_ = cb; }
123
124 void setAudioInfo(AudioInfo info) {
125 descriptor_.setSampleRate(info.sample_rate);
126 descriptor_.setNumChannels(info.channels);
127 descriptor_.setBitsPerSample(info.bits_per_sample);
128 }
129
130 // bool send(const uint8_t* data, size_t len) {
131 // if (!tud_ready()) return false;
132 // uint16_t written = endpointWrite(ItfNum, EPIn, data, len);
133 // return written == len;
134 // }
135
136 static bool tudAudioTxDoneCb(uint8_t itf, uint8_t ep) {
137 if (itf != ItfNum || ep != EPIn) return false;
138 auto& inst = instance();
139 if (inst.txCallback_) {
140 uint16_t packetSize = inst.descriptor_.calcMaxPacketSize();
141 uint8_t buffer[packetSize];
142 size_t len = packetSize;
143 inst.txCallback_(buffer, len);
144 endpointWrite(itf, ep, buffer, len);
145 }
146 return true;
147 }
148
149 static bool tudAudioRxDoneCb(uint8_t itf, uint8_t ep) {
150 if (itf != ItfNum || ep != EPOut) return false;
151 auto& inst = instance();
152 if (inst.rxCallback_) {
153 uint8_t buffer[128];
154 int32_t rlen = endpointRead(itf, ep, buffer, sizeof(buffer));
155 if (rlen > 0) inst.rxCallback_(buffer, rlen);
156 }
157 return true;
158 }
159
160 static bool tudAudioSetReqCb(uint8_t rhport,
161 tusb_control_request_t const* req,
162 uint8_t* buffer) {
163 return instance().handleAudioClassRequest(req, buffer, true);
164 }
165
166 static bool tudAudioGetReqCb(uint8_t rhport,
167 tusb_control_request_t const* req,
168 uint8_t* buffer) {
169 return instance().handleAudioClassRequest(req, buffer, false);
170 }
171
172 static const uint8_t* tudAudioDescriptorCb(uint8_t itf, uint8_t alt,
173 uint16_t* len) {
174 return instance().descriptor_.buildDescriptor(itf, alt, len);
175 }
176
178 float volume() const { return toFloatVolume(current_volume); }
179
180 private:
181 AudioDevice() = default;
182 RxCallback rxCallback_ = nullptr;
183 TxCallback txCallback_ = nullptr;
184 VolumeGetCallback volumeGetCallback_ = nullptr;
185 VolumeSetCallback volumeSetCallback_ = nullptr;
186 MuteGetCallback muteGetCallback_ = nullptr;
187 MuteSetCallback muteSetCallback_ = nullptr;
188 bool current_mute = false;
189 int16_t current_volume = 0;
190 int16_t volume_before_mute = 0;
191 VolumeStream volumeStream_;
192 USBAudio2DescriptorBuilder descriptor_{EPIn, EPOut};
193 struct audiod_function_t {
194 audiod_function_t() { memset(this, 0, sizeof(audiod_function_t)); }
195 uint8_t n_bytes_per_sample_tx;
196 uint8_t n_channels_tx;
197 uint8_t format_type_tx = AUDIO_FORMAT_TYPE_I;
198
199 uint8_t rhport;
200 uint8_t const* p_desc =
201 nullptr; // Pointer pointing to Standard AC Interface
202 // Descriptor(4.7.1)
203 // - Audio Control descriptor defining audio function
204
205 uint8_t ep_in; // TX audio data EP.
206 uint16_t ep_in_sz; // Current size of TX EP
207 uint8_t
208 ep_in_as_intf_num; // Corresponding Standard AS Interface Descriptor
209 // (4.9.1) belonging to output terminal to which
210 // this EP belongs - 0 is invalid (this fits to
211 // UAC2 specification since AS interfaces can not
212 // have interface number equal to zero)
213 uint8_t ep_out; // Incoming (into uC) audio data EP.
214 uint16_t ep_out_sz; // Current size of RX EP
215 uint8_t
216 ep_out_as_intf_num; // Corresponding Standard AS Interface Descriptor
217 // (4.9.1) belonging to input terminal to which
218 // this EP belongs - 0 is invalid (this fits to
219 // UAC2 specification since AS interfaces can not
220 // have interface number equal to zero)
221
222 uint8_t ep_fb; // Feedback EP.
223
224 uint8_t ep_int; // Audio control interrupt EP.
225
226 bool mounted; // Device opened
227
228 /*------------- From this point, data is not cleared by bus reset
229 * -------------*/
230
231 uint16_t desc_length; // Length of audio function descriptor
232
233 struct feedback {
234 CFG_TUSB_MEM_ALIGN uint32_t
235 value; // Feedback value for asynchronous mode (in 16.16 format).
236 uint32_t
237 min_value; // min value according to UAC2 FMT-2.0 section 2.3.1.1.
238 uint32_t
239 max_value; // max value according to UAC2 FMT-2.0 section 2.3.1.1.
240
241 uint8_t
242 frame_shift; // bInterval-1 in unit of frame (FS), micro-frame (HS)
243 uint8_t compute_method;
244
245 union {
246 uint8_t power_of_2; // pre-computed power of 2 shift
247 float float_const; // pre-computed float constant
248
249 struct {
250 uint32_t sample_freq;
251 uint32_t mclk_freq;
252 } fixed;
253
254 } compute;
255
256 } feedback;
257
258 // Decoding parameters - parameters are set when alternate AS interface is
259 // set by host Coding is currently only supported for EP. Software coding
260 // corresponding to AS interfaces without EPs are not supported currently.
261 uint32_t sample_rate_tx;
262 uint16_t packet_sz_tx[3];
263 uint8_t bclock_id_tx;
264 uint8_t interval_tx;
265
266 // Encoding parameters - parameters are set when alternate AS interface is
267 // set by host
268
269 // Buffer for control requests
270 uint8_t* ctrl_buf;
271 uint8_t ctrl_buf_sz;
272
273 // Current active alternate settings
274 uint8_t* alt_setting; // We need to save the current alternate setting
275 // this way, because it is possible that there are
276 // AS interfaces which do not have an EP!
277
278 // EP Transfer buffers and FIFOs
279 tu_fifo_t ep_out_ff;
280 tu_fifo_t ep_in_ff;
281
282 // Audio control interrupt buffer - no FIFO - 6 Bytes according to UAC 2
283 // specification (p. 74)
284 CFG_TUSB_MEM_ALIGN uint8_t ep_int_buf[6];
285
286 // Linear buffer in case target MCU is not capable of handling a ring
287 // buffer FIFO e.g. no hardware buffer is available or driver is would
288 // need to be changed dramatically OR the support FIFOs are used
289 uint8_t* lin_buf_out;
290 uint8_t* lin_buf_in;
291 };
292
293 static std::vector<CFG_TUD_MEM_SECTION audiod_function_t> _audiod_fct;
294
296 float toFloatVolume(int16_t intVol) const {
297 return (32768.0f + intVol) / 65536.0f;
298 }
299
300 static inline uint16_t endpointWrite(uint8_t itf, uint8_t ep,
301 const void* buffer, uint16_t size) {
302 (void)itf;
303 return 0;
304 }
305
306 static inline int32_t endpointRead(uint8_t itf, uint8_t ep, void* buffer,
307 uint16_t size) {
308 (void)itf;
309 return 0;
310 }
311
312 void setVolumeGetCallback(VolumeGetCallback cb) { volumeGetCallback_ = cb; }
313
314 void setVolumeSetCallback(VolumeSetCallback cb) { volumeSetCallback_ = cb; }
315
316 void setMuteGetCallback(MuteGetCallback cb) { muteGetCallback_ = cb; }
317
318 void setMuteSetCallback(MuteSetCallback cb) { muteSetCallback_ = cb; }
319
320 bool handleAudioClassRequest(tusb_control_request_t const* req,
321 uint8_t* buffer, bool isSet) {
322 const uint8_t cs = (req->wValue >> 8) & 0xFF;
323 const uint8_t entityId = (req->wIndex >> 8) & 0xFF;
324 if (entityId != 2) return false;
325 switch (req->bRequest) {
326 case AUDIO_CS_REQ_CUR:
327 if (cs == AUDIO_FU_CTRL_MUTE) {
328 if (isSet) {
329 bool mute = buffer[0];
330 if (muteSetCallback_) muteSetCallback_(*this, mute);
331 } else {
332 buffer[0] = muteGetCallback_ ? muteGetCallback_(*this) : 0;
333 }
334 return true;
335 } else if (cs == AUDIO_FU_CTRL_VOLUME) {
336 if (isSet) {
337 int16_t vol = buffer[0] | (buffer[1] << 8);
338 if (volumeSetCallback_) volumeSetCallback_(*this, vol);
339 } else {
340 int16_t vol = volumeGetCallback_ ? volumeGetCallback_(*this) : 0;
341 buffer[0] = vol & 0xFF;
342 buffer[1] = (vol >> 8) & 0xFF;
343 }
344 return true;
345 }
346 break;
347 }
348 return false;
349 }
350
351 //--------------------------------------------------------------------+
352 // READ API
353 //--------------------------------------------------------------------+
354
355 static uint16_t tud_audio_n_available(uint8_t func_id) {
356 TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
357 return tu_fifo_count(&_audiod_fct[func_id].ep_out_ff);
358 }
359
360 static uint16_t tud_audio_n_read(uint8_t func_id, void* buffer,
361 uint16_t bufsize) {
362 TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
363 return tu_fifo_read_n(&_audiod_fct[func_id].ep_out_ff, buffer, bufsize);
364 }
365
366 static bool tud_audio_n_clear_ep_out_ff(uint8_t func_id) {
367 TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
368 return tu_fifo_clear(&_audiod_fct[func_id].ep_out_ff);
369 }
370
371 static tu_fifo_t* tud_audio_n_get_ep_out_ff(uint8_t func_id) {
372 if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL)
373 return &_audiod_fct[func_id].ep_out_ff;
374 return NULL;
375 }
376
377 // This function is called once an audio packet is received by the USB and is
378 // responsible for putting data from USB memory into EP_OUT_FIFO.
379 static bool audiod_rx_done_cb(uint8_t rhport, audiod_function_t* audio,
380 uint16_t n_bytes_received) {
381 uint8_t idxItf = 0;
382 uint8_t const* dummy2;
383 uint8_t idx_audio_fct = 0;
384
385 idx_audio_fct = audiod_get_audio_fct_idx(audio);
386 TU_VERIFY(audiod_get_AS_interface_index(audio->ep_out_as_intf_num, audio,
387 &idxItf, &dummy2));
388
389 // Call a weak callback here - a possibility for user to get informed an
390 // audio packet was received and data gets now loaded into EP FIFO
391 TU_VERIFY(tud_audio_rx_done_pre_read_cb(rhport, n_bytes_received,
392 idx_audio_fct, audio->ep_out,
393 audio->alt_setting[idxItf]));
394
395 // Data currently is in linear buffer, copy into EP OUT FIFO
396 TU_VERIFY(tu_fifo_write_n(&audio->ep_out_ff, audio->lin_buf_out,
397 n_bytes_received));
398
399 // Schedule for next receive
400 TU_VERIFY(usbd_edpt_xfer(rhport, audio->ep_out, audio->lin_buf_out,
401 audio->ep_out_sz),
402 false);
403
404 if (audio->feedback.compute_method == AUDIO_FEEDBACK_METHOD_FIFO_COUNT) {
405 audiod_fb_fifo_count_update(audio, tu_fifo_count(&audio->ep_out_ff));
406 }
407
408 // Call a weak callback here - a possibility for user to get informed
409 // decoding was completed
410 TU_VERIFY(tud_audio_rx_done_post_read_cb(rhport, n_bytes_received,
411 idx_audio_fct, audio->ep_out,
412 audio->alt_setting[idxItf]));
413
414 return true;
415 }
416
430 static uint16_t tud_audio_n_write(uint8_t func_id, const void* data,
431 uint16_t len) {
432 TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
433 return tu_fifo_write_n(&_audiod_fct[func_id].ep_in_ff, data, len);
434 }
435
436 static bool tud_audio_n_clear_ep_in_ff(
437 uint8_t func_id) // Delete all content in the EP IN FIFO
438 {
439 TU_VERIFY(func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL);
440 return tu_fifo_clear(&_audiod_fct[func_id].ep_in_ff);
441 }
442
443 static tu_fifo_t* tud_audio_n_get_ep_in_ff(uint8_t func_id) {
444 if (func_id < CFG_TUD_AUDIO && _audiod_fct[func_id].p_desc != NULL)
445 return &_audiod_fct[func_id].ep_in_ff;
446 return NULL;
447 }
448
449 // This function is called once a transmit of an audio packet was successfully
450 // completed. Here, we encode samples and place it in IN EP's buffer for next
451 // transmission. n_bytes_copied - Informs caller how many bytes were loaded.
452 // In case n_bytes_copied = 0, a ZLP is scheduled to inform host no data is
453 // available for current frame.
454 static bool audiod_tx_done_cb(uint8_t rhport, audiod_function_t* audio) {
455 uint8_t idxItf;
456 uint8_t const* dummy2;
457
458 uint8_t idx_audio_fct = audiod_get_audio_fct_idx(audio);
459 TU_VERIFY(audiod_get_AS_interface_index(audio->ep_in_as_intf_num, audio,
460 &idxItf, &dummy2));
461
462 // Only send something if current alternate interface is not 0 as in this
463 // case nothing is to be sent due to UAC2 specifications
464 if (audio->alt_setting[idxItf] == 0) return false;
465
466 // Call a weak callback here - a possibility for user to get informed former
467 // TX was completed and data gets now loaded into EP in buffer (in case
468 // FIFOs are used) or if no FIFOs are used the user may use this call back
469 // to load its data into the EP IN buffer by use of
470 // tud_audio_n_write_ep_in_buffer().
471 TU_VERIFY(tud_audio_tx_done_pre_load_cb(rhport, idx_audio_fct, audio->ep_in,
472 audio->alt_setting[idxItf]));
473
474 // Send everything in ISO EP FIFO
475 uint16_t n_bytes_tx;
476
477 // packet_sz_tx is based on total packet size
478 n_bytes_tx = audiod_tx_packet_size(audio->packet_sz_tx,
479 tu_fifo_count(&audio->ep_in_ff),
480 audio->ep_in_ff.depth, audio->ep_in_sz);
481 tu_fifo_read_n(&audio->ep_in_ff, audio->lin_buf_in, n_bytes_tx);
482 TU_VERIFY(
483 usbd_edpt_xfer(rhport, audio->ep_in, audio->lin_buf_in, n_bytes_tx));
484
485 // Call a weak callback here - a possibility for user to get informed former
486 // TX was completed and how many bytes were loaded for the next frame
487 TU_VERIFY(tud_audio_tx_done_post_load_cb(rhport, n_bytes_tx, idx_audio_fct,
488 audio->ep_in,
489 audio->alt_setting[idxItf]));
490
491 return true;
492 }
493};
494
495} // namespace tinyusb
Definition AudioDevice.h:17
float volume() const
Get the current volume as a float in the range 0.0 to 1.0.
Definition AudioDevice.h:178
audio_feature_unit_control_selector_t
A.17.7 - Feature Unit Control Selectors.
Definition AudioDevice.h:20
Definition Audio2DescriptorBuilder.h:10