5#include <driver/i2s_std.h>
6#include <driver/i2s_pdm.h>
15static const UBaseType_t MAX_LISTENERS = 16;
17static const uint32_t READ_DURATION_MS = 16;
19static const size_t TASK_STACK_SIZE = 4096;
20static const ssize_t TASK_PRIORITY = 23;
37 ESP_LOGE(TAG,
"Creating semaphore failed");
44 ESP_LOGE(TAG,
"Creating event group failed");
49#ifdef USE_I2S_AUDIO_FULL_DUPLEX
50 if (this->
parent_->is_full_duplex() && this->parent_->is_failed()) {
51 this->
mark_failed(LOG_STR(
"Full duplex bus setup failed"));
64 " DC offset correction: %s",
69 uint8_t channel_count = 1;
70 uint8_t bits_per_sample = 16;
75 if (this->
slot_mode_ == I2S_SLOT_MODE_STEREO) {
79#ifdef USE_ESP32_VARIANT_ESP32
83 if (bits_per_sample < 16) {
85 }
else if ((bits_per_sample > 16) && (bits_per_sample <= 32)) {
105 i2s_std_slot_config_t std_slot_cfg =
110 i2s_std_gpio_config_t pin_config = this->
parent_->get_pin_config();
120 .slot_cfg = std_slot_cfg,
121 .gpio_cfg = pin_config,
125#ifdef USE_I2S_AUDIO_FULL_DUPLEX
129bool I2SAudioMicrophone::start_driver_() {
130#ifdef USE_I2S_AUDIO_FULL_DUPLEX
131 if (this->
parent_->is_full_duplex()) {
134 ESP_LOGE(TAG,
"Full duplex channel unavailable");
142 if (!this->
parent_->try_lock()) {
148 i2s_chan_config_t chan_cfg = {
149 .id = this->
parent_->get_port(),
152 .dma_frame_num = 256,
156 err = i2s_new_channel(&chan_cfg, NULL, &this->
rx_handle_);
162#if SOC_I2S_SUPPORTS_PDM_RX
164 i2s_std_gpio_config_t pin_config = this->
parent_->get_pin_config();
165 i2s_pdm_rx_clk_config_t clk_cfg = {
172 i2s_pdm_rx_slot_config_t slot_cfg = I2S_PDM_RX_SLOT_DEFAULT_CONFIG(I2S_DATA_BIT_WIDTH_16BIT, this->
slot_mode_);
174 case I2S_STD_SLOT_LEFT:
175 slot_cfg.slot_mask = I2S_PDM_SLOT_LEFT;
177 case I2S_STD_SLOT_RIGHT:
178 slot_cfg.slot_mask = I2S_PDM_SLOT_RIGHT;
180 case I2S_STD_SLOT_BOTH:
181 slot_cfg.slot_mask = I2S_PDM_SLOT_BOTH;
186 i2s_pdm_rx_config_t pdm_rx_cfg = {
188 .slot_cfg = slot_cfg,
191 .clk = pin_config.ws,
195 .clk_inv = pin_config.invert_flags.ws_inv,
199 err = i2s_channel_init_pdm_rx_mode(this->
rx_handle_, &pdm_rx_cfg);
203 i2s_std_config_t std_cfg;
206 err = i2s_channel_init_std_mode(this->
rx_handle_, &std_cfg);
232void I2SAudioMicrophone::stop_driver_() {
236#ifdef USE_I2S_AUDIO_FULL_DUPLEX
237 if (this->
parent_->is_full_duplex()) {
240 this->
parent_->release_rx_channel();
252 ESP_LOGW(TAG,
"Error stopping: %s", esp_err_to_name(err));
269 xEventGroupSetBits(this_microphone->
event_group_, MicrophoneEventGroupBits::TASK_STARTING);
274 std::vector<uint8_t> samples;
275 samples.reserve(bytes_to_read);
277 xEventGroupSetBits(this_microphone->
event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
279 while (!(xEventGroupGetBits(this_microphone->
event_group_) & MicrophoneEventGroupBits::COMMAND_STOP)) {
281 samples.resize(bytes_to_read);
282 size_t bytes_read = this_microphone->read_(samples.data(), bytes_to_read, 2 * READ_DURATION_MS);
283 samples.resize(bytes_read);
285 this_microphone->fix_dc_offset_(samples);
289 vTaskDelay(pdMS_TO_TICKS(READ_DURATION_MS));
294 xEventGroupSetBits(this_microphone->
event_group_, MicrophoneEventGroupBits::TASK_STOPPED);
297 vTaskDelay(pdMS_TO_TICKS(10));
301void I2SAudioMicrophone::fix_dc_offset_(std::vector<uint8_t> &data) {
341 const uint8_t dc_filter_shift = 10;
344 for (
uint32_t sample_index = 0; sample_index < total_samples; ++sample_index) {
345 const uint32_t byte_index = sample_index * bytes_per_sample;
355size_t I2SAudioMicrophone::read_(uint8_t *buf,
size_t len,
uint32_t timeout_ms) {
356 size_t bytes_read = 0;
357 esp_err_t err = i2s_channel_read(this->
rx_handle_, buf,
len, &bytes_read, timeout_ms);
358 if ((err != ESP_OK) && ((err != ESP_ERR_TIMEOUT) || (timeout_ms != 0))) {
362 ESP_LOGW(TAG,
"Read error: %s", esp_err_to_name(err));
367 if ((bytes_read == 0) && (timeout_ms > 0)) {
372#ifdef USE_ESP32_VARIANT_ESP32
375 int16_t *samples =
reinterpret_cast<int16_t *
>(buf);
376 size_t sample_count = bytes_read /
sizeof(int16_t);
377 for (
size_t i = 0; i + 1 < sample_count; i += 2) {
378 int16_t tmp = samples[i];
379 samples[i] = samples[i + 1];
380 samples[i + 1] = tmp;
390 if (event_group_bits & MicrophoneEventGroupBits::TASK_STARTING) {
391 ESP_LOGV(TAG,
"Task started, attempting to allocate buffer");
392 xEventGroupClearBits(this->
event_group_, MicrophoneEventGroupBits::TASK_STARTING);
395 if (event_group_bits & MicrophoneEventGroupBits::TASK_RUNNING) {
396 ESP_LOGV(TAG,
"Task is running and reading data");
398 xEventGroupClearBits(this->
event_group_, MicrophoneEventGroupBits::TASK_RUNNING);
402 if ((event_group_bits & MicrophoneEventGroupBits::TASK_STOPPED)) {
403 ESP_LOGV(TAG,
"Task finished, freeing resources and uninstalling driver");
407 this->stop_driver_();
432 if (!this->start_driver_()) {
433 ESP_LOGE(TAG,
"Driver failed to start; retrying in 1 second");
435 this->stop_driver_();
444 ESP_LOGE(TAG,
"Task failed to start, retrying in 1 second");
446 this->stop_driver_();
454 xEventGroupSetBits(this->
event_group_, MicrophoneEventGroupBits::COMMAND_STOP);
void mark_failed()
Mark this component as failed.
void status_set_warning()
void status_clear_error()
bool status_has_warning() const
bool status_has_error() const
void status_momentary_error(uint32_t length=5000)
Set error status flag and clear it after length ms.
void status_clear_warning()
I2SAudioComponent * parent_
size_t ms_to_bytes(uint32_t ms) const
Converts duration to bytes.
size_t samples_to_bytes(uint32_t samples) const
Converts samples to bytes.
uint32_t bytes_to_samples(size_t bytes) const
Convert bytes to samples.
i2s_std_slot_mask_t std_slot_mask_
i2s_slot_bit_width_t slot_bit_width_
i2s_slot_mode_t slot_mode_
i2s_clock_src_t get_clock_source_() const
i2s_mclk_multiple_t mclk_multiple_
SemaphoreHandle_t active_listeners_semaphore_
void build_full_duplex_config(i2s_std_config_t &std_cfg) override
TaskHandle_t task_handle_
int32_t dc_offset_prev_output_
EventGroupHandle_t event_group_
void configure_stream_settings_()
Starts the I2S driver.
void dump_config() override
int32_t dc_offset_prev_input_
static void mic_task(void *params)
void build_std_config_(i2s_std_config_t &std_cfg) const
Builds the standard (non-PDM) mode configuration from the configured settings.
i2s_chan_handle_t rx_handle_
audio::AudioStreamInfo audio_stream_info_
CallbackManager< void(const std::vector< uint8_t > &)> data_callbacks_
int32_t unpack_audio_sample_to_q31(const uint8_t *data, size_t bytes_per_sample)
Unpacks a quantized audio sample into a Q31 fixed-point number.
void pack_q31_as_audio_sample(int32_t sample, uint8_t *data, size_t bytes_per_sample)
Packs a Q31 fixed-point number as an audio sample with the specified number of bytes per sample.
const char * esp_err_to_name(esp_err_t err)
ESPHOME_LOG_TAG(TAG, "i2s_audio")