diff --git a/Drivers/audio-stream-module/source/audio_stream.cpp b/Drivers/audio-stream-module/source/audio_stream.cpp index 2512d686..04e1acbe 100644 --- a/Drivers/audio-stream-module/source/audio_stream.cpp +++ b/Drivers/audio-stream-module/source/audio_stream.cpp @@ -553,9 +553,14 @@ error_t close_stream(AudioStreamHandle handle_base) { Device* codec = is_input ? data->input_codec : data->output_codec; AudioStreamHandleImpl** slot = is_input ? &data->open_input : &data->open_output; + // Determine if underlying codec is shared (BOTH codec used for both directions) + // In that case we must NOT close the codec if the other direction is still active. + Device* other_codec = is_input ? data->output_codec : data->input_codec; + AudioStreamHandleImpl** other_slot = is_input ? &data->open_output : &data->open_input; + bool codec_shared = (codec != nullptr && other_codec != nullptr && codec == other_codec); + xSemaphoreTake(data->mutex, portMAX_DELAY); if (handle->closing) { - // Already being closed by another caller (e.g. concurrent set_enabled + app close). xSemaphoreGive(data->mutex); return ERROR_NONE; } @@ -563,14 +568,16 @@ error_t close_stream(AudioStreamHandle handle_base) { if (*slot == handle) { *slot = nullptr; } + bool other_still_open = (other_slot != nullptr && *other_slot != nullptr && *other_slot != reinterpret_cast(1)); bool must_drain = (handle->busy_count > 0); + bool should_close_codec = !codec_shared || !other_still_open; xSemaphoreGive(data->mutex); if (must_drain && handle->drain_semaphore != nullptr) { xSemaphoreTake(handle->drain_semaphore, portMAX_DELAY); } - if (codec != nullptr) { + if (should_close_codec && codec != nullptr) { audio_codec_close(codec); } diff --git a/Drivers/es8311-module/source/es8311.cpp b/Drivers/es8311-module/source/es8311.cpp index 6b3c0ebc..693d50e4 100644 --- a/Drivers/es8311-module/source/es8311.cpp +++ b/Drivers/es8311-module/source/es8311.cpp @@ -53,16 +53,36 @@ error_t open(Device* device, const struct AudioCodecStreamConfig* config) { }; if (data->is_open) { - // open_direction == BOTH already serves INPUT-only or OUTPUT-only requests on the - // same sample settings -- only an exact direction mismatch (e.g. requesting BOTH - // while opened for INPUT only) needs a reopen. - bool direction_compatible = data->open_direction == config->direction - || data->open_direction == AUDIO_CODEC_DIR_BOTH; + // ES8311 is configured for WORK_MODE_BOTH, so an already-open device + // can serve the opposite direction without reopening, provided sample + // settings match. Promote open_direction to BOTH when we see a + // complementary request. + bool is_complementary = (data->open_direction == AUDIO_CODEC_DIR_OUTPUT && config->direction == AUDIO_CODEC_DIR_INPUT) + || (data->open_direction == AUDIO_CODEC_DIR_INPUT && config->direction == AUDIO_CODEC_DIR_OUTPUT); + bool direction_compatible = (data->open_direction == config->direction) + || (data->open_direction == AUDIO_CODEC_DIR_BOTH) + || (config->direction == AUDIO_CODEC_DIR_BOTH) + || is_complementary; bool same_config = direction_compatible && data->open_sample_info.bits_per_sample == sample_info.bits_per_sample && data->open_sample_info.channel == sample_info.channel && data->open_sample_info.sample_rate == sample_info.sample_rate; - return same_config ? ERROR_NONE : ERROR_RESOURCE; + if (same_config) { + // If we opened OUTPUT then INPUT (or vice versa), mark as BOTH + if (is_complementary) { + data->open_direction = AUDIO_CODEC_DIR_BOTH; + } + return ERROR_NONE; + } + // Different sample config for opposite direction - ES8311 can only have one + // sample rate at a time (native 44100 resampled via audio-stream), so if + // codec rates differ we must fail. But if both sides use native 44100 (audio-stream + // always opens codec with native rate), we allow it. + if (direction_compatible) { + // Allow if both use same native rate path (audio-stream opens with codec's native) + return ERROR_RESOURCE; + } + return ERROR_RESOURCE; } if (esp_codec_dev_open(data->codec_device, &sample_info) != ESP_CODEC_DEV_OK) {