Merge branch 'bump-noise-c-0.1.11' into integration

This commit is contained in:
J. Nick Koston
2026-03-08 14:42:41 -10:00
10 changed files with 59 additions and 35 deletions
+1 -1
View File
@@ -1 +1 @@
b6f8c16c1ddd222134bf4a71910b4c832e764e23caf49f9bce3280b079955fcf
e4b9c4b54e705d3c9400e1cdda8ba0b32634780cfa5f32271832e911bdcafe7e
+1 -1
View File
@@ -453,7 +453,7 @@ async def to_code(config: ConfigType) -> None:
# and plaintext disabled. Only a factory reset can remove it.
cg.add_define("USE_API_PLAINTEXT")
cg.add_define("USE_API_NOISE")
cg.add_library("esphome/noise-c", "0.1.10")
cg.add_library("esphome/noise-c", "0.1.11")
else:
cg.add_define("USE_API_PLAINTEXT")
@@ -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
@@ -11,14 +11,14 @@
#include <array>
#include <cstring>
namespace esphome {
namespace mixer_speaker {
namespace esphome::mixer_speaker {
static const UBaseType_t MIXER_TASK_PRIORITY = 10;
static const uint32_t STOPPING_TIMEOUT_MS = 5000;
static const uint32_t TRANSFER_BUFFER_DURATION_MS = 50;
static const uint32_t TASK_DELAY_MS = 25;
static const uint32_t MIXER_AUTO_STOP_DEBOUNCE_MS = 200;
static const size_t TASK_STACK_SIZE = 4096;
@@ -471,6 +471,7 @@ void MixerSpeaker::loop() {
this->task_.deallocate();
ESP_LOGD(TAG, "Stopped");
xEventGroupClearBits(this->event_group_, MIXER_TASK_ALL_BITS);
this->all_stopped_since_ms_ = 0;
}
if (this->task_.is_created()) {
@@ -483,8 +484,18 @@ void MixerSpeaker::loop() {
}
if (all_stopped) {
// Send stop command signal to the mixer task since no source speakers are active
xEventGroupSetBits(this->event_group_, MIXER_TASK_COMMAND_STOP);
if (this->all_stopped_since_ms_ == 0) {
this->all_stopped_since_ms_ = millis();
} else if ((millis() - this->all_stopped_since_ms_) >= MIXER_AUTO_STOP_DEBOUNCE_MS) {
// Send stop command only after a short debounce to avoid stop/start thrash during rapid seeks.
xEventGroupSetBits(this->event_group_, MIXER_TASK_COMMAND_STOP);
}
} else {
this->all_stopped_since_ms_ = 0;
// New activity detected; clear any stale auto-stop request before it can stop the running task.
if (event_group_bits & MIXER_TASK_COMMAND_STOP) {
xEventGroupClearBits(this->event_group_, MIXER_TASK_COMMAND_STOP);
}
}
} else {
// Task is fully stopped and cleaned up, check if we can disable loop
@@ -515,6 +526,9 @@ esp_err_t MixerSpeaker::start(audio::AudioStreamInfo &stream_info) {
this->enable_loop_soon_any_context(); // ensure loop processes command
// Starting a new stream supersedes any previously queued stop request.
xEventGroupClearBits(this->event_group_, MIXER_TASK_COMMAND_STOP);
uint32_t event_bits = xEventGroupGetBits(this->event_group_);
if (!(event_bits & MIXER_TASK_COMMAND_START)) {
// Set MIXER_TASK_COMMAND_START bit if not already set, and then immediately wake for low latency
@@ -755,7 +769,6 @@ void MixerSpeaker::audio_mixer_task(void *params) {
vTaskSuspend(nullptr); // Suspend this task indefinitely until the loop method deletes it
}
} // namespace mixer_speaker
} // namespace esphome
} // namespace esphome::mixer_speaker
#endif
@@ -14,8 +14,7 @@
#include <atomic>
namespace esphome {
namespace mixer_speaker {
namespace esphome::mixer_speaker {
/* Classes for mixing several source speaker audio streams and writing it to another speaker component.
* - Volume controls are passed through to the output speaker
@@ -200,9 +199,9 @@ class MixerSpeaker : public Component {
optional<audio::AudioStreamInfo> audio_stream_info_;
std::atomic<uint32_t> frames_in_pipeline_{0}; // Frames written to output but not yet played
uint32_t all_stopped_since_ms_{0}; // Debounce transient all-stopped windows before stopping task
};
} // namespace mixer_speaker
} // namespace esphome
} // namespace esphome::mixer_speaker
#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;
}
}
+2 -2
View File
@@ -46,7 +46,7 @@ lib_deps_base =
lib_deps =
${common.lib_deps_base}
esphome/noise-c@0.1.10 ; api
esphome/noise-c@0.1.11 ; api
improv/Improv@1.2.4 ; improv_serial / esp32_improv
kikuchan98/pngle@1.1.0 ; online_image
; Using the repository directly, otherwise ESP-IDF can't use the library
@@ -542,7 +542,7 @@ build_unflags =
extends = common
platform = platformio/native
lib_deps =
esphome/noise-c@0.1.10 ; used by api
esphome/noise-c@0.1.11 ; used by api
build_flags =
${common.build_flags}
-DUSE_HOST