From 220e4fab27b7f419b3e89e2f2d28ae789aac09d7 Mon Sep 17 00:00:00 2001
From: wangzhibo <wangzhibo@shsening.com>
Date: 星期四, 08 十月 2026 16:29:55 +0800
Subject: [PATCH] 5种场景模式家长端ok
---
xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mp3_player.cc | 203 +++++++++++++++++++++++++++++++++++++-------------
1 files changed, 149 insertions(+), 54 deletions(-)
diff --git a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mp3_player.cc b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mp3_player.cc
index ca97f2b..007bcaf 100644
--- a/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mp3_player.cc
+++ b/xiaozhi-esp32-2.2.6/main/boards/sening-xiaozhi-s3/mp3_player.cc
@@ -30,7 +30,7 @@
typedef struct {
int count;
- sdcard_mp3_playlist_item_t items[SDCARD_MP3_PLAYLIST_MAX];
+ sdcard_mp3_playlist_item_t items[];
} mp3_player_task_arg_t;
static TaskHandle_t mp3_player_task_handle;
@@ -41,6 +41,7 @@
static volatile int mp3_repeat_left;
static TickType_t s_mp3_audible_started;
static SemaphoreHandle_t s_shadowing_listen_closed;
+static volatile bool s_mp3_offline_wake_ok;
static int16_t *s_pcm_a = nullptr;
static int16_t *s_pcm_b = nullptr;
@@ -143,23 +144,110 @@
return !mp3_stop_requested;
}
-static void wait_for_sd_dma(void)
+static bool wait_for_internal_sram(size_t need_bytes, int tries)
{
- for (int i = 0; i < 20; ++i) {
+ for (int i = 0; i < tries; ++i) {
size_t largest = heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA);
- if (largest >= 4096) {
- return;
+ if (largest >= need_bytes) {
+ return true;
}
- ESP_LOGW(TAG, "wait SD DMA largest=%u try=%d", (unsigned)largest, i);
+ ESP_LOGW(TAG, "wait SD DMA largest=%u need=%u try=%d",
+ (unsigned)largest, (unsigned)need_bytes, i);
vTaskDelay(pdMS_TO_TICKS(40));
}
+ return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) >= need_bytes;
+}
+
+static bool sd_internal_heap_ok_for_fopen(void)
+{
+ return heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL | MALLOC_CAP_DMA) >= 4096 &&
+ heap_caps_get_largest_free_block(MALLOC_CAP_INTERNAL) >= 2048;
+}
+
+static FILE *fopen_mp3_if_heap_ok(const char *path)
+{
+ wait_for_internal_sram(4096, 25);
+ if (!sd_internal_heap_ok_for_fopen()) {
+ ESP_LOGE(TAG, "refuse fopen, internal DMA heap too small: %s", path);
+ return nullptr;
+ }
+ return fopen(path, "rb");
+}
+
+static void ensure_shadowing_listen_sem(void);
+
+static void close_mp3_decode_resources(esp_audio_simple_dec_handle_t *decoder,
+ esp_ae_rate_cvt_handle_t *rate_cvt,
+ FILE **fp, uint8_t **file_buf)
+{
+ if (rate_cvt && *rate_cvt) {
+ esp_ae_rate_cvt_close(*rate_cvt);
+ *rate_cvt = nullptr;
+ }
+ if (decoder && *decoder) {
+ esp_audio_simple_dec_close(*decoder);
+ *decoder = nullptr;
+ }
+ if (fp && *fp) {
+ fclose(*fp);
+ *fp = nullptr;
+ }
+ if (file_buf && *file_buf) {
+ heap_caps_free(*file_buf);
+ *file_buf = nullptr;
+ }
+}
+
+static void apply_stop_afe_for_sd(bool leave_listening)
+{
+ auto &app = Application::GetInstance();
+ if (leave_listening) {
+ app.HoldIdleForServerPrompt();
+ if (app.GetDeviceState() == kDeviceStateSpeaking) {
+ app.AbortSpeaking(kAbortReasonNone);
+ }
+ if (app.GetDeviceState() == kDeviceStateListening) {
+ app.SetDeviceState(kDeviceStateIdle);
+ }
+ }
+ auto &audio = app.GetAudioService();
+ audio.EnableVoiceProcessing(false);
+ audio.EnableWakeWordDetection(false);
+ audio.ReleaseAfeRuntime();
+}
+
+static void stop_afe_for_sd_and_wait(bool leave_listening)
+{
+ s_mp3_offline_wake_ok = false;
+ auto &app = Application::GetInstance();
+ if (app.IsOnMainLoop()) {
+ apply_stop_afe_for_sd(leave_listening);
+ wait_for_internal_sram(4096, 40);
+ return;
+ }
+ ensure_shadowing_listen_sem();
+ xSemaphoreTake(s_shadowing_listen_closed, 0);
+ app.Schedule([leave_listening]() {
+ apply_stop_afe_for_sd(leave_listening);
+ if (s_shadowing_listen_closed != nullptr) {
+ xSemaphoreGive(s_shadowing_listen_closed);
+ }
+ });
+ if (xSemaphoreTake(s_shadowing_listen_closed, pdMS_TO_TICKS(2500)) != pdTRUE) {
+ ESP_LOGW(TAG, "pause wake for SD timeout");
+ }
+ wait_for_internal_sram(4096, 40);
+}
+
+static void pause_wake_for_sd(void)
+{
+ stop_afe_for_sd_and_wait(false);
}
static uint8_t *load_mp3_to_psram(const char *path, size_t *out_len)
{
*out_len = 0;
- wait_for_sd_dma();
- FILE *fp = fopen(path, "rb");
+ FILE *fp = fopen_mp3_if_heap_ok(path);
if (!fp) {
return nullptr;
}
@@ -274,16 +362,6 @@
return true;
}
-static void pause_wake_for_sd(void)
-{
- Application::GetInstance().Schedule([]() {
- auto &audio = Application::GetInstance().GetAudioService();
- audio.EnableVoiceProcessing(false);
- audio.EnableWakeWordDetection(false);
- });
- vTaskDelay(pdMS_TO_TICKS(40));
-}
-
static void wait_shadowing_listen(unsigned duration_ms)
{
unsigned wait_ms = Board_ShadowingListenWindowMs(duration_ms);
@@ -322,6 +400,7 @@
if (xSemaphoreTake(s_shadowing_listen_closed, pdMS_TO_TICKS(2500)) != pdTRUE) {
ESP_LOGW(TAG, "shadowing wait mic off timeout, force AFE stop");
Application::GetInstance().GetAudioService().EnableVoiceProcessing(false);
+ Application::GetInstance().GetAudioService().EnableWakeWordDetection(false);
}
}
@@ -329,26 +408,26 @@
{
mp3_player_task_handle = xTaskGetCurrentTaskHandle();
mp3_player_task_arg_t *task_arg = static_cast<mp3_player_task_arg_t *>(arg);
- sdcard_mp3_playlist_item_t items[SDCARD_MP3_PLAYLIST_MAX];
- int count = task_arg->count;
+ int count = task_arg ? task_arg->count : 0;
if (count < 0) {
count = 0;
}
if (count > SDCARD_MP3_PLAYLIST_MAX) {
count = SDCARD_MP3_PLAYLIST_MAX;
}
- if (count > 0) {
- memcpy(items, task_arg->items, sizeof(items[0]) * static_cast<size_t>(count));
- }
- bool power_off_after = count > 0 ? items[count - 1].power_off_after : false;
- heap_caps_free(task_arg);
+ const sdcard_mp3_playlist_item_t *items = (task_arg && count > 0) ? task_arg->items : nullptr;
+ bool power_off_after = (items != nullptr) ? items[count - 1].power_off_after : false;
auto codec = Board::GetInstance().GetAudioCodec();
- if (!codec || count < 1) {
+ if (!codec || count < 1 || items == nullptr) {
ESP_LOGE(TAG, "audio codec unavailable or empty playlist");
+ if (task_arg) {
+ heap_caps_free(task_arg);
+ }
mp3_player_task_handle = nullptr;
mp3_stop_requested = false;
mp3_repeat_left = 0;
+ s_mp3_offline_wake_ok = false;
Board_OnSkillPlaybackFinished(true, false, 0);
vTaskDelete(nullptr);
return;
@@ -371,30 +450,38 @@
ESP_LOGE(TAG, "no memory for MP3 playback");
}
+ s_mp3_offline_wake_ok = false;
for (int item_i = 0; in_buf && out_buf && item_i < count && !mp3_stop_requested; ++item_i) {
strlcpy(path, items[item_i].path, sizeof(path));
float speed = SDCard_Clamp_MP3_Speed(items[item_i].speed);
int repeat = items[item_i].repeat < 1 ? 1 : items[item_i].repeat;
- if (item_i > 0) {
- pause_wake_for_sd();
- }
+ pause_wake_for_sd();
size_t file_len = 0;
uint8_t *file_buf = load_mp3_to_psram(path, &file_len);
+ if (!file_buf) {
+ pause_wake_for_sd();
+ file_buf = load_mp3_to_psram(path, &file_len);
+ }
FILE *fp = nullptr;
if (!file_buf) {
- wait_for_sd_dma();
- fp = fopen(path, "rb");
+ fp = fopen_mp3_if_heap_ok(path);
}
if (!file_buf && !fp) {
ESP_LOGE(TAG, "open MP3 failed: %s", path);
continue;
}
- if (!mp3_pause_requested) {
- Application::GetInstance().Schedule([]() {
- Application::GetInstance().ApplyMp3CommandOnlyMic();
- });
+ // 鍏堟妸鏂囦欢璇昏繘 PSRAM锛屽啀寮�绂荤嚎鍙d护鍞ら啋銆傚弽杩囨潵浼氭妸 SD DMA 闇�瑕佺殑杩炵画鍐呴儴 SRAM 鍚冩帀銆�
+ if (file_buf) {
+ s_mp3_offline_wake_ok = true;
+ if (!mp3_pause_requested) {
+ Application::GetInstance().Schedule([]() {
+ Application::GetInstance().ApplyMp3CommandOnlyMic();
+ });
+ }
+ } else {
+ s_mp3_offline_wake_ok = false;
}
esp_audio_simple_dec_handle_t decoder = nullptr;
@@ -442,8 +529,7 @@
fp = nullptr;
}
vTaskDelay(pdMS_TO_TICKS(30));
- wait_for_sd_dma();
- fp = fopen(path, "rb");
+ fp = fopen_mp3_if_heap_ok(path);
if (!fp) {
ESP_LOGE(TAG, "reopen MP3 failed: %s", path);
break;
@@ -578,6 +664,11 @@
unsigned duration_ms = s_mp3_audible_started
? pdTICKS_TO_MS(xTaskGetTickCount() - s_mp3_audible_started)
: 0;
+ // 鍚獥瑕佸紑 AFE銆傚厛鍏虫帀 MP3 瑙g爜鍣紝閬垮厤鍜屽綍闊虫姠鍐呴儴 SRAM銆�
+ close_mp3_decode_resources(&decoder, &rate_cvt_handle, &fp, &file_buf);
+ audio_info_checked = false;
+ mp3_sample_rate = 0;
+ mp3_channels = 0;
Board_OnShadowingClipFinished(duration_ms);
wait_shadowing_listen(duration_ms);
wait_shadowing_mic_off();
@@ -585,22 +676,7 @@
}
}
- if (rate_cvt_handle) {
- esp_ae_rate_cvt_close(rate_cvt_handle);
- rate_cvt_handle = nullptr;
- }
- if (decoder) {
- esp_audio_simple_dec_close(decoder);
- decoder = nullptr;
- }
- if (fp) {
- fclose(fp);
- fp = nullptr;
- }
- if (file_buf) {
- heap_caps_free(file_buf);
- file_buf = nullptr;
- }
+ close_mp3_decode_resources(&decoder, &rate_cvt_handle, &fp, &file_buf);
ESP_LOGI(TAG, "MP3 item %s: %s", mp3_stop_requested ? "stopped" : "finished", path);
}
@@ -622,10 +698,15 @@
unsigned duration_ms = s_mp3_audible_started
? pdTICKS_TO_MS(xTaskGetTickCount() - s_mp3_audible_started)
: pdTICKS_TO_MS(xTaskGetTickCount() - play_started);
+ if (task_arg) {
+ heap_caps_free(task_arg);
+ task_arg = nullptr;
+ }
mp3_player_task_handle = nullptr;
mp3_stop_requested = false;
mp3_pause_requested = false;
mp3_repeat_left = 0;
+ s_mp3_offline_wake_ok = false;
if (should_power_off) {
Application::GetInstance().RequestPowerOffStandby();
}
@@ -666,6 +747,7 @@
return ESP_ERR_INVALID_ARG;
}
if (count > SDCARD_MP3_PLAYLIST_MAX) {
+ ESP_LOGW(TAG, "playlist truncated incoming=%d max=%d", count, SDCARD_MP3_PLAYLIST_MAX);
count = SDCARD_MP3_PLAYLIST_MAX;
}
@@ -679,7 +761,10 @@
}
mp3_player_task_arg_t *arg = static_cast<mp3_player_task_arg_t *>(
- heap_caps_calloc(1, sizeof(mp3_player_task_arg_t), MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
+ heap_caps_calloc(1,
+ sizeof(mp3_player_task_arg_t) +
+ sizeof(sdcard_mp3_playlist_item_t) * static_cast<size_t>(count),
+ MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT));
if (!arg) {
return ESP_ERR_NO_MEM;
}
@@ -748,6 +833,16 @@
});
}
+extern "C" void SDCard_PreparePlaybackAndWait(void)
+{
+ stop_afe_for_sd_and_wait(true);
+}
+
+extern "C" bool SDCard_Mp3OfflineWakeAllowed(void)
+{
+ return s_mp3_offline_wake_ok;
+}
+
extern "C" bool SDCard_Is_Playing(void)
{
return mp3_player_task_handle != nullptr;
--
Gitblit v1.9.1