追踪原因,是由于: sd 管道中的 rsp 元素 发生延迟 导致。
操作过程如下:
唤醒 --- 下一曲 (切换到下一曲并开始播放)--- 唤醒 (暂停sd, 等待命令词)--- 没有命令词发生,定时器超时,恢复sd播放 ,无法恢复播放。
以下是输出信息:
Code: Select all
Build fst from commands.
I (4709) wwe_tone_sd_v04_1: Starting Recording Pipeline
D (4709) AUDIO_PIPELINE: start el[ rec_i2s], linked:1, state:1,[0x3c10d5ac],
I (4711) AUDIO_ELEMENT: [rec_i2s-0x3c10d5ac] Element task created
D (4717) AUDIO_PIPELINE: start el[ rec_filter], linked:1, state:1,[0x3c10d910],
I (4724) AUDIO_ELEMENT: [rec_filter-0x3c10d910] Element task created
D (4730) AUDIO_PIPELINE: start el[ rec_raw], linked:1, state:1,[0x3c10da4c],
D (4738) AUDIO_ELEMENT: REPORT_STATUS,[rec_raw]evt out cmd = 8,status:12
I (4744) AUDIO_ELEMENT: [rec_raw-0x3c10da4c] Element task created
I (4750) AUDIO_PIPELINE: Func:audio_pipeline_run, Line:408, MEM Total:4569792 Bytes, Inter:189275 Bytes, Dram:189275 Bytes, Dram largest free:90100Bytes
D (4764) AUDIO_PIPELINE: resume,linked:1, state:1,[rec_i2s-0x3c10d5ac]
I (4770) AUDIO_ELEMENT: [rec_i2s] AEL_MSG_CMD_RESUME,state:1
D (4775) AUDIO_ELEMENT: [rec_i2s] el opened
D (4779) AUDIO_ELEMENT: REPORT_STATUS,[rec_i2s]evt out cmd = 8,status:12
D (4786) AUDIO_PIPELINE: resume,linked:1, state:1,[rec_filter-0x3c10d910]
I (4792) AUDIO_ELEMENT: [rec_filter] AEL_MSG_CMD_RESUME,state:1
I (4799) RSP_FILTER: sample rate of source data : 48000, channel of source data : 4, sample rate of destination data : 16000, channel of destination data : 4
D (4812) AUDIO_ELEMENT: [rec_filter] el opened
D (4816) AUDIO_ELEMENT: REPORT_STATUS,[rec_filter]evt out cmd = 8,status:12
D (4827) AUDIO_PIPELINE: resume,linked:1, state:3,[rec_raw-0x3c10da4c]
D (4829) AUDIO_ELEMENT: REPORT_STATUS,[rec_raw]evt out cmd = 8,status:12
D (4836) AUDIO_ELEMENT: [rec_raw] RESUME: Element is already running, state:3, task_run:1, is_running:1
I (4844) AUDIO_PIPELINE: Pipeline started
I (4869) wwe_tone_sd_v04_1: Creating voice command timer...
I (4870) wwe_tone_sd_v04_1: [AppMain] g_app_event_queue created successfully.
I (4871) wwe_tone_sd_v04_1: Func:app_main, Line:511, MEM Total:4565280 Bytes, Inter:186907 Bytes, Dram:186907 Bytes, Dram largest free:90100Bytes
I (4884) wwe_tone_sd_v04_1: [AppMain] app_event_handler_task created.
I (4891) wwe_tone_sd_v04_1: Func:app_main, Line:516, MEM Total:4548032 Bytes, Inter:169659 Bytes, Dram:169659 Bytes, Dram largest free:90100Bytes
I (4903) wwe_tone_sd_v04_1: [AppMain] control_logic_task created.
I (4909) wwe_tone_sd_v04_1: System Initialized. Waiting for voice commands...
I (4949) wwe_tone_sd_v04_1: Func:app_main, Line:522, MEM Total:4530784 Bytes, Inter:152411 Bytes, Dram:152411 Bytes, Dram largest free:71668Bytes
I (4954) wwe_tone_sd_v04_1: Starting main event loop...
D (14621) [* REC_CB *]: WAKEUP_START.
D (14621) [* REC_CB *]: Sending App Event 1 (APP_EVENT_VOICE_WAKEUP_START) to g_app_event_queue.
D (14622) [* CTRL_TASK *]: WAKEUP_START.
D (14625) [* REC_CB *]: App Event 1 (APP_EVENT_VOICE_WAKEUP_START) sent successfully.
D (14844) [* REC_CB *]: VAD_START.
D (15962) [* REC_CB *]: VAD_END.
D (16562) [* REC_CB *]: WAKEUP_END.
D (16562) [* REC_CB *]: Sending App Event 2 (APP_EVENT_VOICE_WAKEUP_END) to g_app_event_queue.
D (16563) [* CTRL_TASK *]: WAKEUP_END.
D (16566) [* CTRL_TASK *]: Timer is started.
D (16570) [* REC_CB *]: App Event 2 (APP_EVENT_VOICE_WAKEUP_END) sent successfully.
D (17323) [* REC_CB *]: AUDIO_REC_COMMAND_DECT: command_id 3, phrase_id 3, prob 0.449510, str: xia ju
D (17324) [* REC_CB *]: Sending App Event 3 (APP_EVENT_VOICE_CMD_RECOGNIZED) to g_app_event_queue.
D (17331) [* CTRL_TASK *]: Timer is stopped by COMMAND_DECT.
D (17336) AUDIO_PIPELINE: audio_element_stop
W (17340) AUDIO_PIPELINE: Without stop, st:1
W (17344) AUDIO_PIPELINE: Without wait stop, st:1
D (17348) AUDIO_PIPELINE: Destroy audio_pipeline elements
W (17353) AUDIO_ELEMENT: [sd_file] Element has not create when AUDIO_ELEMENT_TERMINATE
W (17361) AUDIO_ELEMENT: [sd_mp3] Element has not create when AUDIO_ELEMENT_TERMINATE
W (17369) AUDIO_ELEMENT: [sd_filter] Element has not create when AUDIO_ELEMENT_TERMINATE
W (17376) AUDIO_ELEMENT: [sd_i2s] Element has not create when AUDIO_ELEMENT_TERMINATE
D (17384) [* CTRL_TASK *]: URL: file://sdcard/podcast.mp3
D (17389) AUDIO_PIPELINE: start el[ sd_file], linked:1, state:1,[0x3c108754],
D (17397) [* REC_CB *]: App Event 3 (APP_EVENT_VOICE_CMD_RECOGNIZED) sent successfully.
I (17430) AUDIO_ELEMENT: [sd_file-0x3c108754] Element task created
D (17430) AUDIO_PIPELINE: start el[ sd_mp3], linked:1, state:1,[0x3c1088f0],
I (17433) AUDIO_ELEMENT: [sd_mp3-0x3c1088f0] Element task created
D (17438) AUDIO_PIPELINE: start el[ sd_filter], linked:1, state:1,[0x3c108a74],
I (17446) AUDIO_ELEMENT: [sd_filter-0x3c108a74] Element task created
D (17452) AUDIO_PIPELINE: start el[ sd_i2s], linked:1, state:1,[0x3c108d44],
I (17460) AUDIO_ELEMENT: [sd_i2s-0x3c108d44] Element task created
I (17465) AUDIO_PIPELINE: Func:audio_pipeline_run, Line:408, MEM Total:4502776 Bytes, Inter:142895 Bytes, Dram:142895 Bytes, Dram largest free:71668Bytes
D (17479) AUDIO_PIPELINE: resume,linked:1, state:1,[sd_file-0x3c108754]
I (17526) AUDIO_ELEMENT: [sd_file] AEL_MSG_CMD_RESUME,state:1
D (17526) AUDIO_PIPELINE: resume,linked:1, state:1,[sd_mp3-0x3c1088f0]
I (17527) AUDIO_ELEMENT: [sd_mp3] AEL_MSG_CMD_RESUME,state:1
D (17532) AUDIO_PIPELINE: resume,linked:1, state:1,[sd_filter-0x3c108a74]
I (17539) AUDIO_ELEMENT: [sd_filter] AEL_MSG_CMD_RESUME,state:1
D (17544) AUDIO_PIPELINE: resume,linked:1, state:1,[sd_i2s-0x3c108d44]
I (17550) AUDIO_ELEMENT: [sd_i2s] AEL_MSG_CMD_RESUME,state:1
D (17556) AUDIO_ELEMENT: [sd_i2s] el opened
D (17560) AUDIO_ELEMENT: REPORT_STATUS,[sd_i2s]evt out cmd = 8,status:12
I (17566) AUDIO_PIPELINE: Pipeline started
D (17566) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c108d44), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (17570) [* CTRL_TASK *]: pipeline_sd is ran by next-cmd.
D (17592) [* CTRL_TASK *]: Command is done. cur_cmd = SD_CONTROL_NONE.
I (17540) MP3_DECODER: MP3 opened
I (17701) RSP_FILTER: sample rate of source data : 44100, channel of source data : 2, sample rate of destination data : 48000, channel of destination data : 2
D (17705) AUDIO_ELEMENT: [sd_filter] el opened
D (17709) AUDIO_ELEMENT: REPORT_STATUS,[sd_filter]evt out cmd = 8,status:12
D (17715) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_filter' (at 0x3c108a74), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
I (17746) FATFS_STREAM: File size: 25181759 byte, file position: 0
D (17747) AUDIO_ELEMENT: [sd_file] el opened
D (17747) AUDIO_ELEMENT: REPORT_STATUS,[sd_file]evt out cmd = 8,status:12
D (17752) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_file' (at 0x3c108754), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
I (17850) CODEC_ELEMENT_HELPER: The element is 0x3c1088f0. The reserve data 2 is 0x0.
D (17852) AUDIO_ELEMENT: [sd_mp3] el opened
D (17852) AUDIO_ELEMENT: REPORT_STATUS,[sd_mp3]evt out cmd = 8,status:12
D (17858) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_mp3' (at 0x3c1088f0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (17883) AUDIO_ELEMENT: REPORT_INFO,[sd_mp3]evt out cmd:9,
D (17883) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_MUSIC_INFO(9), src_type=131072, src=Element 'sd_mp3' (at 0x3c1088f0), data_len=0, data_ptr=0x0 (MusicInfo)
D (17896) [* AUD_EVT *]: SD DECODER: Music info: sr=44100, bits=16, ch=2
D (36823) [* REC_CB *]: WAKEUP_START.
D (36823) [* REC_CB *]: Sending App Event 1 (APP_EVENT_VOICE_WAKEUP_START) to g_app_event_queue.
D (36824) [* REC_CB *]: App Event 1 (APP_EVENT_VOICE_WAKEUP_START) sent successfully.
D (36824) [* CTRL_TASK *]: WAKEUP_START.
D (36835) AUDIO_PIPELINE: pause [sd_file] 0x3c108754
I (36963) AUDIO_ELEMENT: [sd_file] ON_CMD: Processing AEL_MSG_CMD_PAUSE. Current state: 3
D (36965) AUDIO_ELEMENT: [sd_file] ON_CMD_PAUSE: Calling audio_element_process_deinit (el->close).
D (36969) AUDIO_ELEMENT: [sd_file] ON_CMD_PAUSE: audio_element_process_deinit (el->close) returned.
D (37057) [* REC_CB *]: VAD_START.
D (37062) AUDIO_ELEMENT: REPORT_STATUS,[sd_file]evt out cmd = 8,status:13
D (37078) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_file' (at 0x3c108754), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
I (37150) AUDIO_ELEMENT: [sd_file] AEL_MSG_CMD_PAUSE
D (37151) AUDIO_PIPELINE: pause [sd_mp3] 0x3c1088f0
I (37157) AUDIO_ELEMENT: [sd_mp3] ON_CMD: Processing AEL_MSG_CMD_PAUSE. Current state: 3
D (37157) AUDIO_ELEMENT: [sd_mp3] ON_CMD_PAUSE: Calling audio_element_process_deinit (el->close).
I (37168) MP3_DECODER: Closed
D (37170) AUDIO_ELEMENT: [sd_mp3] ON_CMD_PAUSE: audio_element_process_deinit (el->close) returned.
D (37178) AUDIO_ELEMENT: REPORT_STATUS,[sd_mp3]evt out cmd = 8,status:13
D (37185) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_mp3' (at 0x3c1088f0), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
I (37202) AUDIO_ELEMENT: [sd_mp3] AEL_MSG_CMD_PAUSE
D (37205) AUDIO_PIPELINE: pause [sd_filter] 0x3c108a74
D (37909) [* REC_CB *]: VAD_END.
D (38509) [* REC_CB *]: WAKEUP_END.
D (38509) [* REC_CB *]: Sending App Event 2 (APP_EVENT_VOICE_WAKEUP_END) to g_app_event_queue.
D (38510) [* REC_CB *]: App Event 2 (APP_EVENT_VOICE_WAKEUP_END) sent successfully.
D (39210) AUDIO_PIPELINE: pause [sd_i2s] 0x3c108d44
I (39239) AUDIO_ELEMENT: [sd_i2s] ON_CMD: Processing AEL_MSG_CMD_PAUSE. Current state: 3
D (39239) AUDIO_ELEMENT: [sd_i2s] ON_CMD_PAUSE: Calling audio_element_process_deinit (el->close).
D (39244) AUDIO_ELEMENT: [sd_i2s] ON_CMD_PAUSE: audio_element_process_deinit (el->close) returned.
D (39253) AUDIO_ELEMENT: REPORT_STATUS,[sd_i2s]evt out cmd = 8,status:13
I (39259) AUDIO_ELEMENT: [sd_i2s] AEL_MSG_CMD_PAUSE
D (39259) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c108d44), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
D (39264) [* CTRL_TASK *]: SD_CONTROL_PLAY is pending by tone-1.
D (39286) [* CTRL_TASK *]: WAKEUP_END.
D (39290) [* CTRL_TASK *]: Timer is started.
I (43284) RECORDER_SR: MN dect quit
D (44290) [* TMR_CB *]: LATE_CMD_TIMEOUT event sent to queue.
D (44290) [* CTRL_TASK *]: LATE_CMD_TIMEOUT. isToneCmd: 0, isTonePending: 1
D (44291) [* CTRL_TASK *]: Timer expired. el_state is: 4.
D (44296) AUDIO_PIPELINE: resume,linked:1, state:4,[sd_file-0x3c108754]
I (44338) AUDIO_ELEMENT: [sd_file] AEL_MSG_CMD_RESUME,state:4
D (44338) AUDIO_PIPELINE: resume,linked:1, state:4,[sd_mp3-0x3c1088f0]
I (44339) AUDIO_ELEMENT: [sd_mp3] AEL_MSG_CMD_RESUME,state:4
D (44344) AUDIO_PIPELINE: resume,linked:1, state:3,[sd_filter-0x3c108a74]
D (44351) AUDIO_ELEMENT: REPORT_STATUS,[sd_filter]evt out cmd = 8,status:12
D (44357) AUDIO_ELEMENT: [sd_filter] RESUME: Element is already running, state:3, task_run:1, is_running:1
D (44357) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_filter' (at 0x3c108a74), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (44367) AUDIO_PIPELINE: resume,linked:1, state:4,[sd_i2s-0x3c108d44]
I (44390) AUDIO_ELEMENT: [sd_i2s] AEL_MSG_CMD_RESUME,state:4
D (44395) AUDIO_ELEMENT: [sd_i2s] el opened
D (44399) AUDIO_ELEMENT: REPORT_STATUS,[sd_i2s]evt out cmd = 8,status:12
D (44406) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c108d44), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (44406) [* CTRL_TASK *]: Timer expired. SD-PLAYING is resumed.
I (44367) MP3_DECODER: MP3 opened
I (44441) FATFS_STREAM: File size: 25181759 byte, file position: 466944
D (44442) AUDIO_ELEMENT: [sd_file] el opened
D (44443) AUDIO_ELEMENT: REPORT_STATUS,[sd_file]evt out cmd = 8,status:12
D (44481) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_file' (at 0x3c108754), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (44498) AUDIO_ELEMENT: REPORT_INFO,[sd_mp3]evt out cmd:9,
D (44498) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_MUSIC_INFO(9), src_type=131072, src=Element 'sd_mp3' (at 0x3c1088f0), data_len=0, data_ptr=0x0 (MusicInfo)
D (44511) [* AUD_EVT *]: SD DECODER: Music info: sr=44100, bits=16, ch=2
D (44519) AUDIO_ELEMENT: [sd_mp3] el opened
D (44525) AUDIO_ELEMENT: REPORT_STATUS,[sd_mp3]evt out cmd = 8,status:12
D (44528) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_mp3' (at 0x3c1088f0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
I (44554) AUDIO_ELEMENT: [sd_filter] ON_CMD: Processing AEL_MSG_CMD_PAUSE. Current state: 3
D (44554) AUDIO_ELEMENT: [sd_filter] ON_CMD_PAUSE: Calling audio_element_process_deinit (el->close).
D (44564) AUDIO_ELEMENT: [sd_filter] ON_CMD_PAUSE: audio_element_process_deinit (el->close) returned.
D (44572) AUDIO_ELEMENT: REPORT_STATUS,[sd_filter]evt out cmd = 8,status:13
D (44578) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'sd_filter' (at 0x3c108a74), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
I (44596) AUDIO_ELEMENT: [sd_filter] AEL_MSG_CMD_PAUSE
Code: Select all
/**/
#include <stdio.h>
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/timers.h"
#include "freertos/queue.h"
#include "esp_log.h"
#include "esp_err.h"
#include "audio_element.h"
#include "audio_idf_version.h"
#include "audio_mem.h"
#include "audio_pipeline.h"
#include "audio_recorder.h"
#include "audio_thread.h"
#include "audio_error.h"
//#include "audio_tone_uri.h"
#include "board.h"
#include "i2s_stream.h"
#include "mp3_decoder.h"
#include "filter_resample.h"
#include "raw_stream.h"
#include "recorder_sr.h"
//#include "tone_stream.h"
#include "es7210.h"
#include "model_path.h"
#include "esp_peripherals.h"
#include "audio_event_iface.h"
#include "periph_sdcard.h"
#include "sdcard_list.h"
#include "sdcard_scan.h"
#include "fatfs_stream.h"
static char *TAG = "wwe_tone_sd_v04_1"; // 设置为 信息模式(ESP_LOG_INFO),与 调试模式 分离
// 设置为 调试模式(ESP_LOG_DEBUG):白色,容易观察
static char *TAG_REC_CB = "[* REC_CB *]"; // set:info, use:dbg
static char *TAG_TMR_CB = "[* TMR_CB *]"; // set:info, use:dbg
static char *TAG_AUD_EVT = "[* AUD_EVT *]"; // set:info, use:dbg
static char *TAG_CTRL_TASK = "[* CTRL_TASK *]"; // set:dbg, use:dbg
#define DEBUG_AUD_EVT 1 // 调试开关: 输出:音频管道 中的所有事件
// SD Card Playback Pipeline
audio_pipeline_handle_t pipeline_sd = NULL;
audio_element_handle_t sd_stream_reader = NULL, sd_mp3_decoder = NULL, filter_sd = NULL, sd_stream_writer = NULL;
playlist_operator_handle_t sdcard_list_handle = NULL; // To store scanned MP3 files
// Recording Pipeline
audio_pipeline_handle_t pipeline_rec = NULL;
audio_element_handle_t rec_stream_reader = NULL, filter_rec = NULL, raw_read = NULL;
audio_rec_handle_t recorder = NULL;
audio_event_iface_handle_t evt = NULL; // Unified event listener
// ----------------------------------------
// 简化命令词
//#define SPEECH_COMMANDS_V01 ("PLd;PeZ;PRmVmcS;NfKST;VnLYoM cP;VnLYoM DtN;") // 播放;暂停;上一曲;下一曲;大声点;小声点
//#define SPEECH_COMMANDS_V01 ("play;pause;previous;next;volume up;volume down;") // 播放;暂停;上一曲;下一曲;大声点;小声点
#define SPEECH_COMMANDS_V01 ("bo fang;zan ting;shang ju;xia ju;da sheng dian;xiao sheng dian;") // 播放;暂停;上一曲;下一曲;大声点;小声点
#define MAX_SINGLE_COMMAND_PHRASE_LENGTH 32
#define VOLUME_STEP 10
// 命令词ID (与SPEECH_COMMANDS_V01中的顺序对应)
#define CMD_ID_PLAY 0
#define CMD_ID_PAUSE 1
#define CMD_ID_PREV 2
#define CMD_ID_NEXT 3
#define CMD_ID_VOLUP 4
#define CMD_ID_VOLDN 5
typedef enum {
SD_CONTROL_NONE = -1,
SD_CONTROL_PLAY,
SD_CONTROL_PAUSE,
SD_CONTROL_PREV,
SD_CONTROL_NEXT,
SD_CONTROL_VOLUP,
SD_CONTROL_VOLDN
} sd_state_e;
// Timer for late command
static TimerHandle_t timer_voice_cmd = NULL;
const uint32_t TIMEOUT_MS_CMD = 5000; // 必须覆盖:命令词识别 窗口期,否则会造成:提示音2 与 SD恢复后的冲突。
// ----------------------------------------
// --- Application Event System ---
typedef enum {
APP_EVENT_NONE = 0,
APP_EVENT_VOICE_WAKEUP_START,
APP_EVENT_VOICE_WAKEUP_END,
APP_EVENT_VOICE_CMD_RECOGNIZED,
//APP_EVENT_TONE_PLAYBACK_FINISHED,
APP_EVENT_SD_PLAYBACK_FINISHED,
APP_EVENT_LATE_CMD_TIMEOUT
} app_event_type_t;
typedef struct {
app_event_type_t type;
int phrase_id;
} app_event_t;
#define APP_EVENT_QUEUE_LENGTH 64
static QueueHandle_t g_app_event_queue = NULL;
// --- end of event ---------
static void app_event_handler_task(void *pvParameters); // 音频事件监听 及 消息发送
static void control_logic_task(void *pvParameters); // 中控逻辑处理
static const char* app_event_type_to_string(app_event_type_t type);
// Task handles
static TaskHandle_t g_app_event_handler_task_handle = NULL;
static TaskHandle_t g_control_logic_task_handle = NULL;
static esp_err_t rec_engine_cb(audio_rec_evt_t *event, void *user_data)
{
app_event_t app_evt;
memset(&app_evt, 0, sizeof(app_event_t)); // 每次调用:回调函数 时都会 初始化 结构体变量:app_evt
if (AUDIO_REC_WAKEUP_START == event->type) {
ESP_LOGD(TAG_REC_CB, "WAKEUP_START.");
app_evt.type = APP_EVENT_VOICE_WAKEUP_START;
}
else if (AUDIO_REC_VAD_START == event->type) {
ESP_LOGD(TAG_REC_CB, "VAD_START.");
}
else if (AUDIO_REC_VAD_END == event->type) {
ESP_LOGD(TAG_REC_CB, "VAD_END.");
}
else if (AUDIO_REC_WAKEUP_END == event->type) {
ESP_LOGD(TAG_REC_CB, "WAKEUP_END.");
app_evt.type = APP_EVENT_VOICE_WAKEUP_END;
}
else if (AUDIO_REC_COMMAND_DECT <= event->type) {
recorder_sr_mn_result_t *mn_result = event->event_data;
ESP_LOGD(TAG_REC_CB, "AUDIO_REC_COMMAND_DECT: command_id %d, phrase_id %d, prob %f, str: %s",
event->type, mn_result->phrase_id, mn_result->prob, mn_result->str);
app_evt.type = APP_EVENT_VOICE_CMD_RECOGNIZED;
app_evt.phrase_id = mn_result->phrase_id;
}
else {
ESP_LOGE(TAG_REC_CB, "Unknown recorder event: %d", event->type);
}
if (app_evt.type != APP_EVENT_NONE) {
if (g_app_event_queue) {
ESP_LOGD(TAG_REC_CB, "Sending App Event %d (%s) to g_app_event_queue.", app_evt.type, app_event_type_to_string(app_evt.type));
if (xQueueSend(g_app_event_queue, &app_evt, pdMS_TO_TICKS(50)) != pdPASS) {
ESP_LOGE(TAG_REC_CB, "Failed to send App Event %d to g_app_event_queue!", app_evt.type);
} else {
ESP_LOGD(TAG_REC_CB, "App Event %d (%s) sent successfully.", app_evt.type, app_event_type_to_string(app_evt.type));
}
} else {
ESP_LOGE(TAG_REC_CB, "g_app_event_queue is NULL! Cannot send event %d.", app_evt.type);
}
}
return ESP_OK;
}
// =======================================================================================
// Timer callback function
// =======================================================================================
static void timer_cb(TimerHandle_t xTimer) {
app_event_t evt;
memset(&evt, 0, sizeof(app_event_t)); // Good practice to zero out
evt.type = APP_EVENT_LATE_CMD_TIMEOUT;
if (g_app_event_queue) {
if (xQueueSend(g_app_event_queue, &evt, 0) != pdPASS) {
ESP_LOGE(TAG_TMR_CB, "Failed to send LATE_CMD_TIMEOUT event to queue!");
} else {
ESP_LOGD(TAG_TMR_CB, "LATE_CMD_TIMEOUT event sent to queue.");
}
} else {
ESP_LOGE(TAG_TMR_CB, "g_app_event_queue is NULL, cannot send LATE_CMD_TIMEOUT.");
}
}
static int input_cb_for_afe(int16_t *buffer, int buf_sz, void *user_ctx, TickType_t ticks)
{
return raw_stream_read(raw_read, (char *)buffer, buf_sz);
}
void sdcard_url_save_cb(void *user_data, char *url)
{
playlist_operator_handle_t pl_handle = (playlist_operator_handle_t)user_data;
esp_err_t ret = sdcard_list_save(pl_handle, url);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Failed to save sdcard url to playlist: %s", url);
} else {
ESP_LOGI(TAG, "Saved URL to playlist: %s", url);
}
}
// Helper to convert app_event_type_t to string for logging
static const char* app_event_type_to_string(app_event_type_t type) {
switch (type) {
case APP_EVENT_NONE: return "APP_EVENT_NONE";
case APP_EVENT_VOICE_WAKEUP_START: return "APP_EVENT_VOICE_WAKEUP_START";
case APP_EVENT_VOICE_WAKEUP_END: return "APP_EVENT_VOICE_WAKEUP_END";
case APP_EVENT_VOICE_CMD_RECOGNIZED: return "APP_EVENT_VOICE_CMD_RECOGNIZED";
//case APP_EVENT_TONE_PLAYBACK_FINISHED: return "APP_EVENT_TONE_PLAYBACK_FINISHED";
case APP_EVENT_SD_PLAYBACK_FINISHED: return "APP_EVENT_SD_PLAYBACK_FINISHED";
default: return "UNKNOWN_APP_EVENT";
}
}
// 1:H --- ... --- 6:L
// none, err, wrn, info, dbg, vbe
static void log_clear(void)
{
esp_log_level_set("*", ESP_LOG_INFO);
esp_log_level_set("AUDIO_THREAD", ESP_LOG_ERROR);
esp_log_level_set("AUDIO_MEM", ESP_LOG_DEBUG); // Show mem usage
// Add other noisy tags if needed
esp_log_level_set("AUDIO_ELEMENT", ESP_LOG_DEBUG);
esp_log_level_set("AUDIO_PIPELINE", ESP_LOG_DEBUG);
esp_log_level_set("MP3_DECODER", ESP_LOG_DEBUG);
esp_log_level_set("I2S_STREAM", ESP_LOG_DEBUG);
esp_log_level_set("RSP_FILTER", ESP_LOG_DEBUG);
esp_log_level_set(TAG, ESP_LOG_INFO); // Set our tag to DEBUG for more info
esp_log_level_set(TAG_REC_CB, ESP_LOG_DEBUG); // set:info, use:dbg
esp_log_level_set(TAG_TMR_CB, ESP_LOG_DEBUG); // set:info, use:dbg
esp_log_level_set(TAG_AUD_EVT, ESP_LOG_DEBUG); // set:info, use:dbg
esp_log_level_set(TAG_CTRL_TASK, ESP_LOG_DEBUG); // set:info, use:dbg
}
// for debug ----------------
#if (DEBUG_AUD_EVT)
// 辅助函数:将 audio_element_msg_cmd_t 转换为字符串
const char *get_ael_msg_cmd_str(audio_element_msg_cmd_t cmd) {
switch (cmd) {
case AEL_MSG_CMD_NONE: return "AEL_MSG_CMD_NONE";
case AEL_MSG_CMD_FINISH: return "AEL_MSG_CMD_FINISH";
case AEL_MSG_CMD_STOP: return "AEL_MSG_CMD_STOP";
case AEL_MSG_CMD_PAUSE: return "AEL_MSG_CMD_PAUSE";
case AEL_MSG_CMD_RESUME: return "AEL_MSG_CMD_RESUME";
case AEL_MSG_CMD_DESTROY: return "AEL_MSG_CMD_DESTROY";
case AEL_MSG_CMD_REPORT_STATUS: return "AEL_MSG_CMD_REPORT_STATUS";
case AEL_MSG_CMD_REPORT_MUSIC_INFO: return "AEL_MSG_CMD_REPORT_MUSIC_INFO";
case AEL_MSG_CMD_REPORT_CODEC_FMT: return "AEL_MSG_CMD_REPORT_CODEC_FMT";
case AEL_MSG_CMD_REPORT_POSITION: return "AEL_MSG_CMD_REPORT_POSITION";
default: return "UNKNOWN_CMD";
}
}
// 辅助函数:将 audio_element_status_t 转换为字符串
const char *get_ael_status_str(audio_element_status_t status) {
switch (status) {
case AEL_STATUS_NONE: return "AEL_STATUS_NONE";
case AEL_STATUS_ERROR_OPEN: return "AEL_STATUS_ERROR_OPEN";
case AEL_STATUS_ERROR_INPUT: return "AEL_STATUS_ERROR_INPUT";
case AEL_STATUS_ERROR_PROCESS: return "AEL_STATUS_ERROR_PROCESS";
case AEL_STATUS_ERROR_OUTPUT: return "AEL_STATUS_ERROR_OUTPUT";
case AEL_STATUS_ERROR_CLOSE: return "AEL_STATUS_ERROR_CLOSE";
case AEL_STATUS_ERROR_TIMEOUT: return "AEL_STATUS_ERROR_TIMEOUT";
case AEL_STATUS_ERROR_UNKNOWN: return "AEL_STATUS_ERROR_UNKNOWN";
case AEL_STATUS_INPUT_DONE: return "AEL_STATUS_INPUT_DONE";
case AEL_STATUS_INPUT_BUFFERING: return "AEL_STATUS_INPUT_BUFFERING";
case AEL_STATUS_OUTPUT_DONE: return "AEL_STATUS_OUTPUT_DONE";
case AEL_STATUS_OUTPUT_BUFFERING: return "AEL_STATUS_OUTPUT_BUFFERING";
case AEL_STATUS_STATE_RUNNING: return "AEL_STATUS_STATE_RUNNING";
case AEL_STATUS_STATE_PAUSED: return "AEL_STATUS_STATE_PAUSED";
case AEL_STATUS_STATE_STOPPED: return "AEL_STATUS_STATE_STOPPED";
case AEL_STATUS_STATE_FINISHED: return "AEL_STATUS_STATE_FINISHED";
case AEL_STATUS_MOUNTED: return "AEL_STATUS_MOUNTED";
case AEL_STATUS_UNMOUNTED: return "AEL_STATUS_UNMOUNTED";
default: return "UNKNOWN_STATUS";
}
}
// 辅助函数:获取 msg.source 的描述性字符串, 需要传入已知的 element handle 以便比较
const char *get_source_description(void *source_ptr,
audio_element_handle_t known_sd_stream_writer)
{
static char description[128];
if (source_ptr == NULL) {
snprintf(description, sizeof(description), "NULL");
} else if (source_ptr == (void *)known_sd_stream_writer) { // 新增对 sd_stream_writer 的判断
snprintf(description, sizeof(description), "sd_stream_writer (at %p)", source_ptr);
}
else {
char *tag = audio_element_get_tag((audio_element_handle_t)source_ptr);
if (tag && strlen(tag) > 0) {
snprintf(description, sizeof(description), "Element '%s' (at %p)", tag, source_ptr);
} else {
snprintf(description, sizeof(description), "Unknown Source (at %p)", source_ptr);
}
}
return description;
}
#endif
// end of debug --------------
void app_main(void)
{
#if 1
log_clear();
ESP_LOGI(TAG, "Starting wwe_tone_sd_v01 application");
audio_board_handle_t board_handle = audio_board_init();
audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);
audio_hal_set_volume(board_handle->audio_hal, 70); // Set a moderate volume
// -------- Initialize Peripherals for SD card --------
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
audio_board_sdcard_init(set, SD_MODE_1_LINE); // Initialize SD card peripheral
// Create playlist and scan for MP3 files
sdcard_list_create(&sdcard_list_handle);
// Scan only for "mp3" extension in the root directory "/sdcard"
sdcard_scan(sdcard_url_save_cb, "/sdcard", 0, (const char *[]){"mp3"}, 1, sdcard_list_handle);
ESP_LOGI(TAG, "SD card scan complete. Playlist content:");
sdcard_list_show(sdcard_list_handle); // Log the found files
// -------- Unified Event Listener --------
audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG(); // 关键-1:大量音频事件 会造成拥堵
evt_cfg.internal_queue_size = 15; // 或更大
evt_cfg.external_queue_size = 15; // 或更大
evt_cfg.queue_set_size = 30; // 至少是 internal + external 之和
evt = audio_event_iface_init(&evt_cfg);
AUDIO_NULL_CHECK(TAG, evt, return);
// -------- SD Card Playback Pipeline (pipeline_sd) --------
ESP_LOGI(TAG, "Initializing SD Card Playback Pipeline (pipeline_sd)");
audio_pipeline_cfg_t pipeline_sd_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline_sd = audio_pipeline_init(&pipeline_sd_cfg);
AUDIO_NULL_CHECK(TAG, pipeline_sd, return);
fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
fatfs_cfg.type = AUDIO_STREAM_READER;
sd_stream_reader = fatfs_stream_init(&fatfs_cfg);
AUDIO_NULL_CHECK(TAG, sd_stream_reader, return);
// URI will be set before playing
mp3_decoder_cfg_t mp3_sd_cfg = DEFAULT_MP3_DECODER_CONFIG();
mp3_sd_cfg.task_stack = 10*1024; // test
mp3_sd_cfg.out_rb_size = 6*1024;
sd_mp3_decoder = mp3_decoder_init(&mp3_sd_cfg);
AUDIO_NULL_CHECK(TAG, sd_mp3_decoder, return);
rsp_filter_cfg_t rsp_sd_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
// Source info will be set dynamically from MP3 decoder
//rsp_sd_cfg.task_prio = 10;//test
rsp_sd_cfg.dest_rate = 48000;
rsp_sd_cfg.dest_ch = 2;
rsp_sd_cfg.dest_bits = 16;
rsp_sd_cfg.mode = RESAMPLE_DECODE_MODE;
rsp_sd_cfg.type = ESP_RESAMPLE_TYPE_AUTO;
filter_sd = rsp_filter_init(&rsp_sd_cfg);
AUDIO_NULL_CHECK(TAG, filter_sd, return);
i2s_stream_cfg_t i2s_sd_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(I2S_NUM_0, 48000, 32, AUDIO_STREAM_WRITER);
i2s_sd_cfg.expand_src_bits = 16; // Resampler outputs 16-bit, I2S DAC might need 32-bit samples (actual data 16-bit)
i2s_sd_cfg.need_expand = true;
sd_stream_writer = i2s_stream_init(&i2s_sd_cfg);
AUDIO_NULL_CHECK(TAG, sd_stream_writer, return);
audio_pipeline_register(pipeline_sd, sd_stream_reader, "sd_file");
audio_pipeline_register(pipeline_sd, sd_mp3_decoder, "sd_mp3");
audio_pipeline_register(pipeline_sd, filter_sd, "sd_filter");
audio_pipeline_register(pipeline_sd, sd_stream_writer, "sd_i2s");
const char *link_sd_tags[] = {"sd_file", "sd_mp3", "sd_filter", "sd_i2s"};
audio_pipeline_link(pipeline_sd, &link_sd_tags[0], 4);
audio_pipeline_set_listener(pipeline_sd, evt); // Use unified event listener
// -------- Recording Pipeline (pipeline_rec) --------
ESP_LOGI(TAG, "Initializing Recording Pipeline (pipeline_rec)");
audio_pipeline_cfg_t pipeline_rec_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline_rec = audio_pipeline_init(&pipeline_rec_cfg);
AUDIO_NULL_CHECK(TAG, pipeline_rec, return);
i2s_stream_cfg_t i2s_rec_cfg = I2S_STREAM_CFG_DEFAULT_WITH_PARA(I2S_NUM_0, 48000, 32, AUDIO_STREAM_READER);
rec_stream_reader = i2s_stream_init(&i2s_rec_cfg);
AUDIO_NULL_CHECK(TAG, rec_stream_reader, return);
rsp_filter_cfg_t rsp_rec_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
rsp_rec_cfg.src_rate = 48000;
rsp_rec_cfg.dest_rate = 16000;
rsp_rec_cfg.mode = RESAMPLE_UNCROSS_MODE;
rsp_rec_cfg.src_ch = 4;
rsp_rec_cfg.dest_ch = 4;
rsp_rec_cfg.max_indata_bytes = 1024;
filter_rec = rsp_filter_init(&rsp_rec_cfg);
AUDIO_NULL_CHECK(TAG, filter_rec, return);
raw_stream_cfg_t raw_cfg = RAW_STREAM_CFG_DEFAULT();
raw_cfg.type = AUDIO_STREAM_READER;
raw_read = raw_stream_init(&raw_cfg);
AUDIO_NULL_CHECK(TAG, raw_read, return);
audio_pipeline_register(pipeline_rec, rec_stream_reader, "rec_i2s");
audio_pipeline_register(pipeline_rec, filter_rec, "rec_filter");
audio_pipeline_register(pipeline_rec, raw_read, "rec_raw");
const char *link_rec_tags[] = {"rec_i2s", "rec_filter", "rec_raw"};
audio_pipeline_link(pipeline_rec, &link_rec_tags[0], 3);
// No listener for rec pipeline, events come via recorder_sr callback
// -------- Speech Recognition Engine --------
ESP_LOGI(TAG, "Initializing Speech Recognition Engine");
recorder_sr_cfg_t recorder_sr_cfg = DEFAULT_RECORDER_SR_CFG(
AUDIO_ADC_INPUT_CH_FORMAT,
"model",
AFE_TYPE_SR,
AFE_MODE_HIGH_PERF//AFE_MODE_LOW_COST
);
recorder_sr_cfg.afe_cfg->memory_alloc_mode = AFE_MEMORY_ALLOC_MORE_PSRAM;
recorder_sr_cfg.afe_cfg->wakenet_init = true;
recorder_sr_cfg.afe_cfg->vad_mode = VAD_MODE_0; // Normal mode
recorder_sr_cfg.multinet_init = true;
recorder_sr_cfg.mn_language = ESP_MN_CHINESE;
recorder_sr_cfg.afe_cfg->aec_init = true;
recorder_sr_cfg.afe_cfg->agc_mode = AFE_MN_PEAK_AGC_MODE_3;
audio_rec_cfg_t rec_create_cfg = AUDIO_RECORDER_DEFAULT_CFG();
rec_create_cfg.read = (recorder_data_read_t)&input_cb_for_afe;
rec_create_cfg.sr_handle = recorder_sr_create(&recorder_sr_cfg, &rec_create_cfg.sr_iface);
AUDIO_NULL_CHECK(TAG, rec_create_cfg.sr_handle, return);
char err_msg[MAX_SINGLE_COMMAND_PHRASE_LENGTH];
esp_err_t ret_sr_cmd = recorder_sr_reset_speech_cmd(rec_create_cfg.sr_handle, SPEECH_COMMANDS_V01, err_msg);
if (ret_sr_cmd != ESP_OK) {
ESP_LOGE(TAG, "Failed to set speech commands: %s", err_msg);
}
rec_create_cfg.event_cb = rec_engine_cb;
rec_create_cfg.wakeup_time = 2000; // 唤醒维持时间(唤醒~VAD触发之前)
rec_create_cfg.wakeup_end = 600; // vad_off 之后的静默时间
rec_create_cfg.vad_start = 160; // 有效的 连续活动语音 判断时长
rec_create_cfg.vad_off = 300; // 静默超时时间
recorder = audio_recorder_create(&rec_create_cfg);
AUDIO_NULL_CHECK(TAG, recorder, return);
// -------- Start Recording Pipeline --------
ESP_LOGI(TAG, "Starting Recording Pipeline");
audio_pipeline_run(pipeline_rec); // Start capturing audio for SR
// -------- Timer for voice command ------------
ESP_LOGI(TAG, "Creating voice command timer...");
timer_voice_cmd = xTimerCreate("VoiceCmdTmr",
pdMS_TO_TICKS(TIMEOUT_MS_CMD),
pdFALSE, // One-shot timer
(void *)0,
timer_cb);
if (timer_voice_cmd == NULL) {
ESP_LOGE(TAG, "FATAL: Failed to create timer_voice_cmd! Halting.");
return;
}
g_app_event_queue = xQueueCreate(APP_EVENT_QUEUE_LENGTH, sizeof(app_event_t));
if (g_app_event_queue == NULL) { ESP_LOGE(TAG, "[AppMain] FATAL: Failed to create g_app_event_queue! Halting."); return; }
ESP_LOGI(TAG, "[AppMain] g_app_event_queue created successfully.");
BaseType_t task_ret; // 关键-2:任务堆栈需要足够大,以容纳音频事件的积累,但需要测试:10KB 是否合适。消息生产者,优先级 小于 消息消费者,但必须比其他音频管道事件高。
AUDIO_MEM_SHOW(TAG);
task_ret = xTaskCreate(app_event_handler_task, "app_evt_hdlr", 1024*16, NULL, configMAX_PRIORITIES - 11, &g_app_event_handler_task_handle);
if (task_ret != pdPASS) { ESP_LOGE(TAG, "[AppMain] Failed to create app_event_handler_task!"); return; }
ESP_LOGI(TAG, "[AppMain] app_event_handler_task created.");
AUDIO_MEM_SHOW(TAG);
task_ret = xTaskCreate(control_logic_task, "ctrl_logic", 1024*16, NULL, configMAX_PRIORITIES - 10, &g_control_logic_task_handle);
if (task_ret != pdPASS) { ESP_LOGE(TAG, "[AppMain] Failed to create control_logic_task!"); return; }
ESP_LOGI(TAG, "[AppMain] control_logic_task created.");
ESP_LOGI(TAG, "System Initialized. Waiting for voice commands...");
AUDIO_MEM_SHOW(TAG);
// -------- Main Event Loop --------
ESP_LOGI(TAG, "Starting main event loop...");
while (1) {
vTaskDelay(pdMS_TO_TICKS(30000));
} // End while(1)
// Cleanup (though this loop is infinite in this example)
ESP_LOGI(TAG, "Stopping pipelines and deinitializing...");
// ... (add deinit code if you plan to exit app_main)
#endif
}
static void app_event_handler_task(void *pvParameters)
{
if (!g_app_event_queue || !evt) {
ESP_LOGE(TAG_AUD_EVT, "Critical component (app_queue or shared_listener) is NULL! Exiting.");
g_app_event_handler_task_handle = NULL;
vTaskDelete(NULL);
return;
}
audio_event_iface_msg_t msg;
while (1) {
esp_err_t ret_listen = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
if (ret_listen != ESP_OK) {
ESP_LOGE(TAG_AUD_EVT, "audio_evt_iface_listener error: %d", ret_listen);
//vTaskDelay(pdMS_TO_TICKS(100));
continue;
}
//AUDIO_MEM_SHOW(TAG); // test
// for debug ----------
#if (DEBUG_AUD_EVT)
const char *cmd_str = get_ael_msg_cmd_str(msg.cmd);
const char *source_str = get_source_description(msg.source, sd_stream_writer); // 传入已知的 tone_stream_writer 句柄
// 根据 msg.cmd 决定如何解释 msg.data
if (msg.cmd == AEL_MSG_CMD_REPORT_STATUS) {
const char *status_str = get_ael_status_str((audio_element_status_t)(int)msg.data);
ESP_LOGD(TAG_AUD_EVT, "audio_evt_iface_listener: cmd=%s(%d), src_type=%d, src=%s, data_len=%d, status_data=%s(%d)",
cmd_str, msg.cmd, msg.source_type, source_str, msg.data_len, status_str, (int)msg.data);
} else if (msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
// 如果是音乐信息,msg.data 指向 audio_element_info_t
// 你可以进一步解析并打印,但这里只做通用处理
// 注意:如果 msg.need_free_data 为 true,则 msg.data 指向的内存需要被释放
// audio_element_info_t* info = (audio_element_info_t*)msg.data;
ESP_LOGD(TAG_AUD_EVT, "audio_evt_iface_listener: cmd=%s(%d), src_type=%d, src=%s, data_len=%d, data_ptr=%p (MusicInfo)",
cmd_str, msg.cmd, msg.source_type, source_str, msg.data_len, msg.data);
} else {
// 其他命令的通用日志
ESP_LOGD(TAG_AUD_EVT, "audio_evt_iface_listener: cmd=%s(%d), src_type=%d, src=%s, data_len=%d, data_ptr=%p",
cmd_str, msg.cmd, msg.source_type, source_str, msg.data_len, msg.data);
}
#endif
// end of debug -------
app_event_t app_evt_to_send;
memset(&app_evt_to_send, 0, sizeof(app_event_t));
bool should_send_app_event = false;
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT) {
if (msg.cmd == AEL_MSG_CMD_REPORT_STATUS){
if (msg.source == (void *)sd_stream_writer) {
if ((int)msg.data == AEL_STATUS_STATE_FINISHED) {
ESP_LOGD(TAG_AUD_EVT, "sd stream writer FINISHED.");
app_evt_to_send.type = APP_EVENT_SD_PLAYBACK_FINISHED;
should_send_app_event = true;
}
}
}
else if (msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
if (msg.source == (void *)sd_mp3_decoder) {
// 直接处理,不再发送消息:(1) 仅仅使用json,(2)尽可能快地设置:filter_sd
audio_element_info_t music_info = {0};
audio_element_getinfo(sd_mp3_decoder, &music_info);
ESP_LOGD(TAG_AUD_EVT, "SD DECODER: Music info: sr=%d, bits=%d, ch=%d",
music_info.sample_rates, music_info.bits, music_info.channels);
if (filter_sd) { // filter_sd should be valid if pipeline_sd is up
esp_err_t chg_ret = rsp_filter_change_src_info(filter_sd,
music_info.sample_rates,
music_info.channels,
music_info.bits);
if (chg_ret != ESP_OK) {
ESP_LOGE(TAG_AUD_EVT, "Failed to change sd-resampler src info.");
}
}
}
}
}
if (should_send_app_event) { // pdMS_TO_TICKS(100)
if (xQueueSend(g_app_event_queue, &app_evt_to_send, 0) != pdPASS) {
ESP_LOGE(TAG_AUD_EVT, "[AppEvtHdlrTask] Failed to send App Event %d to g_app_event_queue!", app_evt_to_send.type);
} else {
ESP_LOGD(TAG_AUD_EVT, "[AppEvtHdlrTask] App Event %d (%s) sent successfully.",
app_evt_to_send.type, app_event_type_to_string(app_evt_to_send.type));
}
}
}
}
static void control_logic_task(void *pvParameters)
{
app_event_t app_event;
bool isTurnStart = false; // 一轮语音交互 是否 已经完成
bool isTonePending = false; // 提示音1 播放,挂起 正在播放的 SD
bool isToneCmd = false; // 提示音2 播放, 也表明:命令已经识别
sd_state_e sd_state = SD_CONTROL_NONE; // SD状态 记录
//sd_state_e cur_cmd = SD_CONTROL_NONE; // 当前命令 记录
while(1) {
if (g_app_event_queue && xQueueReceive(g_app_event_queue, &app_event, portMAX_DELAY) == pdPASS) {
//AUDIO_MEM_SHOW(TAG); // test
switch (app_event.type) {
case APP_EVENT_VOICE_WAKEUP_START: {
ESP_LOGD(TAG_CTRL_TASK, "WAKEUP_START.");
isTurnStart = true; // 新的一轮语音交互开始
// SD已经被挂起,重置定时器以防止:提示音1 与 SD恢复 同时发生
if (isTonePending) {
if (xTimerIsTimerActive(timer_voice_cmd)) {
if (xTimerReset(timer_voice_cmd, 50 ) != pdPASS ) {
ESP_LOGE(TAG_CTRL_TASK, "Failed to restart timer! ");
} else ESP_LOGD(TAG_CTRL_TASK, "Timer is restarted.");
}
} else { // 挂起条件: (1)正在播放,(2)命令词未触发,(3)此前没被挂起 。 需要防止:多次、连续 唤醒
if (!isToneCmd && sd_state == SD_CONTROL_PLAY) {
isTonePending = true;
audio_element_state_t el_state = audio_element_get_state(sd_stream_writer);
if (el_state == AEL_STATE_RUNNING) {
audio_pipeline_pause(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "SD_CONTROL_PLAY is pending by tone-1.");
}
else {
ESP_LOGD(TAG_CTRL_TASK, "In wakeup pending, el_state is: %d.", el_state);
}
}
}
break;
}
case APP_EVENT_VOICE_WAKEUP_END: {
ESP_LOGD(TAG_CTRL_TASK, "WAKEUP_END.");
// 开启定时器条件:此前没有命令被识别,同时,一轮完整的语音交互已经开始。如果定时器已经开启,则说明被连续唤醒,需要:重新计时
if (!isToneCmd && isTurnStart){
if (xTimerIsTimerActive(timer_voice_cmd)) {
if (xTimerReset(timer_voice_cmd, 50 ) != pdPASS ) {
ESP_LOGE(TAG_CTRL_TASK, "Failed to restart timer! ");
} else ESP_LOGD(TAG_CTRL_TASK, "Timer is restarted.");
} else {
if (xTimerStart(timer_voice_cmd, 0) != pdPASS) {
ESP_LOGE(TAG_CTRL_TASK, "Failed to start timer! ");
}
else ESP_LOGD(TAG_CTRL_TASK, "Timer is started.");
}
}
break;
}
case APP_EVENT_VOICE_CMD_RECOGNIZED: { // 定时器超时,恢复SD, 命令被识别
// CLose Timer
if (xTimerIsTimerActive(timer_voice_cmd)) {
xTimerStop(timer_voice_cmd, 0);
ESP_LOGD(TAG_CTRL_TASK, "Timer is stopped by COMMAND_DECT.");
}
// 播放:提示音2, 防止:连续多次唤醒,导致管道阻塞
isToneCmd = true;
// 更新:命令词
switch (app_event.phrase_id) {
case CMD_ID_PLAY:{
switch (sd_state) {
case SD_CONTROL_PLAY:{
audio_pipeline_resume(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "sd is resumed by playing.");
break;
}
case SD_CONTROL_PAUSE:{
audio_pipeline_resume(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "sd is resumed by pause.");
break;
}
case SD_CONTROL_NONE:{ // 装载文件并播放
char *url = NULL;
esp_err_t get_url_ret = sdcard_list_current(sdcard_list_handle, &url);
if (get_url_ret == ESP_OK && url != NULL) {
audio_element_set_uri(sd_stream_reader, url);
ESP_LOGD(TAG_CTRL_TASK, "URL: %s", url);
audio_pipeline_reset_ringbuffer(pipeline_sd);
audio_pipeline_reset_elements(pipeline_sd);
audio_pipeline_run(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "sd is loaded and ran.");
}
break;
}
default:
break;
}
sd_state = SD_CONTROL_PLAY; // 更新状态
isTonePending = false; // 命令已经执行,无需(在定时器超时时)恢复被提示音1挂起的播放
ESP_LOGD(TAG_CTRL_TASK, "sd_state = SD_CONTROL_PLAY, isTonePending = false.");
break;
}
case CMD_ID_PAUSE:{
if (sd_state == SD_CONTROL_PLAY) {
audio_pipeline_pause(pipeline_sd);
sd_state = SD_CONTROL_PAUSE;
isTonePending = false; // 命令已经执行,无需(在定时器超时时)恢复被提示音1挂起的播放
ESP_LOGD(TAG_CTRL_TASK, "sd is paused. sd_state = SD_CONTROL_PAUSE, isTonePending = false.");
}
break;
}
case CMD_ID_PREV:{
char *url = NULL;
audio_pipeline_stop(pipeline_sd);
audio_pipeline_wait_for_stop(pipeline_sd);
audio_pipeline_terminate(pipeline_sd);
sdcard_list_prev(sdcard_list_handle, 1, &url);
ESP_LOGD(TAG_CTRL_TASK, "URL: %s", url);
//audio_element_set_uri(sd_stream_reader, "/sdcard/nce2_58.mp3");
//ESP_LOGD(TAG_CTRL_TASK, "URL: %s", "/sdcard/nce2_58.mp3");
audio_element_set_uri(sd_stream_reader, url);
audio_pipeline_reset_ringbuffer(pipeline_sd);
audio_pipeline_reset_elements(pipeline_sd);
audio_pipeline_run(pipeline_sd);
sd_state = SD_CONTROL_PLAY;
isTonePending = false;
ESP_LOGD(TAG_CTRL_TASK, "pipeline_sd is ran by prev-cmd.");
break;
}
case CMD_ID_NEXT:{
char *url = NULL;
audio_pipeline_stop(pipeline_sd);
audio_pipeline_wait_for_stop(pipeline_sd);
audio_pipeline_terminate(pipeline_sd);
sdcard_list_next(sdcard_list_handle, 1, &url);
ESP_LOGD(TAG_CTRL_TASK, "URL: %s", url);
//audio_element_set_uri(sd_stream_reader, "/sdcard/podcast.mp3");
//ESP_LOGD(TAG_CTRL_TASK, "URL: %s", "/sdcard/podcast.mp3");
audio_element_set_uri(sd_stream_reader, url);
audio_pipeline_reset_ringbuffer(pipeline_sd);
audio_pipeline_reset_elements(pipeline_sd);
audio_pipeline_run(pipeline_sd);
sd_state = SD_CONTROL_PLAY;
isTonePending = false;
ESP_LOGD(TAG_CTRL_TASK, "pipeline_sd is ran by next-cmd.");
break;
}
case CMD_ID_VOLUP:{
audio_board_handle_t bh = audio_board_get_handle();
if (!bh || !bh->audio_hal) {
ESP_LOGE(TAG_CTRL_TASK, "Failed get board/HAL handle for volume!");
break;
}
audio_hal_handle_t hal = bh->audio_hal;
int cv = 0;
if (audio_hal_get_volume(hal, &cv) == ESP_OK) {
int nv = cv + VOLUME_STEP;
if (nv > 100) nv = 100;
if (audio_hal_set_volume(hal, nv) == ESP_OK) {
ESP_LOGD(TAG_CTRL_TASK, "Volume-Up changed: %d -> %d", cv, nv);
} else {
ESP_LOGE(TAG_CTRL_TASK, "audio_hal_set_volume failed");
}
} else { ESP_LOGE(TAG_CTRL_TASK, "audio_hal_get_volume failed"); }
if (isTonePending) { // 如果 SD 被挂起,恢复,不依赖定时器
isTonePending = false;
audio_pipeline_resume(pipeline_sd);
sd_state = SD_CONTROL_PLAY;
ESP_LOGD(TAG_CTRL_TASK, "SD-PLAYING is resumed by SD_CONTROL_VOLUP.");
}
break;
}
case CMD_ID_VOLDN:{
audio_board_handle_t bh = audio_board_get_handle();
if (!bh || !bh->audio_hal) {
ESP_LOGE(TAG_CTRL_TASK, "Failed get board/HAL handle for volume!");
break;
}
audio_hal_handle_t hal = bh->audio_hal;
int cv = 0;
if (audio_hal_get_volume(hal, &cv) == ESP_OK) {
int nv = cv - VOLUME_STEP;
if (nv < 0) nv = 0;
if (audio_hal_set_volume(hal, nv) == ESP_OK) {
ESP_LOGD(TAG_CTRL_TASK, "Volume-Dn changed: %d -> %d", cv, nv);
} else {
ESP_LOGE(TAG_CTRL_TASK, "audio_hal_set_volume failed");
}
} else { ESP_LOGE(TAG_CTRL_TASK, "audio_hal_get_volume failed"); }
if (isTonePending) {
isTonePending = false;
audio_pipeline_resume(pipeline_sd);
sd_state = SD_CONTROL_PLAY;
ESP_LOGD(TAG_CTRL_TASK, "SD-PLAYING is resumed by SD_CONTROL_VOLDN.");
}
break;
}
default:
ESP_LOGW(TAG_CTRL_TASK, "Unknown command phrase_id: %d. No ack tone.", app_event.phrase_id);
isToneCmd = false;
ESP_LOGE(TAG_CTRL_TASK, "cur_cmd = SD_CONTROL_NONE, isToneCmd = false.");
break;
}
isToneCmd = false; // 确保所有命令执行完成后复位,避免定时器恢复
isTurnStart = false; // 新的一轮语音交互结束
ESP_LOGD(TAG_CTRL_TASK, "Command is done. cur_cmd = SD_CONTROL_NONE.");
break;
}
case APP_EVENT_SD_PLAYBACK_FINISHED: {
audio_pipeline_stop(pipeline_sd);
audio_pipeline_wait_for_stop(pipeline_sd);
audio_pipeline_terminate(pipeline_sd);
audio_pipeline_reset_ringbuffer(pipeline_sd);
audio_pipeline_reset_elements(pipeline_sd);
isToneCmd = false; // 确保所有命令执行完成后复位,避免定时器恢复
isTurnStart = false; // 新的一轮语音交互结束
sd_state = SD_CONTROL_NONE; // 状态 重置
ESP_LOGD(TAG_CTRL_TASK, "sd-player is finished.");
break;
}
case APP_EVENT_LATE_CMD_TIMEOUT: {
ESP_LOGD(TAG_CTRL_TASK, "LATE_CMD_TIMEOUT. isToneCmd: %d, isTonePending: %d", isToneCmd, isTonePending);
if (isToneCmd) { // 从命令词识别事件被接收,到处理完成的时段内,不允许恢复SD.
ESP_LOGD(TAG_CTRL_TASK, "A command is active (isToneCmd=true), timer will not resume SD.");
}
else if (isTonePending) {
isTonePending = false;
audio_element_state_t el_state = audio_element_get_state(sd_stream_writer);
ESP_LOGD(TAG_CTRL_TASK, "Timer expired. el_state is: %d.", el_state);
if (el_state == AEL_STATE_PAUSED) {
audio_pipeline_resume(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "Timer expired. SD-PLAYING is resumed.");
sd_state = SD_CONTROL_PLAY;
}
else if (el_state == AEL_STATE_INIT) {
audio_pipeline_run(pipeline_sd);
ESP_LOGD(TAG_CTRL_TASK, "Timer expired. SD-PLAYING is ran.");
sd_state = SD_CONTROL_PLAY;
}
else
ESP_LOGD(TAG_CTRL_TASK, "Timer expired. el_state is: %d.", el_state);
}
else {
ESP_LOGD(TAG_CTRL_TASK, "Timer expired. No SD pending.");
}
break;
}
default:
break;
}
} else {
ESP_LOGE(TAG_CTRL_TASK, "[Control Task] Failed receive event!");
vTaskDelay(pdMS_TO_TICKS(100));
}
}
}
