ESPHome  2025.2.0
resampler_speaker.cpp
Go to the documentation of this file.
1 #include "resampler_speaker.h"
2 
3 #ifdef USE_ESP32
4 
6 
7 #include "esphome/core/helpers.h"
8 #include "esphome/core/log.h"
9 
10 #include <algorithm>
11 #include <cstring>
12 
13 namespace esphome {
14 namespace resampler {
15 
16 static const UBaseType_t RESAMPLER_TASK_PRIORITY = 1;
17 
18 static const uint32_t TRANSFER_BUFFER_DURATION_MS = 50;
19 
20 static const uint32_t TASK_DELAY_MS = 20;
21 static const uint32_t TASK_STACK_SIZE = 3072;
22 
23 static const char *const TAG = "resampler_speaker";
24 
25 enum ResamplingEventGroupBits : uint32_t {
26  COMMAND_STOP = (1 << 0), // stops the resampler task
27  STATE_STARTING = (1 << 10),
28  STATE_RUNNING = (1 << 11),
29  STATE_STOPPING = (1 << 12),
30  STATE_STOPPED = (1 << 13),
31  ERR_ESP_NO_MEM = (1 << 19),
32  ERR_ESP_NOT_SUPPORTED = (1 << 20),
33  ERR_ESP_FAIL = (1 << 21),
34  ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
35 };
36 
38  this->event_group_ = xEventGroupCreate();
39 
40  if (this->event_group_ == nullptr) {
41  ESP_LOGE(TAG, "Failed to create event group");
42  this->mark_failed();
43  return;
44  }
45 
47  [this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) {
48  int32_t adjustment = this->playback_differential_ms_;
49  this->playback_differential_ms_ -= adjustment;
50  int32_t adjusted_playback_ms = static_cast<int32_t>(new_playback_ms) + adjustment;
51  this->audio_output_callback_(adjusted_playback_ms, remainder_us, pending_ms, write_timestamp);
52  });
53 }
54 
56  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
57 
58  if (event_group_bits & ResamplingEventGroupBits::STATE_STARTING) {
59  ESP_LOGD(TAG, "Starting resampler task");
60  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_STARTING);
61  }
62 
63  if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NO_MEM) {
64  this->status_set_error("Resampler task failed to allocate the internal buffers");
65  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NO_MEM);
67  }
68  if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED) {
69  this->status_set_error("Cannot resample due to an unsupported audio stream");
70  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED);
72  }
73  if (event_group_bits & ResamplingEventGroupBits::ERR_ESP_FAIL) {
74  this->status_set_error("Resampler task failed");
75  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ERR_ESP_FAIL);
77  }
78 
79  if (event_group_bits & ResamplingEventGroupBits::STATE_RUNNING) {
80  ESP_LOGD(TAG, "Started resampler task");
81  this->status_clear_error();
82  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_RUNNING);
83  }
84  if (event_group_bits & ResamplingEventGroupBits::STATE_STOPPING) {
85  ESP_LOGD(TAG, "Stopping resampler task");
86  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::STATE_STOPPING);
87  }
88  if (event_group_bits & ResamplingEventGroupBits::STATE_STOPPED) {
89  if (this->delete_task_() == ESP_OK) {
90  ESP_LOGD(TAG, "Stopped resampler task");
91  xEventGroupClearBits(this->event_group_, ResamplingEventGroupBits::ALL_BITS);
92  }
93  }
94 
95  switch (this->state_) {
97  esp_err_t err = this->start_();
98  if (err == ESP_OK) {
99  this->status_clear_error();
101  } else {
102  switch (err) {
103  case ESP_ERR_INVALID_STATE:
104  this->status_set_error("Failed to start resampler: resampler task failed to start");
105  break;
106  case ESP_ERR_NO_MEM:
107  this->status_set_error("Failed to start resampler: not enough memory for task stack");
108  default:
109  this->status_set_error("Failed to start resampler");
110  break;
111  }
112 
114  }
115  break;
116  }
118  if (this->output_speaker_->is_stopped()) {
120  }
121 
122  break;
124  this->stop_();
126  break;
128  break;
129  }
130 }
131 
132 size_t ResamplerSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
133  if (this->is_stopped()) {
134  this->start();
135  }
136 
137  size_t bytes_written = 0;
138  if ((this->output_speaker_->is_running()) && (!this->requires_resampling_())) {
139  bytes_written = this->output_speaker_->play(data, length, ticks_to_wait);
140  } else {
141  if (this->ring_buffer_.use_count() == 1) {
142  std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
143  bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait);
144  }
145  }
146 
147  return bytes_written;
148 }
149 
151 
155 
157  this->output_speaker_->start();
158 
159  if (this->requires_resampling_()) {
160  // Start the resampler task to handle converting sample rates
161  return this->start_task_();
162  }
163 
164  return ESP_OK;
165 }
166 
168  if (this->task_stack_buffer_ == nullptr) {
169  if (this->task_stack_in_psram_) {
171  this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
172  } else {
174  this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
175  }
176  }
177 
178  if (this->task_stack_buffer_ == nullptr) {
179  return ESP_ERR_NO_MEM;
180  }
181 
182  if (this->task_handle_ == nullptr) {
183  this->task_handle_ = xTaskCreateStatic(resample_task, "sample", TASK_STACK_SIZE, (void *) this,
184  RESAMPLER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_);
185  }
186 
187  if (this->task_handle_ == nullptr) {
188  return ESP_ERR_INVALID_STATE;
189  }
190 
191  return ESP_OK;
192 }
193 
195 
197  if (this->task_handle_ != nullptr) {
198  xEventGroupSetBits(this->event_group_, ResamplingEventGroupBits::COMMAND_STOP);
199  }
200  this->output_speaker_->stop();
201 }
202 
204  if (!this->task_created_) {
205  this->task_handle_ = nullptr;
206 
207  if (this->task_stack_buffer_ != nullptr) {
208  if (this->task_stack_in_psram_) {
210  stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
211  } else {
213  stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
214  }
215 
216  this->task_stack_buffer_ = nullptr;
217  }
218 
219  return ESP_OK;
220  }
221 
222  return ESP_ERR_INVALID_STATE;
223 }
224 
226 
228  bool has_ring_buffer_data = false;
229  if (this->requires_resampling_() && (this->ring_buffer_.use_count() > 0)) {
230  has_ring_buffer_data = (this->ring_buffer_.lock()->available() > 0);
231  }
232  return (has_ring_buffer_data || this->output_speaker_->has_buffered_data());
233 }
234 
235 void ResamplerSpeaker::set_mute_state(bool mute_state) {
236  this->mute_state_ = mute_state;
237  this->output_speaker_->set_mute_state(mute_state);
238 }
239 
240 void ResamplerSpeaker::set_volume(float volume) {
241  this->volume_ = volume;
242  this->output_speaker_->set_volume(volume);
243 }
244 
246  return (this->audio_stream_info_.get_sample_rate() != this->target_sample_rate_) ||
248 }
249 
251  ResamplerSpeaker *this_resampler = (ResamplerSpeaker *) params;
252 
253  this_resampler->task_created_ = true;
254  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STARTING);
255 
256  std::unique_ptr<audio::AudioResampler> resampler =
257  make_unique<audio::AudioResampler>(this_resampler->audio_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS),
258  this_resampler->target_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS));
259 
260  esp_err_t err = resampler->start(this_resampler->audio_stream_info_, this_resampler->target_stream_info_,
261  this_resampler->taps_, this_resampler->filters_);
262 
263  if (err == ESP_OK) {
264  std::shared_ptr<RingBuffer> temp_ring_buffer =
265  RingBuffer::create(this_resampler->audio_stream_info_.ms_to_bytes(this_resampler->buffer_duration_ms_));
266 
267  if (temp_ring_buffer.use_count() == 0) {
268  err = ESP_ERR_NO_MEM;
269  } else {
270  this_resampler->ring_buffer_ = temp_ring_buffer;
271  resampler->add_source(this_resampler->ring_buffer_);
272 
273  this_resampler->output_speaker_->set_audio_stream_info(this_resampler->target_stream_info_);
274  resampler->add_sink(this_resampler->output_speaker_);
275  }
276  }
277 
278  if (err == ESP_OK) {
279  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_RUNNING);
280  } else if (err == ESP_ERR_NO_MEM) {
281  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_NO_MEM);
282  } else if (err == ESP_ERR_NOT_SUPPORTED) {
283  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_NOT_SUPPORTED);
284  }
285 
286  this_resampler->playback_differential_ms_ = 0;
287  while (err == ESP_OK) {
288  uint32_t event_bits = xEventGroupGetBits(this_resampler->event_group_);
289 
290  if (event_bits & ResamplingEventGroupBits::COMMAND_STOP) {
291  break;
292  }
293 
294  // Stop gracefully if the decoder is done
295  int32_t ms_differential = 0;
296  audio::AudioResamplerState resampler_state = resampler->resample(false, &ms_differential);
297 
298  this_resampler->playback_differential_ms_ += ms_differential;
299 
300  if (resampler_state == audio::AudioResamplerState::FINISHED) {
301  break;
302  } else if (resampler_state == audio::AudioResamplerState::FAILED) {
303  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::ERR_ESP_FAIL);
304  break;
305  }
306  }
307 
308  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STOPPING);
309  resampler.reset();
310  xEventGroupSetBits(this_resampler->event_group_, ResamplingEventGroupBits::STATE_STOPPED);
311  this_resampler->task_created_ = false;
312  vTaskDelete(nullptr);
313 }
314 
315 } // namespace resampler
316 } // namespace esphome
317 
318 #endif
bool is_running() const
Definition: speaker.h:66
virtual void set_volume(float volume)
Definition: speaker.h:71
uint8_t get_channels() const
Definition: audio.h:29
void add_audio_output_callback(std::function< void(uint32_t, uint32_t, uint32_t, uint32_t)> &&callback)
Callback function for sending the duration of the audio written to the speaker since the last callbac...
Definition: speaker.h:112
uint8_t get_bits_per_sample() const
Definition: audio.h:28
T * allocate(size_t n)
Definition: helpers.h:703
void set_volume(float volume) override
Volume state changes are passed to the parent&#39;s output speaker.
virtual void finish()
Definition: speaker.h:58
virtual bool has_buffered_data() const =0
esp_err_t start_()
Starts the output speaker after setting the resampled stream info.
CallbackManager< void(uint32_t, uint32_t, uint32_t, uint32_t)> audio_output_callback_
Definition: speaker.h:126
bool is_stopped() const
Definition: speaker.h:67
void stop_()
Stops the output speaker. If the resampling task is running, it sends the stop command.
void set_audio_stream_info(const audio::AudioStreamInfo &audio_stream_info)
Definition: speaker.h:99
void status_set_error(const char *message="unspecified")
Definition: component.cpp:159
static void resample_task(void *params)
uint32_t get_sample_rate() const
Definition: audio.h:30
size_t ms_to_bytes(uint32_t ms) const
Converts duration to bytes.
Definition: audio.h:73
esp_err_t delete_task_()
Deallocates the task stack and resets the pointers.
void deallocate(T *p, size_t n)
Definition: helpers.h:720
void set_mute_state(bool mute_state) override
Mute state changes are passed to the parent&#39;s output speaker.
void status_clear_error()
Definition: component.cpp:172
virtual void start()=0
virtual void mark_failed()
Mark this component as failed.
Definition: component.cpp:118
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
audio::AudioStreamInfo target_stream_info_
esp_err_t start_task_()
Starts the resampler task after allocating the task stack.
std::weak_ptr< RingBuffer > ring_buffer_
virtual size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait)
Plays the provided audio data.
Definition: speaker.h:38
An STL allocator that uses SPI or internal RAM.
Definition: helpers.h:683
static std::unique_ptr< RingBuffer > create(size_t len)
Definition: ring_buffer.cpp:22
virtual void stop()=0
audio::AudioStreamInfo audio_stream_info_
Definition: speaker.h:118
virtual void set_mute_state(bool mute_state)
Definition: speaker.h:81
size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override