24#include <driver/i2s_std.h>
25#include "WrappedAudioOutputBase.h"
34#if SOC_I2S_HW_VERSION_2
35#undef I2S_STD_CLK_DEFAULT_CONFIG
36#define I2S_STD_CLK_DEFAULT_CONFIG(rate) \
37 { .sample_rate_hz = rate, .clk_src = I2S_CLK_SRC_DEFAULT, .ext_clk_freq_hz = 0, .mclk_multiple = I2S_MCLK_MULTIPLE_256, }
53 ESP32I2SAudio(int8_t bclk = 0, int8_t ws = 1, int8_t dout = 2, int8_t mclk = -1) {
78 void setPins(int8_t bclk, int8_t ws, int8_t dout, int8_t mclk = -1) {
107 bool setBuffers(
size_t buffers,
size_t bufferWords, int32_t silenceSample = 0)
override {
110 _bufferWords = bufferWords;
111 _silenceSample = silenceSample;
124 if (!_running && bps == 16) {
138 if (_running && (_sampleRate != freq)) {
139 i2s_std_clk_config_t clk_cfg;
140 clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG((uint32_t)freq);
141 i2s_channel_disable(_tx_handle);
142 i2s_channel_reconfig_std_clock(_tx_handle, &clk_cfg);
143 i2s_channel_enable(_tx_handle);
176 i2s_chan_config_t chan_cfg = I2S_CHANNEL_DEFAULT_CONFIG(I2S_NUM_AUTO, I2S_ROLE_MASTER);
177 chan_cfg.dma_desc_num = _buffers;
178 chan_cfg.dma_frame_num = _bufferWords * 4;
179 i2s_new_channel(&chan_cfg, &_tx_handle,
nullptr);
181 i2s_std_config_t std_cfg = {
182 .clk_cfg = I2S_STD_CLK_DEFAULT_CONFIG(_sampleRate),
183 .slot_cfg = I2S_STD_MSB_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, I2S_SLOT_MODE_STEREO),
185 .mclk = _mclk < 0 ? I2S_GPIO_UNUSED : (gpio_num_t)_mclk,
186 .bclk = (gpio_num_t)_bclk,
187 .ws = (gpio_num_t)_ws,
188 .dout = (gpio_num_t)_dout,
189 .din = I2S_GPIO_UNUSED,
191 .mclk_inv = _mclkInv,
192 .bclk_inv = _bclkInv,
197 i2s_channel_init_std_mode(_tx_handle, &std_cfg);
200 int16_t a[2] = {0, 0};
203 i2s_channel_preload_data(_tx_handle, (
void*)a,
sizeof(a), &written);
204 _totalAvailable += written;
208 i2s_event_callbacks_t _cbs = {
210 .on_recv_q_ovf =
nullptr,
214 i2s_channel_register_event_callback(_tx_handle, &_cbs, (
void *)
this);
215 xTaskCreate(
_taskShim,
"BackgroundAudioI2S", 8192, (
void*)
this, 2, &_taskHandle);
216 _running = ESP_OK == i2s_channel_enable(_tx_handle);
225 static IRAM_ATTR
bool _onSent(i2s_chan_handle_t handle, i2s_event_data_t *event,
void *user_ctx) {
226 return ((
ESP32I2SAudio *)user_ctx)->_onSentCB(handle, event);
234 static IRAM_ATTR
bool _onSentUnder(i2s_chan_handle_t handle, i2s_event_data_t *event,
void *user_ctx) {
235 return ((
ESP32I2SAudio *)user_ctx)->_onSentCB(handle, event,
true);
251 uint32_t ulNotifiedValue;
252 xTaskNotifyWait(0, ULONG_MAX, &ulNotifiedValue, portMAX_DELAY);
256 int32_t size = (int32_t)ulNotifiedValue;
263 if (_available > _totalAvailable) {
264 _available = _totalAvailable;
306 IRAM_ATTR
bool _onSentCB(i2s_chan_handle_t handle, i2s_event_data_t *event,
bool underflow =
false) {
307 BaseType_t xHigherPriorityTaskWoken;
308 xHigherPriorityTaskWoken = pdFALSE;
314 xTaskNotifyFromISR(_taskHandle, event->size * (underflow ? -1 : 1), eSetValueWithOverwrite, &xHigherPriorityTaskWoken);
316 return (
bool)xHigherPriorityTaskWoken;
327 i2s_channel_disable(_tx_handle);
328 i2s_del_channel(_tx_handle);
329 vTaskDelete(_taskHandle);
342 auto ret = _underflowed;
343 _underflowed =
false;
369 size_t write(
const uint8_t *buffer,
size_t size)
override {
371 i2s_channel_write(_tx_handle, buffer, size, &written, 0);
373 if (written != size) {
375 }
else if (_available >= written) {
376 _available -= written;
408 bool _bclkInv =
false;
410 bool _mclkInv =
false;
411 bool _underflowed =
false;
415 uint32_t _silenceSample;
416 TaskHandle_t _taskHandle = 0;
420 i2s_chan_handle_t _tx_handle;
421 size_t _totalAvailable = 0;
422 size_t _available = 0;
424 uint32_t _frames = 0;
425 uint32_t _underflows = 0;
I2S object with IRQ-based callbacks to a FreeRTOS task, for use with BackgroundAudio.
Definition ESP32I2SAudio.h:43
ESP32I2SAudio(int8_t bclk=0, int8_t ws=1, int8_t dout=2, int8_t mclk=-1)
Construct ESP32-based I2S object with IRQ-based callbacks to a FreeRTOS task, for use with Background...
Definition ESP32I2SAudio.h:53
static void _taskShim(void *pvParameters)
C-language shim to start the real object's task.
Definition ESP32I2SAudio.h:241
void _backgroundTask()
Background I2S DMA buffer notification task. Tracks number of bytes available to be written.
Definition ESP32I2SAudio.h:248
uint32_t irqs()
Get the number of input data shifts processed by decoder since begin
Definition ESP32I2SAudio.h:288
bool end() override
Stop the I2S device.
Definition ESP32I2SAudio.h:325
IRAM_ATTR bool _onSentCB(i2s_chan_handle_t handle, i2s_event_data_t *event, bool underflow=false)
Object-based callback for I2S Sent notification.
Definition ESP32I2SAudio.h:306
uint32_t frames()
Get number of DMA frames(buffers) processed.
Definition ESP32I2SAudio.h:279
bool setStereo(bool stereo=true) override
Set mono or stereo mode. Only stereo supported.
Definition ESP32I2SAudio.h:156
bool setFrequency(int freq) override
Set the sample rate (LRCLK/WS) of the I2S interface. Can be called while running.
Definition ESP32I2SAudio.h:137
uint32_t underflows()
Get the number of times the MP3 decoder has underflowed waiting on raw data since begin
Definition ESP32I2SAudio.h:297
void setPins(int8_t bclk, int8_t ws, int8_t dout, int8_t mclk=-1)
Set the I2S GPIO pins before calling begin
Definition ESP32I2SAudio.h:78
bool getUnderflow() override
Determine if there was an underflow since the last time this was called. Cleared on read.
Definition ESP32I2SAudio.h:340
int availableForWrite() override
Determine the number of bytes we can write to the DMA buffers at this instant.
Definition ESP32I2SAudio.h:398
static IRAM_ATTR bool _onSent(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
C-language wrapper for I2S Sent event.
Definition ESP32I2SAudio.h:225
size_t write(uint8_t d) override
Write single byte to I2S buffers. Not supported.
Definition ESP32I2SAudio.h:389
static IRAM_ATTR bool _onSentUnder(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx)
C-language wrapper for I2S Sent Underflow event.
Definition ESP32I2SAudio.h:234
bool setBuffers(size_t buffers, size_t bufferWords, int32_t silenceSample=0) override
Set the size and number of the I2S buffers before begin
Definition ESP32I2SAudio.h:107
bool setBitsPerSample(int bps) override
Set the bits per sample for the I2S output. Only 16-bit supported.
Definition ESP32I2SAudio.h:123
void onTransmit(void(*cb)(void *), void *cbData) override
Set the callback function to be called every DMA buffer completion.
Definition ESP32I2SAudio.h:354
bool begin() override
Start the I2S interface.
Definition ESP32I2SAudio.h:170
size_t write(const uint8_t *buffer, size_t size) override
Write data to the I2S interface. Not legal from IRQ context. Will not block and may write less than r...
Definition ESP32I2SAudio.h:369
void setInverted(bool bclk, bool ws, bool mclk=false)
Set the I2S GPIO inversions before calling begin
Definition ESP32I2SAudio.h:92