ESPHome 2026.11.0-dev
Loading...
Searching...
No Matches
i2s_audio_speaker.h
Go to the documentation of this file.
1#pragma once
2
3#ifdef USE_ESP32
4
5#include "../i2s_audio.h"
6
7#include <freertos/event_groups.h>
8#include <freertos/queue.h>
9#include <freertos/FreeRTOS.h>
10
14
16#include "esphome/core/gpio.h"
18
19#include <gain.h> // esp-audio-libs
20
21namespace esphome::i2s_audio {
22
23// Shared constants used by both standard and SPDIF speaker implementations
24static constexpr size_t TASK_STACK_SIZE = 4096;
25static constexpr ssize_t TASK_PRIORITY = 19;
26
28 COMMAND_START = (1 << 0), // indicates loop should start speaker task
29 COMMAND_STOP = (1 << 1), // stops the speaker task
30 COMMAND_STOP_GRACEFULLY = (1 << 2), // Stops the speaker task once all data has been written
31
32 TASK_STARTING = (1 << 10),
33 TASK_RUNNING = (1 << 11),
34 TASK_STOPPING = (1 << 12),
35 TASK_STOPPED = (1 << 13),
36
37 ERR_ESP_NO_MEM = (1 << 19),
38
39 ERR_DROPPED_EVENT = (1 << 20), // ISR overflowed the event queue, dropping a completion event
40
41 ALL_BITS = 0x00FFFFFF, // All valid FreeRTOS event group bits
42};
43
48 public:
49 float get_setup_priority() const override { return esphome::setup_priority::PROCESSOR; }
50
51 void setup() override;
52 void dump_config() override;
53 void loop() override;
54
55 void set_buffer_duration(uint32_t buffer_duration_ms) { this->buffer_duration_ms_ = buffer_duration_ms; }
56 void set_timeout(uint32_t ms) { this->timeout_ = ms; }
57 void set_keep_alive(bool keep_alive) { this->keep_alive_ = keep_alive; }
58 void set_dout_pin(uint8_t pin) { this->dout_pin_ = (gpio_num_t) pin; }
59
61 i2s_chan_handle_t get_tx_handle() const { return this->tx_handle_; }
62
63 void start() override;
64 void stop() override;
65 void finish() override;
66
67 void set_pause_state(bool pause_state) override { this->pause_state_ = pause_state; }
68 bool get_pause_state() const override { return this->pause_state_; }
69
76 size_t play(const uint8_t *data, size_t length, TickType_t ticks_to_wait) override;
77 size_t play(const uint8_t *data, size_t length) override { return play(data, length, 0); }
78
79 bool has_buffered_data() const override;
80
85 void set_volume(float volume) override;
86
90 void set_mute_state(bool mute_state) override;
91
92 protected:
96 void post_software_gain_(uint32_t rate_samples);
97
100 static void speaker_task(void *params);
101
103 virtual void run_speaker_task() = 0;
104
107 void stop_(bool wait_on_empty);
108
114 static bool i2s_on_sent_cb(i2s_chan_handle_t handle, i2s_event_data_t *event, void *user_ctx);
115
119 virtual esp_err_t start_i2s_driver(audio::AudioStreamInfo &audio_stream_info) = 0;
120
127 esp_err_t init_i2s_channel_(const i2s_chan_config_t &chan_cfg, const i2s_std_config_t &std_cfg,
128 size_t event_queue_size);
129
133 esp_err_t prepare_event_queues_(size_t event_queue_size);
134
135#ifdef USE_I2S_AUDIO_FULL_DUPLEX
139 esp_err_t acquire_full_duplex_channel_(size_t event_queue_size);
140#endif
141
143 void stop_i2s_driver_(bool keep_channel);
144
146 void park_dout_pin_();
147
149 void delete_channel_();
150
152 virtual void on_task_stopped() {}
153
160 template<typename F> bool resync_lockstep_(uint32_t extra_frames, F &&preload) {
161 this->drain_lockstep_(extra_frames);
162 return preload() && (i2s_channel_enable(this->tx_handle_) == ESP_OK);
163 }
164
167 void drain_lockstep_(uint32_t extra_frames);
168
173 void apply_software_volume_(uint8_t *data, size_t bytes_read);
174
176 static bool esp32_mono_swap_needed(const audio::AudioStreamInfo &stream_info);
178 bool esp32_mono_swap_needed_() const { return esp32_mono_swap_needed(this->output_stream_info_); }
179
184 void swap_esp32_mono_samples_(uint8_t *data, size_t bytes_read);
185
186 TaskHandle_t speaker_task_handle_{nullptr};
187 EventGroupHandle_t event_group_{nullptr};
188
189 // Lockstepped DMA buffer queues: i2s_event is outgoing, write_records is incoming
190 QueueHandle_t i2s_event_queue_{nullptr};
191 QueueHandle_t write_records_queue_{nullptr};
192
193 std::weak_ptr<ring_buffer::RingBuffer> audio_ring_buffer_;
194
196
197 optional<uint32_t> timeout_;
198
199 // A format start_i2s_driver() refused. Those checks compare against fixed settings, so it can never start.
200 optional<audio::AudioStreamInfo> rejected_stream_info_;
201
202 bool pause_state_{false};
203 bool keep_alive_{false};
204
205 // Smooths software gain changes. The main loop posts targets, the speaker task processes;
206 // GainRamp's mailbox makes that safe. The main loop is the only poster.
207 esp_audio_libs::gain::GainRamp gain_ramp_;
208
209 audio::AudioStreamInfo current_stream_info_; // Format of the audio in the ring buffer (the I2S input)
210 // Format actually clocked out of the I2S peripheral. Same channel count and sample rate as
211 // current_stream_info_, but the bits per sample may be converted to the speaker's configured slot bit width.
212 // Set by start_i2s_driver before the speaker task starts.
214
215 gpio_num_t dout_pin_;
216 i2s_chan_handle_t tx_handle_{nullptr};
217};
218
219} // namespace esphome::i2s_audio
220
221#endif // USE_ESP32
Abstract base class for I2S audio speaker implementations.
esp_audio_libs::gain::GainRamp gain_ramp_
std::weak_ptr< ring_buffer::RingBuffer > audio_ring_buffer_
optional< audio::AudioStreamInfo > rejected_stream_info_
void swap_esp32_mono_samples_(uint8_t *data, size_t bytes_read)
Plays the provided audio data.
void set_buffer_duration(uint32_t buffer_duration_ms)
void set_pause_state(bool pause_state) override
i2s_chan_handle_t get_tx_handle() const
Get the I2S TX channel handle.
virtual size_t play(const uint8_t *data, size_t length)=0
Plays the provided audio data.
virtual void set_volume(float volume)
Definition speaker.h:74
virtual void set_mute_state(bool mute_state)
Definition speaker.h:85
virtual bool has_buffered_data() const =0
__int64 ssize_t
Definition httplib.h:178
constexpr float PROCESSOR
For components that use data from sensors like displays.
Definition component.h:47
static void uint32_t
spi_device_handle_t handle