ESPHome  2025.3.3
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  bool get_mute_state() override;
57 
59  void set_volume(float volume) override;
60  float get_volume() override;
61 
62  void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; }
63  bool get_pause_state() const override { return this->pause_state_; }
64 
68  size_t process_data_from_source(TickType_t ticks_to_wait);
69 
73  void apply_ducking(uint8_t decibel_reduction, uint32_t duration);
74 
75  void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
76  void set_parent(MixerSpeaker *parent) { this->parent_ = parent; }
77  void set_timeout(uint32_t ms) { this->timeout_ms_ = ms; }
78 
79  std::weak_ptr<audio::AudioSourceTransferBuffer> get_transfer_buffer() { return this->transfer_buffer_; }
80 
81  protected:
82  friend class MixerSpeaker;
83  esp_err_t start_();
84  void stop_();
85 
95  static void duck_samples(int16_t *input_buffer, uint32_t input_samples_to_duck, int8_t *current_ducking_db_reduction,
96  uint32_t *ducking_transition_samples_remaining, uint32_t samples_per_ducking_step,
97  int8_t db_change_per_ducking_step);
98 
100 
101  std::shared_ptr<audio::AudioSourceTransferBuffer> transfer_buffer_;
102  std::weak_ptr<RingBuffer> ring_buffer_;
103 
105  uint32_t last_seen_data_ms_{0};
107  bool stop_gracefully_{false};
108 
109  bool pause_state_{false};
110 
116 
118 
119  uint32_t pending_playback_ms_{0};
120 };
121 
122 class MixerSpeaker : public Component {
123  public:
124  void dump_config() override;
125  void setup() override;
126  void loop() override;
127 
128  void add_source_speaker(SourceSpeaker *source_speaker) { this->source_speakers_.push_back(source_speaker); }
129 
137  esp_err_t start(audio::AudioStreamInfo &stream_info);
138 
139  void stop();
140 
141  void set_output_channels(uint8_t output_channels) { this->output_channels_ = output_channels; }
142  void set_output_speaker(speaker::Speaker *speaker) { this->output_speaker_ = speaker; }
143  void set_queue_mode(bool queue_mode) { this->queue_mode_ = queue_mode; }
144  void set_task_stack_in_psram(bool task_stack_in_psram) { this->task_stack_in_psram_ = task_stack_in_psram; }
145 
146  speaker::Speaker *get_output_speaker() const { return this->output_speaker_; }
147 
148  protected:
157  static void copy_frames(const int16_t *input_buffer, audio::AudioStreamInfo input_stream_info, int16_t *output_buffer,
158  audio::AudioStreamInfo output_stream_info, uint32_t frames_to_transfer);
159 
171  static void mix_audio_samples(const int16_t *primary_buffer, audio::AudioStreamInfo primary_stream_info,
172  const int16_t *secondary_buffer, audio::AudioStreamInfo secondary_stream_info,
173  int16_t *output_buffer, audio::AudioStreamInfo output_stream_info,
174  uint32_t frames_to_mix);
175 
176  static void audio_mixer_task(void *params);
177 
182  esp_err_t start_task_();
183 
186  esp_err_t delete_task_();
187 
188  EventGroupHandle_t event_group_{nullptr};
189 
190  std::vector<SourceSpeaker *> source_speakers_;
191  speaker::Speaker *output_speaker_{nullptr};
192 
195  bool task_stack_in_psram_{false};
196 
197  bool task_created_{false};
198 
199  TaskHandle_t task_handle_{nullptr};
200  StaticTask_t task_stack_;
201  StackType_t *task_stack_buffer_{nullptr};
202 
204 };
205 
206 } // namespace mixer_speaker
207 } // namespace esphome
208 
209 #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_
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:79
void set_queue_mode(bool queue_mode)
void set_parent(MixerSpeaker *parent)
Definition: mixer_speaker.h:76
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:75
void set_pause_state(bool pause_state) override
Definition: mixer_speaker.h:62
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:63
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_
uint8_t duration
Definition: msa3xx.h:430