ESPHome  2024.11.0
i2s_audio_speaker.cpp
Go to the documentation of this file.
1 #include "i2s_audio_speaker.h"
2 
3 #ifdef USE_ESP32
4 
5 #include <driver/i2s.h>
6 
8 
10 #include "esphome/core/hal.h"
11 #include "esphome/core/log.h"
12 
13 namespace esphome {
14 namespace i2s_audio {
15 
16 static const uint8_t DMA_BUFFER_DURATION_MS = 15;
17 static const size_t DMA_BUFFERS_COUNT = 4;
18 
19 static const size_t TASK_DELAY_MS = DMA_BUFFER_DURATION_MS * DMA_BUFFERS_COUNT / 2;
20 
21 static const size_t TASK_STACK_SIZE = 4096;
22 static const ssize_t TASK_PRIORITY = 23;
23 
24 static const size_t I2S_EVENT_QUEUE_COUNT = DMA_BUFFERS_COUNT + 1;
25 
26 static const char *const TAG = "i2s_audio.speaker";
27 
28 enum SpeakerEventGroupBits : uint32_t {
29  COMMAND_START = (1 << 0), // starts the speaker task
30  COMMAND_STOP = (1 << 1), // stops the speaker task
31  COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written
32  STATE_STARTING = (1 << 10),
33  STATE_RUNNING = (1 << 11),
34  STATE_STOPPING = (1 << 12),
35  STATE_STOPPED = (1 << 13),
36  ERR_INVALID_FORMAT = (1 << 14),
38  ERR_ESP_INVALID_STATE = (1 << 16),
39  ERR_ESP_INVALID_ARG = (1 << 17),
40  ERR_ESP_INVALID_SIZE = (1 << 18),
41  ERR_ESP_NO_MEM = (1 << 19),
42  ERR_ESP_FAIL = (1 << 20),
44  ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
45 };
46 
47 // Translates a SpeakerEventGroupBits ERR_ESP bit to the coressponding esp_err_t
48 static esp_err_t err_bit_to_esp_err(uint32_t bit) {
49  switch (bit) {
51  return ESP_ERR_INVALID_STATE;
53  return ESP_ERR_INVALID_ARG;
55  return ESP_ERR_INVALID_SIZE;
57  return ESP_ERR_NO_MEM;
58  default:
59  return ESP_FAIL;
60  }
61 }
62 
72 static void q15_multiplication(const int16_t *input, int16_t *output, size_t len, int16_t c) {
73  for (int i = 0; i < len; i++) {
74  int32_t acc = (int32_t) input[i] * (int32_t) c;
75  output[i] = (int16_t) (acc >> 15);
76  }
77 }
78 
79 // Lists the Q15 fixed point scaling factor for volume reduction.
80 // Has 100 values representing silence and a reduction [49, 48.5, ... 0.5, 0] dB.
81 // dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014)
82 // float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15)
83 static const std::vector<int16_t> Q15_VOLUME_SCALING_FACTORS = {
84  0, 116, 122, 130, 137, 146, 154, 163, 173, 183, 194, 206, 218, 231, 244,
85  259, 274, 291, 308, 326, 345, 366, 388, 411, 435, 461, 488, 517, 548, 580,
86  615, 651, 690, 731, 774, 820, 868, 920, 974, 1032, 1094, 1158, 1227, 1300, 1377,
87  1459, 1545, 1637, 1734, 1837, 1946, 2061, 2184, 2313, 2450, 2596, 2750, 2913, 3085, 3269,
88  3462, 3668, 3885, 4116, 4360, 4619, 4893, 5183, 5490, 5816, 6161, 6527, 6914, 7324, 7758,
89  8218, 8706, 9222, 9770, 10349, 10963, 11613, 12302, 13032, 13805, 14624, 15491, 16410, 17384, 18415,
90  19508, 20665, 21891, 23189, 24565, 26022, 27566, 29201, 30933, 32767};
91 
93  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
94 
95  this->event_group_ = xEventGroupCreate();
96 
97  if (this->event_group_ == nullptr) {
98  ESP_LOGE(TAG, "Failed to create event group");
99  this->mark_failed();
100  return;
101  }
102 }
103 
105  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
106 
107  if (event_group_bits & SpeakerEventGroupBits::STATE_STARTING) {
108  ESP_LOGD(TAG, "Starting Speaker");
110  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STARTING);
111  }
112  if (event_group_bits & SpeakerEventGroupBits::STATE_RUNNING) {
113  ESP_LOGD(TAG, "Started Speaker");
115  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
116  this->status_clear_warning();
117  this->status_clear_error();
118  }
119  if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPING) {
120  ESP_LOGD(TAG, "Stopping Speaker");
122  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
123  }
124  if (event_group_bits & SpeakerEventGroupBits::STATE_STOPPED) {
125  if (!this->task_created_) {
126  ESP_LOGD(TAG, "Stopped Speaker");
128  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ALL_BITS);
129  this->speaker_task_handle_ = nullptr;
130  }
131  }
132 
133  if (event_group_bits & SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START) {
134  this->status_set_error("Failed to start speaker task");
135  xEventGroupClearBits(this->event_group_, SpeakerEventGroupBits::ERR_TASK_FAILED_TO_START);
136  }
137 
138  if (event_group_bits & SpeakerEventGroupBits::ERR_INVALID_FORMAT) {
139  this->status_set_error("Failed to adjust I2S bus to match the incoming audio");
140  ESP_LOGE(TAG,
141  "Incompatible audio format: sample rate = %" PRIu32 ", channels = %" PRIu8 ", bits per sample = %" PRIu8,
142  this->audio_stream_info_.sample_rate, this->audio_stream_info_.channels,
143  this->audio_stream_info_.bits_per_sample);
144  }
145 
146  if (event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS) {
147  uint32_t error_bits = event_group_bits & SpeakerEventGroupBits::ALL_ERR_ESP_BITS;
148  ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(err_bit_to_esp_err(error_bits)));
149  this->status_set_warning();
150  }
151 }
152 
153 void I2SAudioSpeaker::set_volume(float volume) {
154  this->volume_ = volume;
155 #ifdef USE_AUDIO_DAC
156  if (this->audio_dac_ != nullptr) {
157  if (volume > 0.0) {
158  this->audio_dac_->set_mute_off();
159  }
160  this->audio_dac_->set_volume(volume);
161  } else
162 #endif
163  {
164  // Fallback to software volume control by using a Q15 fixed point scaling factor
165  ssize_t decibel_index = remap<ssize_t, float>(volume, 0.0f, 1.0f, 0, Q15_VOLUME_SCALING_FACTORS.size() - 1);
166  this->q15_volume_factor_ = Q15_VOLUME_SCALING_FACTORS[decibel_index];
167  }
168 }
169 
170 void I2SAudioSpeaker::set_mute_state(bool mute_state) {
171  this->mute_state_ = mute_state;
172 #ifdef USE_AUDIO_DAC
173  if (this->audio_dac_) {
174  if (mute_state) {
175  this->audio_dac_->set_mute_on();
176  } else {
177  this->audio_dac_->set_mute_off();
178  }
179  } else
180 #endif
181  {
182  if (mute_state) {
183  // Fallback to software volume control and scale by 0
184  this->q15_volume_factor_ = 0;
185  } else {
186  // Revert to previous volume when unmuting
187  this->set_volume(this->volume_);
188  }
189  }
190 }
191 
192 size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
193  if (this->is_failed()) {
194  ESP_LOGE(TAG, "Cannot play audio, speaker failed to setup");
195  return 0;
196  }
197  if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
198  this->start();
199  }
200 
201  size_t bytes_written = 0;
202  if ((this->state_ == speaker::STATE_RUNNING) && (this->audio_ring_buffer_.use_count() == 1)) {
203  // Only one owner of the ring buffer (the speaker task), so the ring buffer is allocated and no other components are
204  // attempting to write to it.
205 
206  // Temporarily share ownership of the ring buffer so it won't be deallocated while writing
207  std::shared_ptr<RingBuffer> temp_ring_buffer = this->audio_ring_buffer_;
208  bytes_written = temp_ring_buffer->write_without_replacement((void *) data, length, ticks_to_wait);
209  }
210 
211  return bytes_written;
212 }
213 
215  if (this->audio_ring_buffer_ != nullptr) {
216  return this->audio_ring_buffer_->available() > 0;
217  }
218  return false;
219 }
220 
221 void I2SAudioSpeaker::speaker_task(void *params) {
222  I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
223  uint32_t event_group_bits =
224  xEventGroupWaitBits(this_speaker->event_group_,
226  SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY, // Bit message to read
227  pdTRUE, // Clear the bits on exit
228  pdFALSE, // Don't wait for all the bits,
229  portMAX_DELAY); // Block indefinitely until a bit is set
230 
232  // Received a stop signal before the task was requested to start
233  this_speaker->delete_task_(0);
234  }
235 
236  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STARTING);
237 
238  audio::AudioStreamInfo audio_stream_info = this_speaker->audio_stream_info_;
239  const ssize_t bytes_per_sample = audio_stream_info.get_bytes_per_sample();
240  const uint8_t number_of_channels = audio_stream_info.channels;
241 
242  const size_t dma_buffers_size = DMA_BUFFERS_COUNT * DMA_BUFFER_DURATION_MS * this_speaker->sample_rate_ / 1000 *
243  bytes_per_sample * number_of_channels;
244  const size_t ring_buffer_size =
245  this_speaker->buffer_duration_ms_ * this_speaker->sample_rate_ / 1000 * bytes_per_sample * number_of_channels;
246 
247  if (this_speaker->send_esp_err_to_event_group_(this_speaker->allocate_buffers_(dma_buffers_size, ring_buffer_size))) {
248  // Failed to allocate buffers
249  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
250  this_speaker->delete_task_(dma_buffers_size);
251  }
252 
253  if (this_speaker->send_esp_err_to_event_group_(this_speaker->start_i2s_driver_())) {
254  // Failed to start I2S driver
255  this_speaker->delete_task_(dma_buffers_size);
256  }
257 
258  if (!this_speaker->send_esp_err_to_event_group_(this_speaker->reconfigure_i2s_stream_info_(audio_stream_info))) {
259  // Successfully set the I2S stream info, ready to write audio data to the I2S port
260 
261  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_RUNNING);
262 
263  bool stop_gracefully = false;
264  uint32_t last_data_received_time = millis();
265  bool tx_dma_underflow = false;
266 
267  while (!this_speaker->timeout_.has_value() ||
268  (millis() - last_data_received_time) <= this_speaker->timeout_.value()) {
269  event_group_bits = xEventGroupGetBits(this_speaker->event_group_);
270 
271  if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP) {
272  break;
273  }
274  if (event_group_bits & SpeakerEventGroupBits::COMMAND_STOP_GRACEFULLY) {
275  stop_gracefully = true;
276  }
277 
278  i2s_event_t i2s_event;
279  while (xQueueReceive(this_speaker->i2s_event_queue_, &i2s_event, 0)) {
280  if (i2s_event.type == I2S_EVENT_TX_Q_OVF) {
281  tx_dma_underflow = true;
282  }
283  }
284 
285  size_t bytes_to_read = dma_buffers_size;
286  size_t bytes_read = this_speaker->audio_ring_buffer_->read((void *) this_speaker->data_buffer_, bytes_to_read,
287  pdMS_TO_TICKS(TASK_DELAY_MS));
288 
289  if (bytes_read > 0) {
290  size_t bytes_written = 0;
291 
292  if ((audio_stream_info.bits_per_sample == 16) && (this_speaker->q15_volume_factor_ < INT16_MAX)) {
293  // Scale samples by the volume factor in place
294  q15_multiplication((int16_t *) this_speaker->data_buffer_, (int16_t *) this_speaker->data_buffer_,
295  bytes_read / sizeof(int16_t), this_speaker->q15_volume_factor_);
296  }
297 
298  if (audio_stream_info.bits_per_sample == (uint8_t) this_speaker->bits_per_sample_) {
299  i2s_write(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read, &bytes_written,
300  portMAX_DELAY);
301  } else if (audio_stream_info.bits_per_sample < (uint8_t) this_speaker->bits_per_sample_) {
302  i2s_write_expand(this_speaker->parent_->get_port(), this_speaker->data_buffer_, bytes_read,
303  audio_stream_info.bits_per_sample, this_speaker->bits_per_sample_, &bytes_written,
304  portMAX_DELAY);
305  }
306 
307  if (bytes_written != bytes_read) {
308  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_SIZE);
309  }
310  tx_dma_underflow = false;
311  last_data_received_time = millis();
312  } else {
313  // No data received
314  if (stop_gracefully && tx_dma_underflow) {
315  break;
316  }
317  }
318  }
319  } else {
320  // Couldn't configure the I2S port to be compatible with the incoming audio
321  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::ERR_INVALID_FORMAT);
322  }
323  i2s_zero_dma_buffer(this_speaker->parent_->get_port());
324 
325  xEventGroupSetBits(this_speaker->event_group_, SpeakerEventGroupBits::STATE_STOPPING);
326 
327  i2s_driver_uninstall(this_speaker->parent_->get_port());
328 
329  this_speaker->parent_->unlock();
330  this_speaker->delete_task_(dma_buffers_size);
331 }
332 
334  if (!this->is_ready() || this->is_failed() || this->status_has_error())
335  return;
336  if ((this->state_ == speaker::STATE_STARTING) || (this->state_ == speaker::STATE_RUNNING))
337  return;
338 
339  if (this->speaker_task_handle_ == nullptr) {
340  xTaskCreate(I2SAudioSpeaker::speaker_task, "speaker_task", TASK_STACK_SIZE, (void *) this, TASK_PRIORITY,
341  &this->speaker_task_handle_);
342  }
343 
344  if (this->speaker_task_handle_ != nullptr) {
345  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_START);
346  this->task_created_ = true;
347  } else {
349  }
350 }
351 
352 void I2SAudioSpeaker::stop() { this->stop_(false); }
353 
354 void I2SAudioSpeaker::finish() { this->stop_(true); }
355 
356 void I2SAudioSpeaker::stop_(bool wait_on_empty) {
357  if (this->is_failed())
358  return;
359  if (this->state_ == speaker::STATE_STOPPED)
360  return;
361 
362  if (wait_on_empty) {
364  } else {
365  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::COMMAND_STOP);
366  }
367 }
368 
370  switch (err) {
371  case ESP_OK:
372  return false;
373  case ESP_ERR_INVALID_STATE:
375  return true;
376  case ESP_ERR_INVALID_ARG:
377  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_INVALID_ARG);
378  return true;
379  case ESP_ERR_INVALID_SIZE:
381  return true;
382  case ESP_ERR_NO_MEM:
383  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_NO_MEM);
384  return true;
385  default:
386  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::ERR_ESP_FAIL);
387  return true;
388  }
389 }
390 
391 esp_err_t I2SAudioSpeaker::allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size) {
392  if (this->data_buffer_ == nullptr) {
393  // Allocate data buffer for temporarily storing audio from the ring buffer before writing to the I2S bus
395  this->data_buffer_ = allocator.allocate(data_buffer_size);
396  }
397 
398  if (this->data_buffer_ == nullptr) {
399  return ESP_ERR_NO_MEM;
400  }
401 
402  if (this->audio_ring_buffer_.use_count() == 0) {
403  // Allocate ring buffer. Uses a shared_ptr to ensure it isn't improperly deallocated.
404  this->audio_ring_buffer_ = RingBuffer::create(ring_buffer_size);
405  }
406 
407  if (this->audio_ring_buffer_ == nullptr) {
408  return ESP_ERR_NO_MEM;
409  }
410 
411  return ESP_OK;
412 }
413 
415  if (!this->parent_->try_lock()) {
416  return ESP_ERR_INVALID_STATE;
417  }
418 
419  int dma_buffer_length = DMA_BUFFER_DURATION_MS * this->sample_rate_ / 1000;
420 
421  i2s_driver_config_t config = {
422  .mode = (i2s_mode_t) (this->i2s_mode_ | I2S_MODE_TX),
423  .sample_rate = this->sample_rate_,
424  .bits_per_sample = this->bits_per_sample_,
425  .channel_format = this->channel_,
426  .communication_format = this->i2s_comm_fmt_,
427  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
428  .dma_buf_count = DMA_BUFFERS_COUNT,
429  .dma_buf_len = dma_buffer_length,
430  .use_apll = this->use_apll_,
431  .tx_desc_auto_clear = true,
432  .fixed_mclk = I2S_PIN_NO_CHANGE,
433  .mclk_multiple = I2S_MCLK_MULTIPLE_256,
434  .bits_per_chan = this->bits_per_channel_,
435 #if SOC_I2S_SUPPORTS_TDM
436  .chan_mask = (i2s_channel_t) (I2S_TDM_ACTIVE_CH0 | I2S_TDM_ACTIVE_CH1),
437  .total_chan = 2,
438  .left_align = false,
439  .big_edin = false,
440  .bit_order_msb = false,
441  .skip_msk = false,
442 #endif
443  };
444 #if SOC_I2S_SUPPORTS_DAC
445  if (this->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
446  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN);
447  }
448 #endif
449 
450  esp_err_t err =
451  i2s_driver_install(this->parent_->get_port(), &config, I2S_EVENT_QUEUE_COUNT, &this->i2s_event_queue_);
452  if (err != ESP_OK) {
453  // Failed to install the driver, so unlock the I2S port
454  this->parent_->unlock();
455  return err;
456  }
457 
458 #if SOC_I2S_SUPPORTS_DAC
459  if (this->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
460 #endif
461  i2s_pin_config_t pin_config = this->parent_->get_pin_config();
462  pin_config.data_out_num = this->dout_pin_;
463 
464  err = i2s_set_pin(this->parent_->get_port(), &pin_config);
465 #if SOC_I2S_SUPPORTS_DAC
466  } else {
467  i2s_set_dac_mode(this->internal_dac_mode_);
468  }
469 #endif
470 
471  if (err != ESP_OK) {
472  // Failed to set the data out pin, so uninstall the driver and unlock the I2S port
473  i2s_driver_uninstall(this->parent_->get_port());
474  this->parent_->unlock();
475  }
476 
477  return err;
478 }
479 
481  if (this->i2s_mode_ & I2S_MODE_MASTER) {
482  // ESP controls for the the I2S bus, so adjust the sample rate and bits per sample to match the incoming audio
483  this->sample_rate_ = audio_stream_info.sample_rate;
484  this->bits_per_sample_ = (i2s_bits_per_sample_t) audio_stream_info.bits_per_sample;
485  } else if (this->sample_rate_ != audio_stream_info.sample_rate) {
486  // Can't reconfigure I2S bus, so the sample rate must match the configured value
487  return ESP_ERR_INVALID_ARG;
488  }
489 
490  if ((i2s_bits_per_sample_t) audio_stream_info.bits_per_sample > this->bits_per_sample_) {
491  // Currently can't handle the case when the incoming audio has more bits per sample than the configured value
492  return ESP_ERR_INVALID_ARG;
493  }
494 
495  if (audio_stream_info.channels == 1) {
496  return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_MONO);
497  } else if (audio_stream_info.channels == 2) {
498  return i2s_set_clk(this->parent_->get_port(), this->sample_rate_, this->bits_per_sample_, I2S_CHANNEL_STEREO);
499  }
500 
501  return ESP_ERR_INVALID_ARG;
502 }
503 
504 void I2SAudioSpeaker::delete_task_(size_t buffer_size) {
505  this->audio_ring_buffer_.reset(); // Releases onwership of the shared_ptr
506 
507  if (this->data_buffer_ != nullptr) {
509  allocator.deallocate(this->data_buffer_, buffer_size);
510  this->data_buffer_ = nullptr;
511  }
512 
513  xEventGroupSetBits(this->event_group_, SpeakerEventGroupBits::STATE_STOPPED);
514 
515  this->task_created_ = false;
516  vTaskDelete(nullptr);
517 }
518 
519 } // namespace i2s_audio
520 } // namespace esphome
521 
522 #endif // USE_ESP32
value_type const & value() const
Definition: optional.h:89
size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override
Plays the provided audio data.
void stop_(bool wait_on_empty)
Sends a stop command to the speaker task via event_group_.
bool send_esp_err_to_event_group_(esp_err_t err)
Sets the corresponding ERR_ESP event group bits.
esp_err_t allocate_buffers_(size_t data_buffer_size, size_t ring_buffer_size)
Allocates the data buffer and ring buffer.
virtual bool set_mute_off()=0
virtual bool set_mute_on()=0
static void speaker_task(void *params)
Function for the FreeRTOS task handling audio output.
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
bool is_failed() const
Definition: component.cpp:143
void set_volume(float volume) override
Sets the volume of the speaker.
T * allocate(size_t n)
Definition: helpers.h:681
esp_err_t reconfigure_i2s_stream_info_(audio::AudioStreamInfo &audio_stream_info)
Adjusts the I2S driver configuration to match the incoming audio stream.
bool has_value() const
Definition: optional.h:87
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
bool is_ready() const
Definition: component.cpp:144
bool status_has_error() const
Definition: component.cpp:150
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
i2s_channel_fmt_t channel_
Definition: i2s_audio.h:25
void status_clear_warning()
Definition: component.cpp:166
void set_mute_state(bool mute_state) override
Mutes or unmute the speaker.
void deallocate(T *p, size_t n)
Definition: helpers.h:700
virtual bool set_volume(float volume)=0
void status_clear_error()
Definition: component.cpp:172
i2s_bits_per_sample_t bits_per_sample_
Definition: i2s_audio.h:27
std::string size_t len
Definition: helpers.h:293
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
audio_dac::AudioDac * audio_dac_
Definition: speaker.h:105
void delete_task_(size_t buffer_size)
Deletes the speaker&#39;s task.
std::shared_ptr< RingBuffer > audio_ring_buffer_
esp_err_t start_i2s_driver_()
Starts the ESP32 I2S driver.
uint16_t length
Definition: tt21100.cpp:12
size_t get_bytes_per_sample() const
Definition: audio.h:14
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
i2s_bits_per_chan_t bits_per_channel_
Definition: i2s_audio.h:28
An STL allocator that uses SPI or internal RAM.
Definition: helpers.h:666
static std::unique_ptr< RingBuffer > create(size_t len)
Definition: ring_buffer.cpp:22
audio::AudioStreamInfo audio_stream_info_
Definition: speaker.h:100