ESPHome  2024.4.1
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 #include "esphome/core/hal.h"
9 #include "esphome/core/log.h"
10 
11 namespace esphome {
12 namespace i2s_audio {
13 
14 static const size_t BUFFER_COUNT = 20;
15 
16 static const char *const TAG = "i2s_audio.speaker";
17 
19  ESP_LOGCONFIG(TAG, "Setting up I2S Audio Speaker...");
20 
21  this->buffer_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(DataEvent));
22  this->event_queue_ = xQueueCreate(BUFFER_COUNT, sizeof(TaskEvent));
23 }
24 
27  if (!this->parent_->try_lock()) {
28  return; // Waiting for another i2s component to return lock
29  }
31 
32  xTaskCreate(I2SAudioSpeaker::player_task, "speaker_task", 8192, (void *) this, 1, &this->player_task_handle_);
33 }
34 
35 void I2SAudioSpeaker::player_task(void *params) {
36  I2SAudioSpeaker *this_speaker = (I2SAudioSpeaker *) params;
37 
38  TaskEvent event;
40  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
41 
42  i2s_driver_config_t config = {
43  .mode = (i2s_mode_t) (I2S_MODE_MASTER | I2S_MODE_TX),
44  .sample_rate = 16000,
45  .bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
46  .channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
47  .communication_format = I2S_COMM_FORMAT_STAND_I2S,
48  .intr_alloc_flags = ESP_INTR_FLAG_LEVEL1,
49  .dma_buf_count = 8,
50  .dma_buf_len = 128,
51  .use_apll = false,
52  .tx_desc_auto_clear = true,
53  .fixed_mclk = I2S_PIN_NO_CHANGE,
54  .mclk_multiple = I2S_MCLK_MULTIPLE_DEFAULT,
55  .bits_per_chan = I2S_BITS_PER_CHAN_DEFAULT,
56  };
57 #if SOC_I2S_SUPPORTS_DAC
58  if (this_speaker->internal_dac_mode_ != I2S_DAC_CHANNEL_DISABLE) {
59  config.mode = (i2s_mode_t) (config.mode | I2S_MODE_DAC_BUILT_IN);
60  }
61 #endif
62 
63  esp_err_t err = i2s_driver_install(this_speaker->parent_->get_port(), &config, 0, nullptr);
64  if (err != ESP_OK) {
65  event.type = TaskEventType::WARNING;
66  event.err = err;
67  xQueueSend(this_speaker->event_queue_, &event, 0);
68  event.type = TaskEventType::STOPPED;
69  xQueueSend(this_speaker->event_queue_, &event, 0);
70  while (true) {
71  delay(10);
72  }
73  }
74 
75 #if SOC_I2S_SUPPORTS_DAC
76  if (this_speaker->internal_dac_mode_ == I2S_DAC_CHANNEL_DISABLE) {
77 #endif
78  i2s_pin_config_t pin_config = this_speaker->parent_->get_pin_config();
79  pin_config.data_out_num = this_speaker->dout_pin_;
80 
81  i2s_set_pin(this_speaker->parent_->get_port(), &pin_config);
82 #if SOC_I2S_SUPPORTS_DAC
83  } else {
84  i2s_set_dac_mode(this_speaker->internal_dac_mode_);
85  }
86 #endif
87 
88  DataEvent data_event;
89 
90  event.type = TaskEventType::STARTED;
91  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
92 
93  int16_t buffer[BUFFER_SIZE / 2];
94 
95  while (true) {
96  if (xQueueReceive(this_speaker->buffer_queue_, &data_event, 100 / portTICK_PERIOD_MS) != pdTRUE) {
97  break; // End of audio from main thread
98  }
99  if (data_event.stop) {
100  // Stop signal from main thread
101  xQueueReset(this_speaker->buffer_queue_); // Flush queue
102  break;
103  }
104  size_t bytes_written;
105 
106  memmove(buffer, data_event.data, data_event.len);
107  size_t remaining = data_event.len / 2;
108  size_t current = 0;
109 
110  while (remaining > 0) {
111  uint32_t sample = (buffer[current] << 16) | (buffer[current] & 0xFFFF);
112 
113  esp_err_t err = i2s_write(this_speaker->parent_->get_port(), &sample, sizeof(sample), &bytes_written,
114  (10 / portTICK_PERIOD_MS));
115  if (err != ESP_OK) {
116  event = {.type = TaskEventType::WARNING, .err = err};
117  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
118  continue;
119  }
120  remaining--;
121  current++;
122  }
123 
124  event.type = TaskEventType::PLAYING;
125  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
126  }
127 
128  i2s_zero_dma_buffer(this_speaker->parent_->get_port());
129 
130  event.type = TaskEventType::STOPPING;
131  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
132 
133  i2s_driver_uninstall(this_speaker->parent_->get_port());
134 
135  event.type = TaskEventType::STOPPED;
136  xQueueSend(this_speaker->event_queue_, &event, portMAX_DELAY);
137 
138  while (true) {
139  delay(10);
140  }
141 }
142 
144  if (this->state_ == speaker::STATE_STOPPED)
145  return;
146  if (this->state_ == speaker::STATE_STARTING) {
148  return;
149  }
151  DataEvent data;
152  data.stop = true;
153  xQueueSendToFront(this->buffer_queue_, &data, portMAX_DELAY);
154 }
155 
157  TaskEvent event;
158  if (xQueueReceive(this->event_queue_, &event, 0) == pdTRUE) {
159  switch (event.type) {
161  ESP_LOGD(TAG, "Starting I2S Audio Speaker");
162  break;
164  ESP_LOGD(TAG, "Started I2S Audio Speaker");
165  break;
167  ESP_LOGD(TAG, "Stopping I2S Audio Speaker");
168  break;
170  this->status_clear_warning();
171  break;
174  vTaskDelete(this->player_task_handle_);
175  this->player_task_handle_ = nullptr;
176  this->parent_->unlock();
177  xQueueReset(this->buffer_queue_);
178  ESP_LOGD(TAG, "Stopped I2S Audio Speaker");
179  break;
181  ESP_LOGW(TAG, "Error writing to I2S: %s", esp_err_to_name(event.err));
182  this->status_set_warning();
183  break;
184  }
185  }
186 }
187 
189  switch (this->state_) {
191  this->start_();
192  break;
195  this->watch_();
196  break;
198  break;
199  }
200 }
201 
202 size_t I2SAudioSpeaker::play(const uint8_t *data, size_t length) {
203  if (this->state_ != speaker::STATE_RUNNING && this->state_ != speaker::STATE_STARTING) {
204  this->start();
205  }
206  size_t remaining = length;
207  size_t index = 0;
208  while (remaining > 0) {
209  DataEvent event;
210  event.stop = false;
211  size_t to_send_length = std::min(remaining, BUFFER_SIZE);
212  event.len = to_send_length;
213  memcpy(event.data, data + index, to_send_length);
214  if (xQueueSend(this->buffer_queue_, &event, 0) != pdTRUE) {
215  return index;
216  }
217  remaining -= to_send_length;
218  index += to_send_length;
219  }
220  return index;
221 }
222 
223 bool I2SAudioSpeaker::has_buffered_data() const { return uxQueueMessagesWaiting(this->buffer_queue_) > 0; }
224 
225 } // namespace i2s_audio
226 } // namespace esphome
227 
228 #endif // USE_ESP32
void status_set_warning(const char *message="unspecified")
Definition: component.cpp:151
static void player_task(void *params)
void status_clear_warning()
Definition: component.cpp:166
size_t play(const uint8_t *data, size_t length) override
uint16_t length
Definition: tt21100.cpp:12
This is a workaround until we can figure out a way to get the tflite-micro idf component code availab...
Definition: a01nyub.cpp:7
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26