ESPHome  2025.2.0
mixer_speaker.h
Go to the documentation of this file.
1 #pragma once
2 
3 #ifdef USE_ESP32
4 
8 
10 
11 #include <freertos/event_groups.h>
12 #include <freertos/FreeRTOS.h>
13 
14 namespace esphome {
15 namespace mixer_speaker {
16 
17 /* Classes for mixing several source speaker audio streams and writing it to another speaker component.
18  * - Volume controls are passed through to the output speaker
19  * - Directly handles pausing at the SourceSpeaker level; pause state is not passed through to the output speaker.
20  * - Audio sent to the SourceSpeaker's must have 16 bits per sample.
21  * - Audio sent to the SourceSpeaker can have any number of channels. They are duplicated or ignored as needed to match
22  * the number of channels required for the output speaker.
23  * - In queue mode, the audio sent to the SoureSpeakers can have different sample rates.
24  * - In non-queue mode, the audio sent to the SourceSpeakers must have the same sample rates.
25  * - SourceSpeaker has an internal ring buffer. It also allocates a shared_ptr for an AudioTranserBuffer object.
26  * - Audio Data Flow:
27  * - Audio data played on a SourceSpeaker first writes to its internal ring buffer.
28  * - MixerSpeaker task temporarily takes shared ownership of each SourceSpeaker's AudioTransferBuffer.
29  * - MixerSpeaker calls SourceSpeaker's `process_data_from_source`, which tranfers audio from the SourceSpeaker's
30  * ring buffer to its AudioTransferBuffer. Audio ducking is applied at this step.
31  * - In queue mode, MixerSpeaker prioritizes the earliest configured SourceSpeaker with audio data. Audio data is
32  * sent to the output speaker.
33  * - In non-queue mode, MixerSpeaker adds all the audio data in each SourceSpeaker into one stream that is written
34  * to the output speaker.
35  */
36 
37 class MixerSpeaker;
38 
39 class SourceSpeaker : public speaker::Speaker, public Component {
40  public:
41  void dump_config() override;
42  void setup() override;
43  void loop() override;
44 
45  size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override;
46  size_t play(const uint8_t *data, size_t length) override { return this->play(data, length, 0); }
47 
48  void start() override;
49  void stop() override;
50  void finish() override;
51 
52  bool has_buffered_data() const override;
53 
55  void set_mute_state(bool mute_state) override;
56 
58  void set_volume(float volume) override;
59 
60  void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; }
61  bool get_pause_state() const override { return this->pause_state_; }
62 
66  size_t process_data_from_source(TickType_t ticks_to_wait);
67 
71  void apply_ducking(uint8_t decibel_reduction, uint32_t duration);
72 
73  void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
74  void set_parent(MixerSpeaker *parent) { this->parent_ = parent; }
75  void set_timeout(uint32_t ms) { this->timeout_ms_ = ms; }
76 
77  std::weak_ptr<audio::AudioSourceTransferBuffer> get_transfer_buffer() { return this->transfer_buffer_; }
78 
79  protected:
80  friend class MixerSpeaker;
81  esp_err_t start_();
82  void stop_();
83 
93  static void duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck, int8_t *current_ducking_db_reduction,
94  uint32_t *ducking_transition_samples_remaining, uint32_t samples_per_ducking_step,
95  int8_t db_change_per_ducking_step);
96 
98 
99  std::shared_ptr<audio::AudioSourceTransferBuffer> transfer_buffer_;
100  std::weak_ptr<RingBuffer> ring_buffer_;
101 
103  uint32_t last_seen_data_ms_{0};
105  bool stop_gracefully_{false};
106 
107  bool pause_state_{false};
108 
114 
116 
117  uint32_t pending_playback_ms_{0};
118 };
119 
120 class MixerSpeaker : public Component {
121  public:
122  void dump_config() override;
123  void setup() override;
124  void loop() override;
125 
126  void add_source_speaker(SourceSpeaker *source_speaker) { this->source_speakers_.push_back(source_speaker); }
127 
135  esp_err_t start(audio::AudioStreamInfo &stream_info);
136 
137  void stop();
138 
139  void set_output_channels(uint8_t output_channels) { this->output_channels_ = output_channels; }
140  void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; }
141  void set_queue_mode(bool queue_mode) { this->queue_mode_ = queue_mode; }
142  void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; }
143 
144  speaker::Speaker *get_output_speaker() const { return this->output_speaker_; }
145 
146  protected:
155  static void copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info, int16_t *output_buffer,
156  audio::AudioStreamInfo output_stream_info, uint32_t frames_to_transfer);
157 
169  static void mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info,
170  const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info,
171  int16_t *output_buffer, audio::AudioStreamInfo output_stream_info,
172  uint32_t frames_to_mix);
173 
174  static void audio_mixer_task(void *params);
175 
180  esp_err_t start_task_();
181 
184  esp_err_t delete_task_();
185 
186  EventGroupHandle_t event_group_{nullptr};
187 
188  std::vector<SourceSpeaker *> source_speakers_;
189  speaker::Speaker *output_speaker_{nullptr};
190 
193  bool task_stack_in_psram_{false};
194 
195  bool task_created_{false};
196 
197  TaskHandle_t task_handle_{nullptr};
198  StaticTask_t task_stack_;
199  StackType_t *task_stack_buffer_{nullptr};
200 
202 };
203 
204 } // namespace mixer_speaker
205 } // namespace esphome
206 
207 #endif
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_
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.
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.
std::weak_ptr< audio::AudioSourceTransferBuffer > get_transfer_buffer()
Definition: mixer_speaker.h:77
void set_queue_mode(bool queue_mode)
void set_parent(MixerSpeaker *parent)
Definition: mixer_speaker.h:74
size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override
void set_buffer_duration(uint32_t buffer_duration_ms)
Definition: mixer_speaker.h:73
void set_pause_state(bool pause_state) override
Definition: mixer_speaker.h:60
void set_output_channels(uint8_t output_channels)
void add_source_speaker(SourceSpeaker *source_speaker)
void set_mute_state(bool mute_state) override
Mute state changes are passed to the parent&#39;s output speaker.
void set_output_speaker(speaker::Speaker *speaker)
size_t play(const uint8_t *data, size_t length) override
Definition: mixer_speaker.h:46
bool get_pause_state() const override
Definition: mixer_speaker.h:61
uint16_t length
Definition: tt21100.cpp:12
Implementation of SPI Controller mode.
Definition: a01nyub.cpp:7
std::weak_ptr< RingBuffer > ring_buffer_
void set_task_stack_in_psram(bool task_stack_in_psram)
void apply_ducking(uint8_t decibel_reduction, uint32_t duration)
Sets the ducking level for the source speaker.
optional< audio::AudioStreamInfo > audio_stream_info_