學習ADF框架,不能錯過音樂播放,本篇博客記錄如何通過SD卡播放音樂:
上代碼前先看這兩張圖:
這兩圖基本就是ADF框架播放mp3的流程:首先將MP3解碼器和I2S流兩個元素添加進管道,解碼器的輸入是MP3文件數據流,I2S流將解碼後的音頻數據輸出到片外,各應用程序之間通過事件接口通信。
上代碼:
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "board.h"
#include "audio_pipeline.h"
#include "fatfs_stream.h"
#include "i2s_stream.h"
#include "raw_stream.h"
#include "esp_audio.h"
#include "mp3_decoder.h"
#include "filter_resample.h"
#include "rec_eng_helper.h"
static const char *TAG = "SDCARD_MP3_EXAMPLE";
static esp_audio_handle_t setup_player() //播放器
{
esp_audio_handle_t player = NULL;
esp_audio_cfg_t cfg = DEFAULT_ESP_AUDIO_CONFIG();
audio_board_handle_t board_handle = audio_board_init();
cfg.vol_handle = board_handle->audio_hal; //音量
cfg.prefer_type = ESP_AUDIO_PREFER_MEM;
#if defined CONFIG_ESP_LYRAT_MINI_V1_1_BOARD
cfg.resample_rate = 16000;//設置重採樣率
#else
cfg.resample_rate = 48000;
#endif
player = esp_audio_create(&cfg);
audio_hal_ctrl_codec(board_handle->audio_hal, AUDIO_HAL_CODEC_MODE_BOTH, AUDIO_HAL_CTRL_START);//音量
fatfs_stream_cfg_t fs_reader = FATFS_STREAM_CFG_DEFAULT();
fs_reader.type = AUDIO_STREAM_READER;
raw_stream_cfg_t raw_reader = RAW_STREAM_CFG_DEFAULT();
raw_reader.type = AUDIO_STREAM_READER;
esp_audio_input_stream_add(player, raw_stream_init(&raw_reader));//
esp_audio_input_stream_add(player, fatfs_stream_init(&fs_reader));//
mp3_decoder_cfg_t mp3_dec_cfg = DEFAULT_MP3_DECODER_CONFIG();
esp_audio_codec_lib_add(player, AUDIO_CODEC_TYPE_DECODER, mp3_decoder_init(&mp3_dec_cfg));//初始化mp3 decoder
i2s_stream_cfg_t i2s_writer = I2S_STREAM_CFG_DEFAULT();
#if defined CONFIG_ESP_LYRAT_MINI_V1_1_BOARD
i2s_writer.i2s_config.sample_rate = 16000;
#else
i2s_writer.i2s_config.sample_rate = 48000;
#endif
i2s_writer.type = AUDIO_STREAM_WRITER;
audio_element_handle_t i2s_stream_writer = i2s_stream_init(&i2s_writer);//初始化i2s stream
esp_audio_output_stream_add(player, i2s_stream_writer);
esp_audio_vol_set(player, 100);//設置音量
ESP_LOGI(TAG, "esp_audio instance is:%p\r\n", player);
return player;
}
void app_main()
{
#if defined CONFIG_ESP_LYRAT_V4_3_BOARD
gpio_config_t gpio_conf = {
.pin_bit_mask = 1UL << get_green_led_gpio(),//LED燈設置
.mode = GPIO_MODE_OUTPUT,
.pull_up_en = 0,
.pull_down_en = 0,
.intr_type = 0
};
gpio_config(&gpio_conf);
#endif
esp_log_level_set("*", ESP_LOG_WARN);
esp_log_level_set(TAG, ESP_LOG_INFO);
audio_pipeline_handle_t pipeline; //管道
audio_element_handle_t i2s_stream_reader, filter, raw_read; //音頻元素
esp_audio_handle_t player;
ESP_LOGI(TAG, "[ 1 ] Start codec chip");//初始化codec 芯片
esp_periph_config_t periph_cfg = DEFAULT_ESP_PERIPH_SET_CONFIG();
audio_board_sdcard_init(esp_periph_set_init(&periph_cfg));
ESP_LOGI(TAG, "[ 2.0 ] Create audio pipeline for recording");//創建音頻管道給recording
audio_pipeline_cfg_t pipeline_cfg = DEFAULT_AUDIO_PIPELINE_CONFIG();
pipeline = audio_pipeline_init(&pipeline_cfg);
mem_assert(pipeline);
ESP_LOGI(TAG, "[ 2.1 ] Create i2s stream to read audio data from codec chip");//爲讀音頻數據創建i2s stream
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
i2s_cfg.i2s_config.sample_rate = 48000;
i2s_cfg.type = AUDIO_STREAM_READER;
#if defined CONFIG_ESP_LYRAT_MINI_V1_1_BOARD
i2s_cfg.i2s_config.sample_rate = 16000;
i2s_cfg.i2s_port = 1;
i2s_cfg.i2s_config.channel_format = I2S_CHANNEL_FMT_ONLY_RIGHT;
i2s_stream_reader = i2s_stream_init(&i2s_cfg);//初始化i2s
#else
i2s_stream_reader = i2s_stream_init(&i2s_cfg);
ESP_LOGI(TAG, "[ 2.2 ] Create filter to resample audio data");//爲重採樣音頻數據創建filter
rsp_filter_cfg_t rsp_cfg = DEFAULT_RESAMPLE_FILTER_CONFIG();
rsp_cfg.src_rate = 48000;
rsp_cfg.src_ch = 2;
rsp_cfg.dest_rate = 16000;
rsp_cfg.dest_ch = 1;
filter = rsp_filter_init(&rsp_cfg);
#endif
ESP_LOGI(TAG, "[ 2.3 ] Create raw to receive data");//爲接收數據創建raw
raw_stream_cfg_t raw_cfg = {
.out_rb_size = 8 * 1024,
.type = AUDIO_STREAM_READER,
};
raw_read = raw_stream_init(&raw_cfg);
ESP_LOGI(TAG, "[ 3 ] Register all elements to audio pipeline");//註冊所有元素到管道
audio_pipeline_register(pipeline, i2s_stream_reader, "i2s");
audio_pipeline_register(pipeline, raw_read, "raw");
#if defined CONFIG_ESP_LYRAT_MINI_V1_1_BOARD
ESP_LOGI(TAG, "[ 4 ] Link elements together [codec_chip]-->i2s_stream-->raw-->[SR]");
audio_pipeline_link(pipeline, (const char *[]) {"i2s", "raw"}, 2);
#else
audio_pipeline_register(pipeline, filter, "filter");
ESP_LOGI(TAG, "[ 4 ] Link elements together [codec_chip]-->i2s_stream-->filter-->raw-->[SR]");
audio_pipeline_link(pipeline, (const char *[]) {"i2s", "filter", "raw"}, 3);
#endif
player = setup_player();
ESP_LOGI(TAG, "[ 5 ] Start audio_pipeline");//開始音頻管道
audio_pipeline_run(pipeline);
esp_audio_sync_play(player, "file://sdcard/test.mp3", 0);//SD卡里文件名爲test的mp3文件
ESP_LOGI(TAG, "[ 6 ] Stop audio_pipeline");//停止音頻管道
audio_pipeline_terminate(pipeline);
/* Terminate the pipeline before removing the listener */
audio_pipeline_remove_listener(pipeline);
audio_pipeline_unregister(pipeline, raw_read);
audio_pipeline_unregister(pipeline, i2s_stream_reader);
audio_pipeline_unregister(pipeline, filter);
/* Release all resources */
audio_pipeline_deinit(pipeline);
audio_element_deinit(raw_read);
audio_element_deinit(i2s_stream_reader);
audio_element_deinit(filter);
ESP_LOGI(TAG, "[ 7 ] Destroy model");
}
此程序是根據ADF例程中\examples\speech_recognition\asr例程修改而來,和player文件裏的例程有所不同!這個修改後的例程將播放器模塊化,方便後面的程序調用,一句:esp_audio_sync_play();即可調用播放!後面將根據這個模塊,結合語音喚醒和命令詞做進一步修改!用語言控制播放!