测试方法:
下一曲并播放 --- 暂停 --- 恢复 --- 暂停 --- 无法恢复,只要不断重复这个操作,大概率发生 。
使用按键: set --- play --- pause , 重复几次 该过程,一定会发生:无法恢复播放。
以上测试只是验证了 sd 管道 控制过程中的错误,根本原因是:管道中的 重采样元素(rsp) 由于 mp3解码器元素 被暂停 而发生 xEventGroupSetBits 2秒 超时 ,当 管道再次 恢复操作时,管道中的其他元素都进入运行状态时,rsp 才执行 暂停 操作,最终整条管道被迫停止。
上述过程是在实现 语音控制SD卡播放时发现的,目前还无法解决。
---
输出信息如下:
Code: Select all
I (692146) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (692146) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (692146) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Resuming audio pipeline
D (692256) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_MUSIC_INFO(9), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=0, data_ptr=0x0 (MusicInfo)
I (692256) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Received music info from mp3 decoder, sample_rates=44100, bits=16, ch=1
D (692266) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (692286) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (692306) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'filter' (at 0x3c0b98cc), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (692316) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'file' (at 0x3c0b9a4c), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
I (694706) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 2
I (694706) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Set] input key event
I (694706) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Stopped, advancing to the next song
W (694706) AUDIO_ELEMENT: OUT-[file] AEL_IO_ABORT
W (694706) AUDIO_ELEMENT: OUT-[mp3] AEL_IO_ABORT
W (694716) MP3_DECODER: Output aborted, -3
W (694706) AUDIO_ELEMENT: OUT-[filter] AEL_IO_ABORT
W (694726) AUDIO_ELEMENT: [0x3c0b9a4c-file] is already in the AEL_STATE_INIT state
D (694736) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_POSITION(11), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=64, data_ptr=0x3c0ca218
W (694736) SDCARD_MP3_CONTROL_EXAMPLE: URL: file://sdcard/podcast.mp3
D (694746) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_STOPPED(14)
E (694856) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Event interface error : -1
D (694876) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (694886) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'filter' (at 0x3c0b98cc), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (694896) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'file' (at 0x3c0b9a4c), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
E (694916) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Event interface error : -1
D (694926) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
E (694936) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Event interface error : -1
E (694946) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Event interface error : -1
D (694956) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_MUSIC_INFO(9), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=0, data_ptr=0x0 (MusicInfo)
I (694966) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Received music info from mp3 decoder, sample_rates=44100, bits=16, ch=2
E (694976) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Event interface error : -1
I (696326) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (696326) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (696326) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Pausing audio pipeline
D (696356) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'file' (at 0x3c0b9a4c), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
D (696386) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
W (697866) ADC_BTN: Old ID:3, New ID:5, Cnt:10
D (698406) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
I (698406) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (698416) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (698426) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Resuming audio pipeline
D (698436) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'filter' (at 0x3c0b98cc), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (698446) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (698466) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_MUSIC_INFO(9), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=0, data_ptr=0x0 (MusicInfo)
I (698476) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Received music info from mp3 decoder, sample_rates=44100, bits=16, ch=2
D (698486) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (698506) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'filter' (at 0x3c0b98cc), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
D (698526) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'file' (at 0x3c0b9a4c), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
W (699766) ADC_BTN: Old ID:5, New ID:3, Cnt:0
I (700026) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (700026) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (700026) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Pausing audio pipeline
D (704036) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
I (704036) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (704046) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (704056) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Resuming audio pipeline
I (704056) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] input key id is 3
I (704066) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] [Play] input key event
I (704066) SDCARD_MP3_CONTROL_EXAMPLE: [ * ] Pausing audio pipeline
D (704156) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'file' (at 0x3c0b9a4c), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (704156) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (704176) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (704196) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'filter' (at 0x3c0b98cc), data_len=4, status_data=AEL_STATUS_STATE_RUNNING(12)
D (704206) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=Element 'mp3' (at 0x3c0b9758), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)
D (708076) [* AUD_EVT *]: audio_evt_iface_listener: cmd=AEL_MSG_CMD_REPORT_STATUS(8), src_type=131072, src=sd_stream_writer (at 0x3c0b93e0), data_len=4, status_data=AEL_STATUS_STATE_PAUSED(13)[/quote]程序源代码:
Code: Select all
/* Control with a touch pad playing MP3 files from SD Card
This example code is in the Public Domain (or CC0 licensed, at your option.)
Unless required by applicable law or agreed to in writing, this
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
CONDITIONS OF ANY KIND, either express or implied.
*/
#include <string.h>
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "nvs_flash.h"
#include "audio_element.h"
#include "audio_pipeline.h"
#include "audio_event_iface.h"
#include "audio_common.h"
#include "fatfs_stream.h"
#include "i2s_stream.h"
#include "mp3_decoder.h"
#include "filter_resample.h"
#include "esp_peripherals.h"
#include "periph_sdcard.h"
#include "periph_touch.h"
#include "periph_button.h"
#include "input_key_service.h"
#include "periph_adc_button.h"
#include "board.h"
#include "sdcard_list.h"
#include "sdcard_scan.h"
static const char *TAG = "SDCARD_MP3_CONTROL_EXAMPLE";
static char *TAG_AUD_EVT = "[* AUD_EVT *]"; // set:info, use:dbg
audio_pipeline_handle_t pipeline;
audio_element_handle_t i2s_stream_writer, mp3_decoder, fatfs_stream_reader, rsp_handle;
playlist_operator_handle_t sdcard_list_handle = NULL;
static esp_err_t input_key_service_cb(periph_service_handle_t handle, periph_service_event_t *evt, void *ctx)
{
/* Handle touch pad events
to start, pause, resume, finish current song and adjust volume
*/
audio_board_handle_t board_handle = (audio_board_handle_t) ctx;
int player_volume;
audio_hal_get_volume(board_handle->audio_hal, &player_volume);
if (evt->type == INPUT_KEY_SERVICE_ACTION_CLICK_RELEASE) {
ESP_LOGI(TAG, "[ * ] input key id is %d", (int)evt->data);
switch ((int)evt->data) {
case INPUT_KEY_USER_ID_PLAY:
ESP_LOGI(TAG, "[ * ] [Play] input key event");
audio_element_state_t el_state = audio_element_get_state(i2s_stream_writer);
switch (el_state) {
case AEL_STATE_INIT :
ESP_LOGI(TAG, "[ * ] Starting audio pipeline");
audio_pipeline_run(pipeline);
break;
case AEL_STATE_RUNNING :
ESP_LOGI(TAG, "[ * ] Pausing audio pipeline");
audio_pipeline_pause(pipeline);
break;
case AEL_STATE_PAUSED :
ESP_LOGI(TAG, "[ * ] Resuming audio pipeline");
audio_pipeline_resume(pipeline);
break;
default :
ESP_LOGI(TAG, "[ * ] Not supported state %d", el_state);
}
break;
case INPUT_KEY_USER_ID_SET:
ESP_LOGI(TAG, "[ * ] [Set] input key event");
ESP_LOGI(TAG, "[ * ] Stopped, advancing to the next song");
char *url = NULL;
audio_pipeline_stop(pipeline);
audio_pipeline_wait_for_stop(pipeline);
audio_pipeline_terminate(pipeline);
sdcard_list_next(sdcard_list_handle, 1, &url);
ESP_LOGW(TAG, "URL: %s", url);
audio_element_set_uri(fatfs_stream_reader, url);
audio_pipeline_reset_ringbuffer(pipeline);
audio_pipeline_reset_elements(pipeline);
audio_pipeline_run(pipeline);
break;
case INPUT_KEY_USER_ID_VOLUP:
ESP_LOGI(TAG, "[ * ] [Vol+] input key event");
player_volume += 10;
if (player_volume > 100) {
player_volume = 100;
}
audio_hal_set_volume(board_handle->audio_hal, player_volume);
ESP_LOGI(TAG, "[ * ] Volume set to %d %%", player_volume);
break;
case INPUT_KEY_USER_ID_VOLDOWN:
ESP_LOGI(TAG, "[ * ] [Vol-] input key event");
player_volume -= 10;
if (player_volume < 0) {
player_volume = 0;
}
audio_hal_set_volume(board_handle->audio_hal, player_volume);
ESP_LOGI(TAG, "[ * ] Volume set to %d %%", player_volume);
break;
}
}
return ESP_OK;
}
void sdcard_url_save_cb(void *user_data, char *url)
{
playlist_operator_handle_t sdcard_handle = (playlist_operator_handle_t)user_data;
esp_err_t ret = sdcard_list_save(sdcard_handle, url);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "Fail to save sdcard url to sdcard playlist");
}
}
#define DEBUG_AUD_EVT 1 // 调试开关: 输出:音频管道 中的所有事件
// 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)
{
esp_log_level_set("*", ESP_LOG_WARN);
esp_log_level_set(TAG, ESP_LOG_DEBUG);
esp_log_level_set(TAG_AUD_EVT, ESP_LOG_DEBUG);
#if 1
ESP_LOGI(TAG, "[1.0] Initialize peripherals management");
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
ESP_LOGI(TAG, "[1.1] Initialize and start peripherals");
audio_board_key_init(set);
audio_board_sdcard_init(set, SD_MODE_1_LINE);
ESP_LOGI(TAG, "[1.2] Set up a sdcard playlist and scan sdcard music save to it");
sdcard_list_create(&sdcard_list_handle);
sdcard_scan(sdcard_url_save_cb, "/sdcard", 0, (const char *[]) {"mp3"}, 1, sdcard_list_handle);
sdcard_list_show(sdcard_list_handle);
ESP_LOGI(TAG, "[ 2 ] Start codec chip");
audio_board_handle_t board_handle = audio_board_init();
audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_DECODE, AUDIO_HAL_CTRL_START);
ESP_LOGI(TAG, "[ 3 ] Create and start input key service");
input_key_service_info_t input_key_info[] = INPUT_KEY_DEFAULT_INFO();
input_key_service_cfg_t input_cfg = INPUT_KEY_SERVICE_DEFAULT_CONFIG();
input_cfg.handle = set;
periph_service_handle_t input_ser = input_key_service_create(&input_cfg);
input_key_service_add_key(input_ser, input_key_info, INPUT_KEY_NUM);
periph_service_set_callback(input_ser, input_key_service_cb, (void *)board_handle);
ESP_LOGI(TAG, "[4.0] Create audio pipeline for playback");
audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline = audio_pipeline_init(&pipeline_cfg);
mem_assert(pipeline);
ESP_LOGI(TAG, "[4.1] Create i2s stream to write data to codec chip");
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
i2s_cfg.type = AUDIO_STREAM_WRITER;
i2s_stream_writer = i2s_stream_init(&i2s_cfg);
i2s_stream_set_clk(i2s_stream_writer, 48000, 16, 2);
ESP_LOGI(TAG, "[4.2] Create mp3 decoder to decode mp3 file");
mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
mp3_decoder = mp3_decoder_init(&mp3_cfg);
/* ZL38063 audio chip on board of ESP32-LyraTD-MSC does not support 44.1 kHz sampling frequency,
so resample filter has been added to convert audio data to other rates accepted by the chip.
You can resample the data to 16 kHz or 48 kHz.
*/
ESP_LOGI(TAG, "[4.3] Create resample filter");
rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
rsp_handle = rsp_filter_init(&rsp_cfg);
ESP_LOGI(TAG, "[4.4] Create fatfs stream to read data from sdcard");
char *url = NULL;
sdcard_list_current(sdcard_list_handle, &url);
fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
fatfs_cfg.type = AUDIO_STREAM_READER;
fatfs_stream_reader = fatfs_stream_init(&fatfs_cfg);
audio_element_set_uri(fatfs_stream_reader, url);
ESP_LOGI(TAG, "[4.5] Register all elements to audio pipeline");
audio_pipeline_register(pipeline, fatfs_stream_reader, "file");
audio_pipeline_register(pipeline, mp3_decoder, "mp3");
audio_pipeline_register(pipeline, rsp_handle, "filter");
audio_pipeline_register(pipeline, i2s_stream_writer, "i2s");
ESP_LOGI(TAG, "[4.6] Link it together [sdcard]-->fatfs_stream-->mp3_decoder-->resample-->i2s_stream-->[codec_chip]");
const char *link_tag[4] = {"file", "mp3", "filter", "i2s"};
audio_pipeline_link(pipeline, &link_tag[0], 4);
ESP_LOGI(TAG, "[5.0] Set up event listener");
audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
audio_event_iface_handle_t evt = audio_event_iface_init(&evt_cfg);
ESP_LOGI(TAG, "[5.1] Listen for all pipeline events");
audio_pipeline_set_listener(pipeline, evt);
ESP_LOGW(TAG, "[ 6 ] Press the keys to control music player:");
ESP_LOGW(TAG, " [Play] to start, pause and resume, [Set] next song.");
ESP_LOGW(TAG, " [Vol-] or [Vol+] to adjust volume.");
#endif
while (1) {
/* Handle event interface messages from pipeline
to set music info and to advance to the next song
*/
audio_event_iface_msg_t msg;
esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[ * ] Event interface error : %d", ret);
continue;
}
// 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, i2s_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 -------
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT) {
// Set music info for a new song to be played
if (msg.source == (void *) mp3_decoder
&& msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
audio_element_info_t music_info = {0};
audio_element_getinfo(mp3_decoder, &music_info);
ESP_LOGI(TAG, "[ * ] Received music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d",
music_info.sample_rates, music_info.bits, music_info.channels);
audio_element_setinfo(i2s_stream_writer, &music_info);
rsp_filter_set_src_info(rsp_handle, music_info.sample_rates, music_info.channels);
continue;
}
// Advance to the next song when previous finishes
if (msg.source == (void *) i2s_stream_writer
&& msg.cmd == AEL_MSG_CMD_REPORT_STATUS) {
audio_element_state_t el_state = audio_element_get_state(i2s_stream_writer);
if (el_state == AEL_STATE_FINISHED) {
ESP_LOGI(TAG, "[ * ] Finished, advancing to the next song");
sdcard_list_next(sdcard_list_handle, 1, &url);
ESP_LOGW(TAG, "URL: %s", url);
/* In previous versions, audio_pipeline_terminal() was called here. It will close all the element task and when we use
* the pipeline next time, all the tasks should be restarted again. It wastes too much time when we switch to another music.
* So we use another method to achieve this as below.
*/
audio_element_set_uri(fatfs_stream_reader, url);
audio_pipeline_reset_ringbuffer(pipeline);
audio_pipeline_reset_elements(pipeline);
audio_pipeline_change_state(pipeline, AEL_STATE_INIT);
audio_pipeline_run(pipeline);
}
continue;
}
}
}
ESP_LOGI(TAG, "[ 7 ] Stop audio_pipeline");
audio_pipeline_stop(pipeline);
audio_pipeline_wait_for_stop(pipeline);
audio_pipeline_terminate(pipeline);
audio_pipeline_unregister(pipeline, mp3_decoder);
audio_pipeline_unregister(pipeline, i2s_stream_writer);
audio_pipeline_unregister(pipeline, rsp_handle);
/* Terminate the pipeline before removing the listener */
audio_pipeline_remove_listener(pipeline);
/* Stop all peripherals before removing the listener */
esp_periph_set_stop_all(set);
audio_event_iface_remove_listener(esp_periph_set_get_event_iface(set), evt);
/* Make sure audio_pipeline_remove_listener & audio_event_iface_remove_listener are called before destroying event_iface */
audio_event_iface_destroy(evt);
/* Release all resources */
sdcard_list_destroy(sdcard_list_handle);
audio_pipeline_deinit(pipeline);
audio_element_deinit(i2s_stream_writer);
audio_element_deinit(mp3_decoder);
audio_element_deinit(rsp_handle);
periph_service_destroy(input_ser);
esp_periph_set_destroy(set);
}