mirror of
https://github.com/esphome/esphome.git
synced 2026-09-01 02:26:01 +00:00
[i2s_audio] Fix mono sample swap and block 8-bit mono on ESP32 (#14516)
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com> Co-authored-by: J. Nick Koston <nick@koston.org>
This commit is contained in:
co-authored by
Claude Opus 4.6
J. Nick Koston
parent
50b3f9d25c
commit
d5dc4a39cb
@@ -46,6 +46,12 @@ def _validate_esp32_variant(config):
|
||||
if config[CONF_ADC_TYPE] == "external":
|
||||
if config[CONF_PDM] and variant not in PDM_VARIANTS:
|
||||
raise cv.Invalid(f"{variant} does not support PDM")
|
||||
if (
|
||||
variant == esp32.VARIANT_ESP32
|
||||
and config.get(CONF_BITS_PER_SAMPLE) == 8
|
||||
and config.get(CONF_CHANNEL) in (CONF_LEFT, CONF_RIGHT)
|
||||
):
|
||||
raise cv.Invalid("8-bit mono mode is not supported on ESP32")
|
||||
return config
|
||||
if config[CONF_ADC_TYPE] == "internal":
|
||||
if variant not in INTERNAL_ADC_VARIANTS:
|
||||
|
||||
@@ -281,7 +281,7 @@ bool I2SAudioMicrophone::start_driver_() {
|
||||
}
|
||||
|
||||
/* Before reading data, start the RX channel first */
|
||||
i2s_channel_enable(this->rx_handle_);
|
||||
err = i2s_channel_enable(this->rx_handle_);
|
||||
if (err != ESP_OK) {
|
||||
ESP_LOGE(TAG, "Enabling failed: %s", esp_err_to_name(err));
|
||||
return false;
|
||||
@@ -454,13 +454,14 @@ size_t I2SAudioMicrophone::read_(uint8_t *buf, size_t len, TickType_t ticks_to_w
|
||||
}
|
||||
this->status_clear_warning();
|
||||
#if defined(USE_ESP32_VARIANT_ESP32) and not defined(USE_I2S_LEGACY)
|
||||
// For ESP32 8/16 bit standard mono mode samples need to be switched.
|
||||
if (this->slot_mode_ == I2S_SLOT_MODE_MONO && this->slot_bit_width_ <= 16 && !this->pdm_) {
|
||||
size_t samples_read = bytes_read / sizeof(int16_t);
|
||||
for (int i = 0; i < samples_read; i += 2) {
|
||||
int16_t tmp = buf[i];
|
||||
buf[i] = buf[i + 1];
|
||||
buf[i + 1] = tmp;
|
||||
// For ESP32 16-bit standard mono mode, adjacent samples need to be swapped.
|
||||
if (this->slot_mode_ == I2S_SLOT_MODE_MONO && this->slot_bit_width_ == I2S_SLOT_BIT_WIDTH_16BIT && !this->pdm_) {
|
||||
int16_t *samples = reinterpret_cast<int16_t *>(buf);
|
||||
size_t sample_count = bytes_read / sizeof(int16_t);
|
||||
for (size_t i = 0; i + 1 < sample_count; i += 2) {
|
||||
int16_t tmp = samples[i];
|
||||
samples[i] = samples[i + 1];
|
||||
samples[i + 1] = tmp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -100,11 +100,16 @@ def _set_stream_limits(config):
|
||||
|
||||
|
||||
def _validate_esp32_variant(config):
|
||||
if config[CONF_DAC_TYPE] != "internal":
|
||||
return config
|
||||
variant = esp32.get_esp32_variant()
|
||||
if variant not in INTERNAL_DAC_VARIANTS:
|
||||
raise cv.Invalid(f"{variant} does not have an internal DAC")
|
||||
if config[CONF_DAC_TYPE] == "internal":
|
||||
if variant not in INTERNAL_DAC_VARIANTS:
|
||||
raise cv.Invalid(f"{variant} does not have an internal DAC")
|
||||
elif (
|
||||
variant == esp32.VARIANT_ESP32
|
||||
and config.get(CONF_BITS_PER_SAMPLE) == 8
|
||||
and config.get(CONF_CHANNEL) in (CONF_MONO, CONF_LEFT, CONF_RIGHT)
|
||||
):
|
||||
raise cv.Invalid("8-bit mono mode is not supported on ESP32")
|
||||
return config
|
||||
|
||||
|
||||
|
||||
@@ -372,15 +372,15 @@ void I2SAudioSpeaker::speaker_task(void *params) {
|
||||
}
|
||||
|
||||
#ifdef USE_ESP32_VARIANT_ESP32
|
||||
// For ESP32 8/16 bit mono mode samples need to be switched.
|
||||
// For ESP32 16-bit mono mode, adjacent samples need to be swapped.
|
||||
if (this_speaker->current_stream_info_.get_channels() == 1 &&
|
||||
this_speaker->current_stream_info_.get_bits_per_sample() <= 16) {
|
||||
size_t len = bytes_read / sizeof(int16_t);
|
||||
int16_t *tmp_buf = (int16_t *) new_data;
|
||||
for (size_t i = 0; i < len; i += 2) {
|
||||
int16_t tmp = tmp_buf[i];
|
||||
tmp_buf[i] = tmp_buf[i + 1];
|
||||
tmp_buf[i + 1] = tmp;
|
||||
this_speaker->current_stream_info_.get_bits_per_sample() == 16) {
|
||||
int16_t *samples = reinterpret_cast<int16_t *>(new_data);
|
||||
size_t sample_count = bytes_read / sizeof(int16_t);
|
||||
for (size_t i = 0; i + 1 < sample_count; i += 2) {
|
||||
int16_t tmp = samples[i];
|
||||
samples[i] = samples[i + 1];
|
||||
samples[i + 1] = tmp;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -504,7 +504,7 @@ void AudioPipeline::decode_task(void *params) {
|
||||
if (!started_playback && has_stream_info) {
|
||||
// Verify enough data is available before starting playback
|
||||
std::shared_ptr<RingBuffer> temp_ring_buffer = this_pipeline->raw_file_ring_buffer_.lock();
|
||||
if (temp_ring_buffer->available() >= initial_bytes_to_buffer) {
|
||||
if (temp_ring_buffer != nullptr && temp_ring_buffer->available() >= initial_bytes_to_buffer) {
|
||||
started_playback = true;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user