esp32S3+ES8388+LEDC+PYTHON PC客户端4 - ES8388播放音乐
·
esp32S3+ES8388+LEDC+PYTHON PC客户端4
ES8388播放音乐
之前创建项目使用的例程,其实是完整可以播放的项目了。需要额外增加SD卡。
数据流
播放数据流,采用的是三级流水线的方式:
1.fatfs读取SD音频数据放入ringbuf1中
2.codec获取ringbuf1中的音频数据,将数据解码放入ringbuf2中
3.i2s获取ringbuf2中的音频数据,发送到ES8388中
以上三个流水线均作为element,放入到pipeline(管道)中管理,包括启动、暂停、停止、监听事件
代码
下面代码理解,如有错误请见谅。
日志等级
static const char *TAG = "PLAY_SDCARD_MUSIC";
esp_log_level_set("*", ESP_LOG_WARN);//*代表整个项目的日志等级均需在>=WARN才能输出
//代表使用TAG字符串输出的日志等级为>=INFO.看起来有点矛盾,我并没有做测试
esp_log_level_set(TAG, ESP_LOG_INFO);
//INFO日志输出.LOGI表示INFO,LOGW表示warn,LOGE表示error
//TAG自定义字符串"PLAY_SDCARD_MUSIC"
//输出格式为: PLAY_SDCARD_MUSIC:[ 1 ] Mount sdcar
ESP_LOGI(TAG, "[ 1 ] Mount sdcard");
主体代码
//管道与element的创建
audio_pipeline_handle_t pipeline;
audio_element_handle_t fatfs_stream_reader, i2s_stream_writer, music_decoder;
//初始化外设管理
//DEFAULT_ESP_PERIPH_SET_CONFIG 展开为
// 任务栈大小、任务优先级、CPU0、无外部栈
//{ .task_stack = (4*1024), .task_prio = (5), .task_core = (0), .extern_stack = 0, }
//periph_cfg 可以使用periph_cfg.task_core = 1,将任务移动到CPU1(应用态)
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
//初始化外设集与外设链表
//用于监听外设
esp_periph_set_handle_t set = esp_periph_set_init(&periph_cfg);
//初始化SD以及SD卡挂载,挂载为文件系统.根目录一般设为/sdcard
//可以使用fopen,fclose,fwrite,fread.对文件进行操作.例如fopen("/sdcard/filename.map3",rb)
//放入到set列表,启动监听
audio_board_sdcard_init(set, SD_MODE_1_LINE);//单线模式.与我们之前SD卡IO口设置有关
//编解码器初始化
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);
//创建管道
audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline = audio_pipeline_init(&pipeline_cfg);
//创建fatfs数据流,从SD卡读取数据.也可以通过fatfs_cfg.task_core = 1放到CPU1
//优先级4.确保自己创建的task优先级不要超过4.或者提高此优先级
fatfs_stream_cfg_t fatfs_cfg = FATFS_STREAM_CFG_DEFAULT();
fatfs_cfg.type = AUDIO_STREAM_READER;
fatfs_stream_reader = fatfs_stream_init(&fatfs_cfg);
//创建I2S数据流.也可以通过i2s_cfg.task_core = 1放到CPU1
//优先级23,确保音频不卡顿
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
i2s_cfg.type = AUDIO_STREAM_WRITER;
i2s_stream_writer = i2s_stream_init(&i2s_cfg);
//解码数据流.也可以通过mp3_cfg.task_core = 1放入到CPU1
//优先级5
mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
music_decoder = mp3_decoder_init(&mp3_cfg);
//3个element加入到管道列表
audio_pipeline_register(pipeline, fatfs_stream_reader, "file");
audio_pipeline_register(pipeline, music_decoder, "dec");
audio_pipeline_register(pipeline, i2s_stream_writer, "i2s");
//名称,与注册时一样
const char *link_tag[3] = {"file", "dec", "i2s"};
//执行顺序[sdcard]-->fatfs_stream-->music_decoder-->i2s_stream-->[codec_chip]
audio_pipeline_link(pipeline, &link_tag[0], 3);
//打开map3文件.以自己的map3名称为主
audio_element_set_uri(fatfs_stream_reader, "/sdcard/test.mp3");
//设置事件队列大小,回调函数
audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
audio_event_iface_handle_t evt = audio_event_iface_init(&evt_cfg);
//设置监听
audio_pipeline_set_listener(pipeline, evt);
//监听SD外设与其他外设.这里只用到SD卡
audio_event_iface_set_listener(esp_periph_set_get_event_iface(set), evt);
//其他3个element的任务.通过事件通讯
audio_pipeline_run(pipeline);
//事件获取.建议扔到task任务执行
while (1) {
audio_event_iface_msg_t msg;
//阻塞执行,直到有事件.所以不用使用vTaskDelay延迟函数
esp_err_t ret = audio_event_iface_listen(evt, &msg, portMAX_DELAY);
if (ret != ESP_OK) {
continue;
}
//解码器AEL_MSG_CMD_REPORT_MUSIC_INFO事件.
//设置I2S 采集波特率sample_rates、位宽、通道数
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) music_decoder
&& msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO) {
audio_element_info_t music_info = {0};
audio_element_getinfo(music_decoder, &music_info);
audio_element_setinfo(i2s_stream_writer, &music_info);
i2s_stream_set_clk(i2s_stream_writer, music_info.sample_rates, music_info.bits, music_info.channels);
continue;
}
//fatfs数据流结束
if (msg.source == (void *)fatfs_stream_reader && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED)))
{
continue;
}
//解码器数据流结束
if (msg.source == (void *)music_decoder && msg.cmd == AEL_MSG_CMD_REPORT_STATUS && (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED)))
{
continue;
}
//I2S传输结束.结束流程一般为FATFS、decoder、i2s
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT && msg.source == (void *) i2s_stream_writer
&& msg.cmd == AEL_MSG_CMD_REPORT_STATUS
&& (((int)msg.data == AEL_STATUS_STATE_STOPPED) || ((int)msg.data == AEL_STATUS_STATE_FINISHED))) {
//如果该监听放在task里面.这里可以通知外部,可以播放下一首歌
//播放下一首歌前调用
//audio_pipeline_wait_for_stop(pipeline);
//audio_pipeline_reset_ringbuffer(pipeline);
//audio_pipeline_reset_elements(pipeline);
//audio_element_set_uri(fatfs_stream_reader, "path");
//audio_pipeline_run(pipeline);
break;
}
}
//结束播放.直接贴源码
audio_pipeline_stop(pipeline);
audio_pipeline_wait_for_stop(pipeline);
audio_pipeline_terminate(pipeline);
audio_pipeline_unregister(pipeline, fatfs_stream_reader);
audio_pipeline_unregister(pipeline, i2s_stream_writer);
audio_pipeline_unregister(pipeline, music_decoder);
/* Terminal the pipeline before removing the listener */
audio_pipeline_remove_listener(pipeline);
/* Stop all periph 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 */
audio_pipeline_deinit(pipeline);
audio_element_deinit(fatfs_stream_reader);
audio_element_deinit(i2s_stream_writer);
audio_element_deinit(music_decoder);
esp_periph_set_destroy(set);
更多推荐


所有评论(0)