ESPHome  2025.2.0
mixer_speaker.cpp
Go to the documentation of this file.
1 #include "mixer_speaker.h"
2 
3 #ifdef USE_ESP32
4 
5 #include "esphome/core/hal.h"
6 #include "esphome/core/helpers.h"
7 #include "esphome/core/log.h"
8 
9 #include <algorithm>
10 #include <cstring>
11 
12 namespace esphome {
13 namespace mixer_speaker {
14 
15 static const UBaseType_t MIXER_TASK_PRIORITY = 10;
16 
17 static const uint32_t TRANSFER_BUFFER_DURATION_MS = 50;
18 static const uint32_t TASK_DELAY_MS = 25;
19 
20 static const size_t TASK_STACK_SIZE = 4096;
21 
22 static const int16_t MAX_AUDIO_SAMPLE_VALUE = INT16_MAX;
23 static const int16_t MIN_AUDIO_SAMPLE_VALUE = INT16_MIN;
24 
25 static const char *const TAG = "speaker_mixer";
26 
27 // Gives the Q15 fixed point scaling factor to reduce by 0 dB, 1dB, ..., 50 dB
28 // dB to PCM scaling factor formula: floating_point_scale_factor = 2^(-db/6.014)
29 // float to Q15 fixed point formula: q15_scale_factor = floating_point_scale_factor * 2^(15)
30 static const std::vector<int16_t> DECIBEL_REDUCTION_TABLE = {
31  32767, 29201, 26022, 23189, 20665, 18415, 16410, 14624, 13032, 11613, 10349, 9222, 8218, 7324, 6527, 5816, 5183,
32  4619, 4116, 3668, 3269, 2913, 2596, 2313, 2061, 1837, 1637, 1459, 1300, 1158, 1032, 920, 820, 731,
33  651, 580, 517, 461, 411, 366, 326, 291, 259, 231, 206, 183, 163, 146, 130, 116, 103};
34 
35 enum MixerEventGroupBits : uint32_t {
36  COMMAND_STOP = (1 << 0), // stops the mixer task
37  STATE_STARTING = (1 << 10),
38  STATE_RUNNING = (1 << 11),
39  STATE_STOPPING = (1 << 12),
40  STATE_STOPPED = (1 << 13),
41  ERR_ESP_NO_MEM = (1 << 19),
42  ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
43 };
44 
46  ESP_LOGCONFIG(TAG, "Mixer Source Speaker");
47  ESP_LOGCONFIG(TAG, " Buffer Duration: %" PRIu32 " ms", this->buffer_duration_ms_);
48  if (this->timeout_ms_.has_value()) {
49  ESP_LOGCONFIG(TAG, " Timeout: %" PRIu32 " ms", this->timeout_ms_.value());
50  } else {
51  ESP_LOGCONFIG(TAG, " Timeout: never");
52  }
53 }
54 
57  [this](uint32_t new_playback_ms, uint32_t remainder_us, uint32_t pending_ms, uint32_t write_timestamp) {
58  uint32_t personal_playback_ms = std::min(new_playback_ms, this->pending_playback_ms_);
59  if (personal_playback_ms > 0) {
60  this->pending_playback_ms_ -= personal_playback_ms;
61  this->audio_output_callback_(personal_playback_ms, remainder_us, this->pending_playback_ms_, write_timestamp);
62  }
63  });
64 }
65 
67  switch (this->state_) {
69  esp_err_t err = this->start_();
70  if (err == ESP_OK) {
72  this->stop_gracefully_ = false;
73  this->last_seen_data_ms_ = millis();
74  this->status_clear_error();
75  } else {
76  switch (err) {
77  case ESP_ERR_NO_MEM:
78  this->status_set_error("Failed to start mixer: not enough memory");
79  break;
80  case ESP_ERR_NOT_SUPPORTED:
81  this->status_set_error("Failed to start mixer: unsupported bits per sample");
82  break;
83  case ESP_ERR_INVALID_ARG:
84  this->status_set_error("Failed to start mixer: audio stream isn't compatible with the other audio stream.");
85  break;
86  case ESP_ERR_INVALID_STATE:
87  this->status_set_error("Failed to start mixer: mixer task failed to start");
88  break;
89  default:
90  this->status_set_error("Failed to start mixer");
91  break;
92  }
93 
95  }
96  break;
97  }
99  if (!this->transfer_buffer_->has_buffered_data()) {
100  if ((this->timeout_ms_.has_value() && ((millis() - this->last_seen_data_ms_) > this->timeout_ms_.value())) ||
101  this->stop_gracefully_) {
103  }
104  }
105  break;
107  this->stop_();
108  this->stop_gracefully_ = false;
110  break;
112  break;
113  }
114 }
115 
116 size_t SourceSpeaker::play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) {
117  if (this->is_stopped()) {
118  this->start();
119  }
120  size_t bytes_written = 0;
121  if (this->ring_buffer_.use_count() == 1) {
122  std::shared_ptr<RingBuffer> temp_ring_buffer = this->ring_buffer_.lock();
123  bytes_written = temp_ring_buffer->write_without_replacement(data, length, ticks_to_wait);
124  if (bytes_written > 0) {
125  this->last_seen_data_ms_ = millis();
126  }
127  }
128  return bytes_written;
129 }
130 
132 
134  const size_t ring_buffer_size = this->audio_stream_info_.ms_to_bytes(this->buffer_duration_ms_);
135  if (this->transfer_buffer_.use_count() == 0) {
136  this->transfer_buffer_ =
137  audio::AudioSourceTransferBuffer::create(this->audio_stream_info_.ms_to_bytes(TRANSFER_BUFFER_DURATION_MS));
138 
139  if (this->transfer_buffer_ == nullptr) {
140  return ESP_ERR_NO_MEM;
141  }
142  std::shared_ptr<RingBuffer> temp_ring_buffer;
143 
144  if (!this->ring_buffer_.use_count()) {
145  temp_ring_buffer = RingBuffer::create(ring_buffer_size);
146  this->ring_buffer_ = temp_ring_buffer;
147  }
148 
149  if (!this->ring_buffer_.use_count()) {
150  return ESP_ERR_NO_MEM;
151  } else {
152  this->transfer_buffer_->set_source(temp_ring_buffer);
153  }
154  }
155 
156  return this->parent_->start(this->audio_stream_info_);
157 }
158 
160  if (this->state_ != speaker::STATE_STOPPED) {
162  }
163 }
164 
166  this->transfer_buffer_.reset(); // deallocates the transfer buffer
167 }
168 
169 void SourceSpeaker::finish() { this->stop_gracefully_ = true; }
170 
172  return ((this->transfer_buffer_.use_count() > 0) && this->transfer_buffer_->has_buffered_data());
173 }
174 
175 void SourceSpeaker::set_mute_state(bool mute_state) {
176  this->mute_state_ = mute_state;
177  this->parent_->get_output_speaker()->set_mute_state(mute_state);
178 }
179 
180 void SourceSpeaker::set_volume(float volume) {
181  this->volume_ = volume;
182  this->parent_->get_output_speaker()->set_volume(volume);
183 }
184 
185 size_t SourceSpeaker::process_data_from_source(TickType_t ticks_to_wait) {
186  if (!this->transfer_buffer_.use_count()) {
187  return 0;
188  }
189 
190  // Store current offset, as these samples are already ducked
191  const size_t current_length = this->transfer_buffer_->available();
192 
193  size_t bytes_read = this->transfer_buffer_->transfer_data_from_source(ticks_to_wait);
194 
195  uint32_t samples_to_duck = this->audio_stream_info_.bytes_to_samples(bytes_read);
196  if (samples_to_duck > 0) {
197  int16_t *current_buffer = reinterpret_cast<int16_t *>(this->transfer_buffer_->get_buffer_start() + current_length);
198 
199  duck_samples(current_buffer, samples_to_duck, &this->current_ducking_db_reduction_,
202  }
203 
204  return bytes_read;
205 }
206 
207 void SourceSpeaker::apply_ducking(uint8_t decibel_reduction, uint32_t duration) {
208  if (this->target_ducking_db_reduction_ != decibel_reduction) {
210 
211  this->target_ducking_db_reduction_ = decibel_reduction;
212 
213  uint8_t total_ducking_steps = 0;
215  // The dB reduction level is increasing (which results in quieter audio)
216  total_ducking_steps = this->target_ducking_db_reduction_ - this->current_ducking_db_reduction_ - 1;
217  this->db_change_per_ducking_step_ = 1;
218  } else {
219  // The dB reduction level is decreasing (which results in louder audio)
220  total_ducking_steps = this->current_ducking_db_reduction_ - this->target_ducking_db_reduction_ - 1;
221  this->db_change_per_ducking_step_ = -1;
222  }
223  if ((duration > 0) && (total_ducking_steps > 0)) {
225 
226  this->samples_per_ducking_step_ = this->ducking_transition_samples_remaining_ / total_ducking_steps;
228  this->samples_per_ducking_step_ * total_ducking_steps; // Adjust for integer division rounding
229 
231  } else {
234  }
235  }
236 }
237 
238 void SourceSpeaker::duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck,
239  int8_t *current_ducking_db_reduction, uint32_t *ducking_transition_samples_remaining,
240  uint32_t samples_per_ducking_step, int8_t db_change_per_ducking_step) {
241  if (*ducking_transition_samples_remaining > 0) {
242  // Ducking level is still transitioning
243 
244  // Takes the ceiling of input_samples_to_duck/samples_per_ducking_step
245  uint32_t ducking_steps_in_batch =
246  input_samples_to_duck / samples_per_ducking_step + (input_samples_to_duck % samples_per_ducking_step != 0);
247 
248  for (uint32_t i = 0; i < ducking_steps_in_batch; ++i) {
249  uint32_t samples_left_in_step = *ducking_transition_samples_remaining % samples_per_ducking_step;
250 
251  if (samples_left_in_step == 0) {
252  samples_left_in_step = samples_per_ducking_step;
253  }
254 
255  uint32_t samples_to_duck = std::min(input_samples_to_duck, samples_left_in_step);
256  samples_to_duck = std::min(samples_to_duck, *ducking_transition_samples_remaining);
257 
258  // Ensure we only point to valid index in the Q15 scaling factor table
259  uint8_t safe_db_reduction_index =
260  clamp<uint8_t>(*current_ducking_db_reduction, 0, DECIBEL_REDUCTION_TABLE.size() - 1);
261  int16_t q15_scale_factor = DECIBEL_REDUCTION_TABLE[safe_db_reduction_index];
262 
263  audio::scale_audio_samples(input_buffer, input_buffer, q15_scale_factor, samples_to_duck);
264 
265  if (samples_left_in_step - samples_to_duck == 0) {
266  // After scaling the current samples, we are ready to transition to the next step
267  *current_ducking_db_reduction += db_change_per_ducking_step;
268  }
269 
270  input_buffer += samples_to_duck;
271  *ducking_transition_samples_remaining -= samples_to_duck;
272  input_samples_to_duck -= samples_to_duck;
273  }
274  }
275 
276  if ((*current_ducking_db_reduction > 0) && (input_samples_to_duck > 0)) {
277  // Audio is ducked, but its not in the middle of a transition step
278 
279  uint8_t safe_db_reduction_index =
280  clamp<uint8_t>(*current_ducking_db_reduction, 0, DECIBEL_REDUCTION_TABLE.size() - 1);
281  int16_t q15_scale_factor = DECIBEL_REDUCTION_TABLE[safe_db_reduction_index];
282 
283  audio::scale_audio_samples(input_buffer, input_buffer, q15_scale_factor, input_samples_to_duck);
284  }
285 }
286 
288  ESP_LOGCONFIG(TAG, "Speaker Mixer:");
289  ESP_LOGCONFIG(TAG, " Number of output channels: %u", this->output_channels_);
290 }
291 
293  this->event_group_ = xEventGroupCreate();
294 
295  if (this->event_group_ == nullptr) {
296  ESP_LOGE(TAG, "Failed to create event group");
297  this->mark_failed();
298  return;
299  }
300 }
301 
303  uint32_t event_group_bits = xEventGroupGetBits(this->event_group_);
304 
305  if (event_group_bits & MixerEventGroupBits::STATE_STARTING) {
306  ESP_LOGD(TAG, "Starting speaker mixer");
307  xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_STARTING);
308  }
309  if (event_group_bits & MixerEventGroupBits::ERR_ESP_NO_MEM) {
310  this->status_set_error("Failed to allocate the mixer's internal buffer");
311  xEventGroupClearBits(this->event_group_, MixerEventGroupBits::ERR_ESP_NO_MEM);
312  }
313  if (event_group_bits & MixerEventGroupBits::STATE_RUNNING) {
314  ESP_LOGD(TAG, "Started speaker mixer");
315  this->status_clear_error();
316  xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_RUNNING);
317  }
318  if (event_group_bits & MixerEventGroupBits::STATE_STOPPING) {
319  ESP_LOGD(TAG, "Stopping speaker mixer");
320  xEventGroupClearBits(this->event_group_, MixerEventGroupBits::STATE_STOPPING);
321  }
322  if (event_group_bits & MixerEventGroupBits::STATE_STOPPED) {
323  if (this->delete_task_() == ESP_OK) {
324  xEventGroupClearBits(this->event_group_, MixerEventGroupBits::ALL_BITS);
325  }
326  }
327 
328  if (this->task_handle_ != nullptr) {
329  bool all_stopped = true;
330 
331  for (auto &speaker : this->source_speakers_) {
332  all_stopped &= speaker->is_stopped();
333  }
334 
335  if (all_stopped) {
336  this->stop();
337  }
338  }
339 }
340 
342  if (!this->audio_stream_info_.has_value()) {
343  if (stream_info.get_bits_per_sample() != 16) {
344  // Audio streams that don't have 16 bits per sample are not supported
345  return ESP_ERR_NOT_SUPPORTED;
346  }
347 
348  this->audio_stream_info_ = audio::AudioStreamInfo(stream_info.get_bits_per_sample(), this->output_channels_,
349  stream_info.get_sample_rate());
350  this->output_speaker_->set_audio_stream_info(this->audio_stream_info_.value());
351  } else {
352  if (!this->queue_mode_ && (stream_info.get_sample_rate() != this->audio_stream_info_.value().get_sample_rate())) {
353  // The two audio streams must have the same sample rate to mix properly if not in queue mode
354  return ESP_ERR_INVALID_ARG;
355  }
356  }
357 
358  return this->start_task_();
359 }
360 
362  if (this->task_stack_buffer_ == nullptr) {
363  if (this->task_stack_in_psram_) {
365  this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
366  } else {
368  this->task_stack_buffer_ = stack_allocator.allocate(TASK_STACK_SIZE);
369  }
370  }
371 
372  if (this->task_stack_buffer_ == nullptr) {
373  return ESP_ERR_NO_MEM;
374  }
375 
376  if (this->task_handle_ == nullptr) {
377  this->task_handle_ = xTaskCreateStatic(audio_mixer_task, "mixer", TASK_STACK_SIZE, (void *) this,
378  MIXER_TASK_PRIORITY, this->task_stack_buffer_, &this->task_stack_);
379  }
380 
381  if (this->task_handle_ == nullptr) {
382  return ESP_ERR_INVALID_STATE;
383  }
384 
385  return ESP_OK;
386 }
387 
389  if (!this->task_created_) {
390  this->task_handle_ = nullptr;
391 
392  if (this->task_stack_buffer_ != nullptr) {
393  if (this->task_stack_in_psram_) {
395  stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
396  } else {
398  stack_allocator.deallocate(this->task_stack_buffer_, TASK_STACK_SIZE);
399  }
400 
401  this->task_stack_buffer_ = nullptr;
402  }
403 
404  return ESP_OK;
405  }
406 
407  return ESP_ERR_INVALID_STATE;
408 }
409 
410 void MixerSpeaker::stop() { xEventGroupSetBits(this->event_group_, MixerEventGroupBits::COMMAND_STOP); }
411 
412 void MixerSpeaker::copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info,
413  int16_t *output_buffer, audio::AudioStreamInfo output_stream_info,
414  uint32_t frames_to_transfer) {
415  uint8_t input_channels = input_stream_info.get_channels();
416  uint8_t output_channels = output_stream_info.get_channels();
417  const uint8_t max_input_channel_index = input_channels - 1;
418 
419  if (input_channels == output_channels) {
420  size_t bytes_to_copy = input_stream_info.frames_to_bytes(frames_to_transfer);
421  memcpy(output_buffer, input_buffer, bytes_to_copy);
422 
423  return;
424  }
425 
426  for (uint32_t frame_index = 0; frame_index < frames_to_transfer; ++frame_index) {
427  for (uint8_t output_channel_index = 0; output_channel_index < output_channels; ++output_channel_index) {
428  uint8_t input_channel_index = std::min(output_channel_index, max_input_channel_index);
429  output_buffer[output_channels * frame_index + output_channel_index] =
430  input_buffer[input_channels * frame_index + input_channel_index];
431  }
432  }
433 }
434 
435 void MixerSpeaker::mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info,
436  const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info,
437  int16_t *output_buffer, audio::AudioStreamInfo output_stream_info,
438  uint32_t frames_to_mix) {
439  const uint8_t primary_channels = primary_stream_info.get_channels();
440  const uint8_t secondary_channels = secondary_stream_info.get_channels();
441  const uint8_t output_channels = output_stream_info.get_channels();
442 
443  const uint8_t max_primary_channel_index = primary_channels - 1;
444  const uint8_t max_secondary_channel_index = secondary_channels - 1;
445 
446  for (uint32_t frames_index = 0; frames_index < frames_to_mix; ++frames_index) {
447  for (uint8_t output_channel_index = 0; output_channel_index < output_channels; ++output_channel_index) {
448  const uint32_t secondary_channel_index = std::min(output_channel_index, max_secondary_channel_index);
449  const int32_t secondary_sample = secondary_buffer[frames_index * secondary_channels + secondary_channel_index];
450 
451  const uint32_t primary_channel_index = std::min(output_channel_index, max_primary_channel_index);
452  const int32_t primary_sample =
453  static_cast<int32_t>(primary_buffer[frames_index * primary_channels + primary_channel_index]);
454 
455  const int32_t added_sample = secondary_sample + primary_sample;
456 
457  output_buffer[frames_index * output_channels + output_channel_index] =
458  static_cast<int16_t>(clamp<int32_t>(added_sample, MIN_AUDIO_SAMPLE_VALUE, MAX_AUDIO_SAMPLE_VALUE));
459  }
460  }
461 }
462 
463 void MixerSpeaker::audio_mixer_task(void *params) {
464  MixerSpeaker *this_mixer = (MixerSpeaker *) params;
465 
466  xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STARTING);
467 
468  this_mixer->task_created_ = true;
469 
470  std::unique_ptr<audio::AudioSinkTransferBuffer> output_transfer_buffer = audio::AudioSinkTransferBuffer::create(
471  this_mixer->audio_stream_info_.value().ms_to_bytes(TRANSFER_BUFFER_DURATION_MS));
472 
473  if (output_transfer_buffer == nullptr) {
474  xEventGroupSetBits(this_mixer->event_group_,
476 
477  this_mixer->task_created_ = false;
478  vTaskDelete(nullptr);
479  }
480 
481  output_transfer_buffer->set_sink(this_mixer->output_speaker_);
482 
483  xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_RUNNING);
484 
485  bool sent_finished = false;
486 
487  while (true) {
488  uint32_t event_group_bits = xEventGroupGetBits(this_mixer->event_group_);
489  if (event_group_bits & MixerEventGroupBits::COMMAND_STOP) {
490  break;
491  }
492 
493  output_transfer_buffer->transfer_data_to_sink(pdMS_TO_TICKS(TASK_DELAY_MS));
494 
495  const uint32_t output_frames_free =
496  this_mixer->audio_stream_info_.value().bytes_to_frames(output_transfer_buffer->free());
497 
498  std::vector<SourceSpeaker *> speakers_with_data;
499  std::vector<std::shared_ptr<audio::AudioSourceTransferBuffer>> transfer_buffers_with_data;
500 
501  for (auto &speaker : this_mixer->source_speakers_) {
502  if (speaker->get_transfer_buffer().use_count() > 0) {
503  std::shared_ptr<audio::AudioSourceTransferBuffer> transfer_buffer = speaker->get_transfer_buffer().lock();
504  speaker->process_data_from_source(0); // Transfers and ducks audio from source ring buffers
505 
506  if ((transfer_buffer->available() > 0) && !speaker->get_pause_state()) {
507  // Store the locked transfer buffers in their own vector to avoid releasing ownership until after the loop
508  transfer_buffers_with_data.push_back(transfer_buffer);
509  speakers_with_data.push_back(speaker);
510  }
511  }
512  }
513 
514  if (transfer_buffers_with_data.empty()) {
515  // No audio available for transferring, block task temporarily
516  delay(TASK_DELAY_MS);
517  continue;
518  }
519 
520  uint32_t frames_to_mix = output_frames_free;
521 
522  if ((transfer_buffers_with_data.size() == 1) || this_mixer->queue_mode_) {
523  // Only one speaker has audio data, just copy samples over
524 
525  audio::AudioStreamInfo active_stream_info = speakers_with_data[0]->get_audio_stream_info();
526 
527  if (active_stream_info.get_sample_rate() ==
529  // Speaker's sample rate matches the output speaker's, copy directly
530 
531  const uint32_t frames_available_in_buffer =
532  active_stream_info.bytes_to_frames(transfer_buffers_with_data[0]->available());
533  frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer);
534  copy_frames(reinterpret_cast<int16_t *>(transfer_buffers_with_data[0]->get_buffer_start()), active_stream_info,
535  reinterpret_cast<int16_t *>(output_transfer_buffer->get_buffer_end()),
536  this_mixer->audio_stream_info_.value(), frames_to_mix);
537 
538  // Update source speaker buffer length
539  transfer_buffers_with_data[0]->decrease_buffer_length(active_stream_info.frames_to_bytes(frames_to_mix));
540  speakers_with_data[0]->accumulated_frames_read_ += frames_to_mix;
541 
542  // Add new audio duration to the source speaker pending playback
543  speakers_with_data[0]->pending_playback_ms_ +=
544  active_stream_info.frames_to_milliseconds_with_remainder(&speakers_with_data[0]->accumulated_frames_read_);
545 
546  // Update output transfer buffer length
547  output_transfer_buffer->increase_buffer_length(
548  this_mixer->audio_stream_info_.value().frames_to_bytes(frames_to_mix));
549  } else {
550  // Speaker's stream info doesn't match the output speaker's, so it's a new source speaker
551  if (!this_mixer->output_speaker_->is_stopped()) {
552  if (!sent_finished) {
553  this_mixer->output_speaker_->finish();
554  sent_finished = true; // Avoid repeatedly sending the finish command
555  }
556  } else {
557  // Speaker has finished writing the current audio, update the stream information and restart the speaker
558  this_mixer->audio_stream_info_ =
559  audio::AudioStreamInfo(active_stream_info.get_bits_per_sample(), this_mixer->output_channels_,
560  active_stream_info.get_sample_rate());
561  this_mixer->output_speaker_->set_audio_stream_info(this_mixer->audio_stream_info_.value());
562  this_mixer->output_speaker_->start();
563  sent_finished = false;
564  }
565  }
566  } else {
567  // Determine how many frames to mix
568  for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
569  const uint32_t frames_available_in_buffer =
570  speakers_with_data[i]->get_audio_stream_info().bytes_to_frames(transfer_buffers_with_data[i]->available());
571  frames_to_mix = std::min(frames_to_mix, frames_available_in_buffer);
572  }
573  int16_t *primary_buffer = reinterpret_cast<int16_t *>(transfer_buffers_with_data[0]->get_buffer_start());
574  audio::AudioStreamInfo primary_stream_info = speakers_with_data[0]->get_audio_stream_info();
575 
576  // Mix two streams together
577  for (int i = 1; i < transfer_buffers_with_data.size(); ++i) {
578  mix_audio_samples(primary_buffer, primary_stream_info,
579  reinterpret_cast<int16_t *>(transfer_buffers_with_data[i]->get_buffer_start()),
580  speakers_with_data[i]->get_audio_stream_info(),
581  reinterpret_cast<int16_t *>(output_transfer_buffer->get_buffer_end()),
582  this_mixer->audio_stream_info_.value(), frames_to_mix);
583 
584  speakers_with_data[i]->pending_playback_ms_ +=
585  speakers_with_data[i]->get_audio_stream_info().frames_to_milliseconds_with_remainder(
586  &speakers_with_data[i]->accumulated_frames_read_);
587 
588  if (i != transfer_buffers_with_data.size() - 1) {
589  // Need to mix more streams together, point primary buffer and stream info to the already mixed output
590  primary_buffer = reinterpret_cast<int16_t *>(output_transfer_buffer->get_buffer_end());
591  primary_stream_info = this_mixer->audio_stream_info_.value();
592  }
593  }
594 
595  // Update source transfer buffer lengths and add new audio durations to the source speaker pending playbacks
596  for (int i = 0; i < transfer_buffers_with_data.size(); ++i) {
597  transfer_buffers_with_data[i]->decrease_buffer_length(
598  speakers_with_data[i]->get_audio_stream_info().frames_to_bytes(frames_to_mix));
599  speakers_with_data[i]->accumulated_frames_read_ += frames_to_mix;
600 
601  speakers_with_data[i]->pending_playback_ms_ +=
602  speakers_with_data[i]->get_audio_stream_info().frames_to_milliseconds_with_remainder(
603  &speakers_with_data[i]->accumulated_frames_read_);
604  }
605 
606  // Update output transfer buffer length
607  output_transfer_buffer->increase_buffer_length(
608  this_mixer->audio_stream_info_.value().frames_to_bytes(frames_to_mix));
609  }
610  }
611 
612  xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STOPPING);
613 
614  output_transfer_buffer.reset();
615 
616  xEventGroupSetBits(this_mixer->event_group_, MixerEventGroupBits::STATE_STOPPED);
617  this_mixer->task_created_ = false;
618  vTaskDelete(nullptr);
619 }
620 
621 } // namespace mixer_speaker
622 } // namespace esphome
623 
624 #endif
value_type const & value() const
Definition: optional.h:89
esp_err_t start_task_()
Starts the mixer task after allocating memory for the task stack.
void set_volume(float volume) override
Volume state changes are passed to the parent&#39;s output speaker.
speaker::Speaker * get_output_speaker() const
std::vector< SourceSpeaker * > source_speakers_
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
static void duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck, int8_t *current_ducking_db_reduction, uint32_t *ducking_transition_samples_remaining, uint32_t samples_per_ducking_step, int8_t db_change_per_ducking_step)
Ducks audio samples by a specified amount.
static std::unique_ptr< AudioSinkTransferBuffer > create(size_t buffer_size)
Creates a new sink transfer buffer.
std::shared_ptr< audio::AudioSourceTransferBuffer > transfer_buffer_
Definition: mixer_speaker.h:99
size_t process_data_from_source(TickType_t ticks_to_wait)
Transfers audio from the ring buffer into the transfer buffer.
uint8_t get_bits_per_sample() const
Definition: audio.h:28
T * allocate(size_t n)
Definition: helpers.h:703
virtual void finish()
Definition: speaker.h:58
size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override
bool has_value() const
Definition: optional.h:87
uint32_t frames_to_milliseconds_with_remainder(uint32_t *frames) const
Computes the duration, in milliseconds, the given amount of frames represents.
Definition: audio.cpp:26
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
uint32_t IRAM_ATTR HOT millis()
Definition: core.cpp:25
uint32_t bytes_to_samples(size_t bytes) const
Convert bytes to samples.
Definition: audio.h:48
static void audio_mixer_task(void *params)
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
void set_mute_state(bool mute_state) override
Mute state changes are passed to the parent&#39;s output speaker.
static void copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info, int16_t *output_buffer, audio::AudioStreamInfo output_stream_info, uint32_t frames_to_transfer)
Copies audio frames from the input buffer to the output buffer taking into account the number of chan...
size_t frames_to_bytes(uint32_t frames) const
Converts frames to bytes.
Definition: audio.h:53
uint32_t ms_to_samples(uint32_t ms) const
Converts duration to samples.
Definition: audio.h:68
static std::unique_ptr< AudioSourceTransferBuffer > create(size_t buffer_size)
Creates a new source transfer buffer.
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
void deallocate(T *p, size_t n)
Definition: helpers.h:720
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
esp_err_t start(audio::AudioStreamInfo &stream_info)
Starts the mixer task.
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
static void mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info, const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info, int16_t *output_buffer, audio::AudioStreamInfo output_stream_info, uint32_t frames_to_mix)
Mixes the primary and secondary streams taking into account the number of channels in each stream...
esp_err_t delete_task_()
If the task is stopped, it sets the task handle to the nullptr and deallocates its stack...
uint32_t bytes_to_frames(size_t bytes) const
Convert bytes to frames.
Definition: audio.h:43
std::weak_ptr< RingBuffer > ring_buffer_
audio::AudioStreamInfo & get_audio_stream_info()
Definition: speaker.h:103
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
void scale_audio_samples(const int16_t *audio_samples, int16_t *output_buffer, int16_t scale_factor, size_t samples_to_scale)
Scales Q15 fixed point audio samples.
Definition: audio.cpp:57
void apply_ducking(uint8_t decibel_reduction, uint32_t duration)
Sets the ducking level for the source speaker.
audio::AudioStreamInfo audio_stream_info_
Definition: speaker.h:118
virtual void set_mute_state(bool mute_state)
Definition: speaker.h:81
void IRAM_ATTR HOT delay(uint32_t ms)
Definition: core.cpp:26
optional< audio::AudioStreamInfo > audio_stream_info_