wangzhibo
10 天以前 220e4fab27b7f419b3e89e2f2d28ae789aac09d7
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,再开离线口令唤醒。反过来会把 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 解码器,避免和录音抢内部 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;