BirdNet-goMikrofon/Arduino-audio-tools-main/arduino-audio-tools-main/examples/examples-communication/usb/usb-rx-i2s/usb-rx-i2s.ino

117 lines
3.8 KiB
Arduino
Raw Normal View History

2026-09-26 01:20:55 +02:00
/**
* @file usb-rx-i2s.ino (speaker)
* @brief USB Audio RX (host → device) forwarded directly to I2S output.
*
* The device appears to the host as a USB speaker (UAC2 RX). Instead of
* copying data out in loop(), the audio is pushed to I2S from the
* rxDoneCallback as soon as a USB packet arrives, avoiding an extra buffer
* copy. The I2S output's availableForWrite() is reported back to the host
* as a feedback percentage so the host can pace its sample rate to match
* the I2S consumer.
*
* Data flow:
* Host → USB isochronous OUT endpoint → USBAudioStream.bufferRx()
* → rxDone() → I2SStream.write()
*
* @note for ESP32: Board settings (Arduino IDE):
* Tools → Board : ESP32S3 Dev Module (or your board)
* Tools → USB Mode : USB-OTG (TinyUSB)
* Tools → USB CDC On Boot : Disabled
* If you want to use the CDC serial port for logging: define USBCDC
* MySerial; and use MySerial!
*
* @note for boards using Adafruit TinyUSB core (e.g. RP2040, STM32, SAMD):
* Board settings (Arduino IDE): USB Mode: USB OTG (TinyUSB)
*
* @note This example only works for platforms where the i2s availableForWrite()
* is a measure of the number of bytes that can be written to the I2S output
* buffer. This is true for ESP32 and RP2040, but not for all platforms.
*
* @author Phil Schatzmann
* @copyright GPLv3
*/
#include "AudioTools.h"
#include "AudioTools/Communication/USB/USBAudioStream.h"
// CDC output is pltform dependent
#ifdef ESP32
USBCDC MySerial;
#else
auto& MySerial = Serial;
#endif
AudioInfo info(48000, 2, 16);
USBAudioStream in;
I2SStream out;
// diagnostics: total capacity of the I2S output buffer, so we can print
// out.availableForWrite() as a percentage (see setup()/loop())
size_t i2s_buffer_capacity = 0;
// Callback to push data to I2S
bool rxDone(USBAudioDeviceBase* p_usb, uint8_t,
USBAudioDeviceBase::audiod_function_t*, uint16_t) {
// read Audio data
int len = in.bufferRx().available();
uint8_t data[len];
in.bufferRx().readArray(data, len);
// write to I2S
out.write(data, len);
// update feedback
int i2s_free_pct = 100 * out.availableForWrite() / (int)i2s_buffer_capacity;
in.setFeedbackPercent(i2s_free_pct);
return true;
}
void setup() {
// Manual begin() is required on core without built-in support e.g. mbed
// rp2040
if (!TinyUSBDevice.isInitialized()) {
TinyUSBDevice.begin(0);
}
// Register the CDC interface first (gets interfaces 0-1).
MySerial.begin(115200);
AudioToolsLogger.begin(Serial, AudioToolsLogLevel::Warning);
// Start I2S so it knows the audio format.
auto cfg = out.defaultConfig(TX_MODE);
cfg.copyFrom(info);
cfg.buffer_size = 256;
cfg.buffer_count = 10;
out.begin(cfg);
// Capture the true capacity directly from the driver right after begin()
// (buffer is empty here, so availableForWrite() == full capacity) --
// avoids assuming buffer_size/buffer_count map 1:1 to availableForWrite()'s
// units, which they don't on RP2040 (confirmed by the initial >100%
// readings).
i2s_buffer_capacity = (size_t)out.availableForWrite();
// We can avoid a copy in the loop by triggering a write to i2s when we
// receive a packet
in.setRxDoneCallback(rxDone);
// Register USB audio in RX mode (host → device, i.e. USB speaker).
// begin_usb defaults to false so USB.begin() below controls the start.
auto config = in.defaultConfig(RX_MODE);
config.copyFrom(info);
config.fifo_packets = 1;
in.begin(config);
// If already enumerated, additional class driver begin() e.g msc, hid, midi
// won't take effect until re-enumeration: on ESP32 you can alternatively call
// USB.begin()
if (TinyUSBDevice.mounted()) {
TinyUSBDevice.detach();
delay(10);
TinyUSBDevice.attach();
}
MySerial.println("USB audio RX + CDC started");
}
void loop() {}