启动后播放开机声音完成

This commit is contained in:
CSBOOK\44744
2026-06-25 16:22:51 +08:00
parent 56ac1cd5d6
commit 5812fc500b
67 changed files with 342 additions and 204 deletions
@@ -296,7 +296,8 @@ typedef struct {
bool uplink_send_queue_with_caps;
TaskHandle_t uplink_task;
TaskHandle_t uplink_send_task;
volatile bool uplink_stop_requested;
volatile bool uplink_recording_active;
volatile bool uplink_task_exit_requested;
multimodal_audio_uplink_cb_t uplink_cb;
void *uplink_cb_ctx;
bool opus_ready;
@@ -79,24 +79,46 @@ static void reset_uplink_debug_stats(void)
#endif
}
static bool audio_get_uplink_stop_requested(const multimodal_audio_ctx_t *ctx)
static bool audio_get_uplink_recording_active(const multimodal_audio_ctx_t *ctx)
{
bool stop_requested = true;
bool active = false;
portENTER_CRITICAL(&s_audio_runtime_lock);
if (ctx != NULL) {
stop_requested = ctx->uplink_stop_requested;
active = ctx->uplink_recording_active;
}
portEXIT_CRITICAL(&s_audio_runtime_lock);
return stop_requested;
return active;
}
static void audio_set_uplink_stop_requested(multimodal_audio_ctx_t *ctx, bool stop_requested)
static void audio_set_uplink_recording_active(multimodal_audio_ctx_t *ctx, bool active)
{
portENTER_CRITICAL(&s_audio_runtime_lock);
if (ctx != NULL) {
ctx->uplink_stop_requested = stop_requested;
ctx->uplink_recording_active = active;
}
portEXIT_CRITICAL(&s_audio_runtime_lock);
}
static bool audio_get_uplink_task_exit_requested(const multimodal_audio_ctx_t *ctx)
{
bool exit_requested = true;
portENTER_CRITICAL(&s_audio_runtime_lock);
if (ctx != NULL) {
exit_requested = ctx->uplink_task_exit_requested;
}
portEXIT_CRITICAL(&s_audio_runtime_lock);
return exit_requested;
}
static void audio_set_uplink_task_exit_requested(multimodal_audio_ctx_t *ctx, bool exit_requested)
{
portENTER_CRITICAL(&s_audio_runtime_lock);
if (ctx != NULL) {
ctx->uplink_task_exit_requested = exit_requested;
}
portEXIT_CRITICAL(&s_audio_runtime_lock);
}
@@ -512,6 +534,7 @@ static void notify_downstream_playback_stopped(void);
static esp_err_t prime_downstream_output_path(multimodal_audio_ctx_t *ctx);
static esp_err_t handle_downstream_opus_sync(const uint8_t *data, size_t len);
static esp_err_t ensure_downstream_playback_task(multimodal_audio_ctx_t *ctx);
static esp_err_t ensure_uplink_worker_tasks(multimodal_audio_ctx_t *ctx);
static esp_err_t stop_downstream_idle_mute_task(multimodal_audio_ctx_t *ctx, int timeout_ms);
#define MULTIMODAL_AUDIO_DOWNSTREAM_STATS_INTERVAL 200U
@@ -620,25 +643,14 @@ static void uplink_send_task(void *arg)
(unsigned)get_uplink_send_queue_len(),
(unsigned)CONFIG_MULTIMODAL_AUDIO_UPLINK_MAX_PACKET_BYTES);
while (true) {
bool stop_requested = audio_get_uplink_stop_requested(ctx);
while (!audio_get_uplink_task_exit_requested(ctx)) {
if (!ctx->uplink_send_queue) {
if (stop_requested) {
break;
}
vTaskDelay(pdMS_TO_TICKS(20));
continue;
}
uplink_packet_t packet = {0};
if (stop_requested && uxQueueMessagesWaiting(ctx->uplink_send_queue) == 0U) {
break;
}
if (xQueueReceive(ctx->uplink_send_queue, &packet, pdMS_TO_TICKS(100)) != pdTRUE) {
if (stop_requested) {
break;
}
continue;
}
if (packet.len == 0U) {
@@ -697,7 +709,12 @@ static void uplink_capture_task(void *arg)
(unsigned)ctx->fs.channel,
(unsigned)ctx->mic_capture_channel);
while (!audio_get_uplink_stop_requested(ctx)) {
while (!audio_get_uplink_task_exit_requested(ctx)) {
if (!audio_get_uplink_recording_active(ctx)) {
ulTaskNotifyTake(pdTRUE, pdMS_TO_TICKS(100));
continue;
}
if (!ctx->initialized || !ctx->codec_dev || !ctx->opus_enc) {
vTaskDelay(pdMS_TO_TICKS(20));
continue;
@@ -736,6 +753,69 @@ static void uplink_capture_task(void *arg)
delete_audio_task_with_caps(NULL);
}
static esp_err_t ensure_uplink_worker_tasks(multimodal_audio_ctx_t *ctx)
{
if (ctx == NULL) {
return ESP_ERR_INVALID_ARG;
}
if (!ctx->initialized || !ctx->codec_dev || !ctx->opus_enc ||
!ctx->uplink_codec_buf || !ctx->uplink_mono_buf || !ctx->uplink_opus_buf) {
return ESP_ERR_INVALID_STATE;
}
if (ctx->uplink_send_queue == NULL) {
bool queue_with_caps = false;
UBaseType_t queue_len = get_uplink_send_queue_len();
ctx->uplink_send_queue = create_uplink_send_queue(queue_len, &queue_with_caps);
if (ctx->uplink_send_queue == NULL) {
ESP_LOGW(TAG,
"Create uplink send queue failed (len=%u item=%u)",
(unsigned)queue_len,
(unsigned)sizeof(uplink_packet_t));
return ESP_ERR_NO_MEM;
}
ctx->uplink_send_queue_with_caps = queue_with_caps;
}
audio_set_uplink_task_exit_requested(ctx, false);
TaskHandle_t capture_task = NULL;
TaskHandle_t send_task = NULL;
audio_snapshot_uplink_tasks(ctx, &capture_task, &send_task);
if (capture_task == NULL) {
BaseType_t created = create_audio_task_with_caps(uplink_capture_task,
"mm_audio_uplink",
get_uplink_task_stack_bytes(),
NULL,
5,
&ctx->uplink_task,
true,
MULTIMODAL_AUDIO_CORE_ID);
if (created != pdPASS) {
audio_set_uplink_capture_task(ctx, NULL);
return ESP_ERR_NO_MEM;
}
}
audio_snapshot_uplink_tasks(ctx, NULL, &send_task);
if (send_task == NULL) {
BaseType_t created = create_audio_task_with_caps(uplink_send_task,
"mm_audio_up_send",
get_uplink_send_task_stack_bytes(),
NULL,
6,
&ctx->uplink_send_task,
true,
MULTIMODAL_AUDIO_CORE_ID);
if (created != pdPASS) {
audio_set_uplink_send_task(ctx, NULL);
return ESP_ERR_NO_MEM;
}
}
return ESP_OK;
}
esp_err_t multimodal_audio_init(void)
{
#if !CONFIG_MULTIMODAL_AUDIO_ENABLE_CODEC_PATH
@@ -793,8 +873,29 @@ esp_err_t multimodal_audio_init(void)
audio_set_deinit_in_progress(&s_audio, false);
audio_set_downstream_playback_stopping(&s_audio, false);
audio_set_downstream_playback_stop_requested(&s_audio, false);
audio_set_uplink_recording_active(&s_audio, false);
audio_set_uplink_task_exit_requested(&s_audio, false);
s_audio.initialized = true;
audio_debug_dump_init();
err = ensure_uplink_worker_tasks(&s_audio);
if (err == ESP_OK) {
err = ensure_downstream_playback_task(&s_audio);
}
if (err != ESP_OK) {
ESP_LOGW(TAG, "Create audio runtime tasks failed: %s", esp_err_to_name(err));
s_audio.initialized = false;
audio_debug_dump_deinit();
(void)multimodal_audio_stop_uplink(1000);
(void)multimodal_audio_stop_downstream_playback();
cleanup_codec_path(&s_audio);
deinit_buses(&s_audio);
if (!reuse_prewarmed_opus) {
cleanup_opus_path(&s_audio);
memset(&s_audio, 0, sizeof(s_audio));
}
last_err = err;
continue;
}
ESP_LOGI(TAG,
"Codec initialized: %uHz %uch %ubit opus(frame=%ums bitrate=%ukbps)",
(unsigned)s_audio.fs.sample_rate,
@@ -838,6 +939,12 @@ void multimodal_audio_deinit(void)
audio_set_downstream_playback_stopping(&s_audio, true);
audio_debug_dump_deinit();
(void)multimodal_audio_stop_uplink(2000);
audio_set_uplink_task_exit_requested(&s_audio, true);
TaskHandle_t notify_capture_task = NULL;
audio_snapshot_uplink_tasks(&s_audio, &notify_capture_task, NULL);
if (notify_capture_task != NULL) {
xTaskNotifyGive(notify_capture_task);
}
(void)multimodal_audio_stop_downstream_playback();
for (int i = 0; i < 100 && audio_has_uplink_tasks(&s_audio); ++i) {
vTaskDelay(pdMS_TO_TICKS(20));
@@ -894,140 +1001,25 @@ esp_err_t multimodal_audio_start_uplink(multimodal_audio_uplink_cb_t cb, void *c
return ESP_ERR_INVALID_STATE;
}
if (audio_get_uplink_stop_requested(&s_audio)) {
ESP_LOGW(TAG, "Mic uplink stop in progress");
return ESP_ERR_INVALID_STATE;
}
if (audio_has_uplink_tasks(&s_audio)) {
return ESP_OK;
}
audio_set_uplink_callback(&s_audio, cb, cb_ctx);
s_audio.uplink_mono_fill_bytes = 0U;
memset(s_audio.uplink_mono_buf, 0, s_audio.uplink_mono_buf_capacity_bytes);
audio_set_uplink_stop_requested(&s_audio, false);
reset_uplink_debug_stats();
uint32_t uplink_stack_bytes = get_uplink_task_stack_bytes();
uint32_t uplink_send_stack_bytes = get_uplink_send_task_stack_bytes();
UBaseType_t uplink_send_queue_len = get_uplink_send_queue_len();
log_heap_snapshot("before_uplink_start");
if (s_audio.uplink_send_queue == NULL) {
bool queue_with_caps = false;
s_audio.uplink_send_queue = create_uplink_send_queue(uplink_send_queue_len, &queue_with_caps);
if (s_audio.uplink_send_queue == NULL) {
size_t free_internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
size_t largest_internal = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#if SOC_SPIRAM_SUPPORTED
size_t free_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
size_t largest_spiram = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_LOGW(TAG,
"Create uplink send queue failed (len=%u item=%u free_int=%u largest_int=%u free_spiram=%u largest_spiram=%u)",
(unsigned)uplink_send_queue_len,
(unsigned)sizeof(uplink_packet_t),
(unsigned)free_internal,
(unsigned)largest_internal,
(unsigned)free_spiram,
(unsigned)largest_spiram);
#else
ESP_LOGW(TAG,
"Create uplink send queue failed (len=%u item=%u free_int=%u largest_int=%u)",
(unsigned)uplink_send_queue_len,
(unsigned)sizeof(uplink_packet_t),
(unsigned)free_internal,
(unsigned)largest_internal);
#endif
audio_set_uplink_callback(&s_audio, NULL, NULL);
audio_set_uplink_stop_requested(&s_audio, false);
return ESP_ERR_NO_MEM;
}
s_audio.uplink_send_queue_with_caps = queue_with_caps;
} else {
reset_uplink_send_queue(&s_audio);
}
BaseType_t created = create_audio_task_with_caps(uplink_capture_task,
"mm_audio_uplink",
uplink_stack_bytes,
NULL,
5,
&s_audio.uplink_task,
true,
MULTIMODAL_AUDIO_CORE_ID);
if (created != pdPASS) {
audio_set_uplink_capture_task(&s_audio, NULL);
size_t free_internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
size_t largest_internal = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#if SOC_SPIRAM_SUPPORTED
size_t free_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
size_t largest_spiram = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_LOGW(TAG,
"Create uplink capture task failed (stack=%uB free_int=%u largest_int=%u free_spiram=%u largest_spiram=%u)",
(unsigned)uplink_stack_bytes,
(unsigned)free_internal,
(unsigned)largest_internal,
(unsigned)free_spiram,
(unsigned)largest_spiram);
#else
ESP_LOGW(TAG,
"Create uplink capture task failed (stack=%uB free_int=%u largest_int=%u)",
(unsigned)uplink_stack_bytes,
(unsigned)free_internal,
(unsigned)largest_internal);
#endif
reset_uplink_send_queue(&s_audio);
esp_err_t err = ensure_uplink_worker_tasks(&s_audio);
if (err != ESP_OK) {
audio_set_uplink_callback(&s_audio, NULL, NULL);
audio_set_uplink_stop_requested(&s_audio, false);
return ESP_ERR_NO_MEM;
return err;
}
TaskHandle_t existing_send_task = NULL;
audio_snapshot_uplink_tasks(&s_audio, NULL, &existing_send_task);
if (existing_send_task == NULL) {
BaseType_t send_created = create_audio_task_with_caps(uplink_send_task,
"mm_audio_up_send",
uplink_send_stack_bytes,
NULL,
6,
&s_audio.uplink_send_task,
true,
MULTIMODAL_AUDIO_CORE_ID);
if (send_created != pdPASS) {
audio_set_uplink_send_task(&s_audio, NULL);
audio_set_uplink_stop_requested(&s_audio, true);
for (int i = 0; i < 40 && audio_has_uplink_tasks(&s_audio); ++i) {
vTaskDelay(pdMS_TO_TICKS(20));
}
size_t free_internal = heap_caps_get_free_size(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
size_t largest_internal = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT);
#if SOC_SPIRAM_SUPPORTED
size_t free_spiram = heap_caps_get_free_size(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
size_t largest_spiram = heap_caps_get_largest_free_block(MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
ESP_LOGW(TAG,
"Create uplink send task failed (stack=%uB free_int=%u largest_int=%u free_spiram=%u largest_spiram=%u)",
(unsigned)uplink_send_stack_bytes,
(unsigned)free_internal,
(unsigned)largest_internal,
(unsigned)free_spiram,
(unsigned)largest_spiram);
#else
ESP_LOGW(TAG,
"Create uplink send task failed (stack=%uB free_int=%u largest_int=%u)",
(unsigned)uplink_send_stack_bytes,
(unsigned)free_internal,
(unsigned)largest_internal);
#endif
reset_uplink_send_queue(&s_audio);
audio_set_uplink_callback(&s_audio, NULL, NULL);
if (!audio_has_uplink_tasks(&s_audio)) {
audio_set_uplink_stop_requested(&s_audio, false);
}
return ESP_ERR_NO_MEM;
}
reset_uplink_send_queue(&s_audio);
audio_set_uplink_recording_active(&s_audio, true);
TaskHandle_t capture_task = NULL;
audio_snapshot_uplink_tasks(&s_audio, &capture_task, NULL);
if (capture_task != NULL) {
xTaskNotifyGive(capture_task);
}
log_heap_snapshot("after_uplink_start");
return ESP_OK;
}
@@ -1038,63 +1030,18 @@ esp_err_t multimodal_audio_stop_uplink(int timeout_ms)
return ESP_ERR_INVALID_ARG;
}
if (!audio_has_uplink_tasks(&s_audio)) {
reset_uplink_send_queue(&s_audio);
audio_set_uplink_callback(&s_audio, NULL, NULL);
s_audio.uplink_mono_fill_bytes = 0U;
audio_set_uplink_stop_requested(&s_audio, false);
return ESP_OK;
}
audio_set_uplink_stop_requested(&s_audio, true);
if (timeout_ms == 0) {
audio_set_uplink_callback(&s_audio, NULL, NULL);
return ESP_OK;
}
int effective_timeout_ms = timeout_ms;
int waited_ms = 0;
while (audio_has_uplink_tasks(&s_audio) && waited_ms < effective_timeout_ms) {
vTaskDelay(pdMS_TO_TICKS(20));
waited_ms += 20;
}
TaskHandle_t capture_task = NULL;
TaskHandle_t send_task = NULL;
audio_snapshot_uplink_tasks(&s_audio, &capture_task, &send_task);
esp_err_t result = ESP_OK;
if (capture_task != NULL || send_task != NULL) {
ESP_LOGW(TAG,
"Mic uplink stop timeout, force delete (capture=%p send=%p)",
(void *)capture_task,
(void *)send_task);
result = ESP_ERR_TIMEOUT;
if (capture_task != NULL) {
delete_audio_task_with_caps(capture_task);
audio_set_uplink_capture_task(&s_audio, NULL);
}
if (send_task != NULL) {
delete_audio_task_with_caps(send_task);
audio_set_uplink_send_task(&s_audio, NULL);
}
}
(void)timeout_ms;
audio_set_uplink_recording_active(&s_audio, false);
reset_uplink_send_queue(&s_audio);
audio_set_uplink_callback(&s_audio, NULL, NULL);
s_audio.uplink_mono_fill_bytes = 0U;
audio_set_uplink_stop_requested(&s_audio, false);
log_heap_snapshot("after_uplink_stop");
return result;
return ESP_OK;
}
bool multimodal_audio_is_uplink_running(void)
{
TaskHandle_t capture_task = NULL;
TaskHandle_t send_task = NULL;
audio_snapshot_uplink_tasks(&s_audio, &capture_task, &send_task);
return capture_task != NULL &&
send_task != NULL &&
!audio_get_uplink_stop_requested(&s_audio);
return audio_get_uplink_recording_active(&s_audio);
}
static uint32_t snapshot_downstream_playback_generation(void)
@@ -1664,9 +1611,6 @@ static void downstream_playback_task(void *arg)
if (received != pdTRUE || packet.data == NULL || packet.len == 0U) {
if (snapshot_downstream_response_end_received()) {
maybe_notify_downstream_playback_completed_after_drain("queue_empty");
if (!snapshot_downstream_response_active()) {
break;
}
}
vTaskDelay(pdMS_TO_TICKS(10));
continue;
@@ -1923,10 +1867,15 @@ esp_err_t multimodal_audio_stop_downstream_playback(void)
(void)advance_downstream_playback_generation();
notify_downstream_playback_stopped();
bool keep_stopping = audio_get_deinit_in_progress(ctx);
esp_err_t task_stop_err = stop_downstream_playback_task(ctx, 1000, keep_stopping);
if (task_stop_err != ESP_OK && task_stop_err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "Stop downstream playback task failed: %s", esp_err_to_name(task_stop_err));
if (audio_get_deinit_in_progress(ctx)) {
esp_err_t task_stop_err = stop_downstream_playback_task(ctx, 1000, true);
if (task_stop_err != ESP_OK && task_stop_err != ESP_ERR_INVALID_STATE) {
ESP_LOGW(TAG, "Stop downstream playback task failed: %s", esp_err_to_name(task_stop_err));
}
} else {
drain_downstream_playback_queue(ctx);
audio_set_downstream_playback_stop_requested(ctx, false);
audio_set_downstream_playback_stopping(ctx, false);
}
if (!ctx->initialized || ctx->codec_dev == NULL) {