# 播放 microSD 卡音乐
- [English Version](./README.md)
- 例程难度:⭐
## 例程简介
本示例展示了使用 FatFs IO 读取 microSD 卡中的音乐文件,经 decoder 解码及音效处理后通过 CODEC_DEV_TX IO 输出播放。
- 本示例使用单管道架构:`io_file` → decoder → 音效处理 → `io_codec_dev`。
- 支持 MP3、WAV、FLAC、AAC、M4A、TS、AMRNB、AMRWB 等格式,默认 MP3。
### 典型场景
- 本地音乐播放、语音提示等从 SD 卡读文件的场景。
## 环境配置
### 硬件要求
- **开发板**:默认以 ESP32-S3-Korvo V3 为例,其他 ESP 音频板同样适用。
- **资源要求**:microSD 卡、Audio DAC、扬声器。
### 默认 IDF 分支
本例程支持 IDF release/v5.4 (>= v5.4.3) 与 release/v5.5 (>= v5.5.2) 分支。
### 软件要求
准备一张 microSD 卡,将自备音源重命名为 `test`(扩展名与格式一致,如 `test.mp3`)并存入卡中。可通过 `DEFAULT_PLAY_URL` 更改播放文件路径。
## 编译和下载
### 编译准备
编译本例程前需先确保已配置 ESP-IDF 环境;若已配置可跳过本段,直接进入工程目录。若未配置,请在 ESP-IDF 根目录运行以下脚本完成环境设置,完整步骤请参阅 [《ESP-IDF 编程指南》](https://docs.espressif.com/projects/esp-idf/zh_CN/latest/esp32s3/index.html)。
```
./install.sh
. ./export.sh
```
下面是简略步骤:
- 进入本例程工程目录:
```
cd $YOUR_GMF_PATH/gmf_examples/basic_examples/pipeline_play_sdcard_music
```
本示例使用 [ESP Board Manager](https://github.com/espressif/esp-board-manager) 管理板级资源。推荐安装辅助工具 [`esp-bmgr-assist`](https://pypi.org/project/esp-bmgr-assist/) 作为默认入口。
- 在已激活的 ESP-IDF Python 环境下安装(同一环境只需安装一次):
```bash
pip install esp-bmgr-assist
pip install --upgrade esp-bmgr-assist # 当提示需要更新时执行此命令
```
- 查看支持的板子:
```bash
idf.py bmgr -l
```
输出示例:
```text
ℹ️ Board Components:
espressif/esp_boards:
[1] esp32_c3_lyra
[2] esp32_lyrat_4_3
[3] esp32_lyrat_mini_1_1
[4] esp32_p4_eye
[5] esp32_p4_function_ev_board
[6] esp32_s31_function_coreboard_1
[7] esp32_s31_korvo_1
[8] esp32_s3_box_3
[9] esp32_s3_box_lite
[10] esp32_s3_korvo_2_3
[11] esp32_s3_lcd_ev_board
[12] esp_vocat_1_0
[13] esp_vocat_1_2
```
以上输出示例基于 `esp_boards` 0.5.2 的开发板列表和排序。不同 `esp_boards` 版本或自定义开发板依赖可能会使列表和序号变化,使用时以 `idf.py bmgr -l` 的实际输出为准。
- 选择开发板:
```bash
idf.py bmgr -b <board_index|board_name>
```
例如选择 `esp32_s3_korvo_2_3`:
```bash
idf.py bmgr -b 10
# 或
idf.py bmgr -b esp32_s3_korvo_2_3
```
首次执行 `idf.py bmgr` 时,组件会根据本工程 `main/idf_component.yml` 中声明的 `espressif/esp_board_manager` 依赖自动下载。
> [!NOTE]
> 如果切换为其他 `esp_board_manager` 支持的开发板,请按相同步骤执行并替换板型名称/索引。
> 自定义开发板请参考 [创建开发板指南](https://docs.espressif.com/projects/esp-board-manager/zh_CN/latest/create-board/index.html)。
> `esp_board_manager` 更多信息请参考 [ESP_BOARD_MANAGER 入门指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/README_CN.md)
### 项目配置
- 若需调整音频效果相关参数(采样率、声道、位深等),可在 menuconfig 中配置 GMF Audio 相关选项。
```bash
idf.py menuconfig
```
在 menuconfig 中进行以下配置:
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Channel Convert Destination Channel`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Bit Convert Destination Bits`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Rate Convert Destination Rate`
> 配置完成后按 `s` 保存,然后按 `Esc` 退出。
### 编译与烧录
- 编译示例程序
```
idf.py build
```
- 烧录程序并运行 monitor 工具来查看串口输出 (替换 PORT 为端口名称):
```
idf.py -p PORT flash monitor
```
- 退出调试界面使用 `Ctrl-]`
## 如何使用例程
### 功能和用法
- 上电后例程会挂载 microSD 卡并初始化音频,自动播放卡中默认文件(如 `/sdcard/test.mp3` 或 `/sdcard/test.wav`),播放完成后停止并释放资源。
### 日志输出
- 正常流程会依次打印挂载 SD 卡、注册元素、创建 pipeline、设置 url、绑定任务、监听事件、启动 pipeline、打开文件并解码播放、等待结束并销毁资源。关键步骤以 `[ 1 ]`~`[ 6 ]` 标出。
```c
I (888) main_task: Calling app_main()
I (888) PLAY_SDCARD_MUSIC: [ 1 ] Mount sdcard
I (892) DEV_FS_FAT_SUB_SDMMC: slot_config: cd=-1, wp=-1, clk=15, cmd=7, d0=4, d1=-1, d2=-1, d3=-1, d4=-1, d5=-1, d6=-1, d7=-1, width=1, flags=0x1
Name: SA32G
Type: SDHC
Speed: 40.00 MHz (limit: 40.00 MHz)
Size: 29544MB
CSD: ver=2, sector_size=512, capacity=60506112 read_bl_len=9
SSR: bus_width=1
I (951) DEV_FS_FAT: Filesystem mounted, base path: /sdcard
I (956) BOARD_MANAGER: Device fs_sdcard initialized
I (961) PERIPH_I2S: I2S[0] TDM, TX, ws: 45, bclk: 9, dout: 8, din: 10
I (967) PERIPH_I2S: I2S[0] initialize success: 0x3c14fae4
I (972) DEV_AUDIO_CODEC: DAC is ENABLED
I (976) DEV_AUDIO_CODEC: Init audio_dac, i2s_name: i2s_audio_out, i2s_rx_handle:0x0, i2s_tx_handle:0x3c14fae4, data_if: 0x3fcead9c
I (988) PERIPH_I2C: I2C master bus initialized successfully
I (998) ES8311: Work in Slave mode
I (1000) DEV_AUDIO_CODEC: Successfully initialized codec: audio_dac
I (1002) DEV_AUDIO_CODEC: Create esp_codec_dev success, dev:0x3fceaff0, chip:es8311
I (1009) BOARD_MANAGER: Device audio_dac initialized
I (1014) BOARD_DEVICE: Device handle audio_dac found, Handle: 0x3fcea024 TO: 0x3fcea024
I (1022) I2S_IF: channel mode 2 bits:16/16 channel:2 mask:3
I (1027) I2S_IF: TDM Mode 1 bits:16/16 channel:2 sample_rate:44100 mask:3
I (1048) Adev_Codec: Open codec device OK
I (1048) PLAY_SDCARD_MUSIC: [ 2 ] Register all the elements and set audio information to play codec device
I (1051) PLAY_SDCARD_MUSIC: [ 3 ] Create audio pipeline
I (1055) PLAY_SDCARD_MUSIC: [ 3.1 ] Set audio url to play
I (1060) PLAY_SDCARD_MUSIC: [ 3.2 ] Create gmf task, bind task to pipeline and load linked element jobs to the bind task
I (1071) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x0, run:0]
I (1078) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x3c151b34, run:0]
I (1087) PLAY_SDCARD_MUSIC: [ 3.3 ] Create envent group and listening event from pipeline
I (1095) PLAY_SDCARD_MUSIC: [ 4 ] Start audio_pipeline
I (1100) ESP_GMF_FILE: Open, dir:1, uri:/sdcard/test.mp3
I (1105) PLAY_SDCARD_MUSIC: [ 5 ] Wait stop event to the pipeline and stop all the pipeline
I (1107) ESP_GMF_FILE: File size: 231023 byte, file position: 0
I (1118) PLAY_SDCARD_MUSIC: CB: RECV Pipeline EVT: el: NULL-0x3c15131c, type: 2000, sub: ESP_GMF_EVENT_STATE_OPENING, payload: 0x0, size: 0, 0x3fced37c
I (1132) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151b34, ctx:0x3c151360, label:aud_dec_open]
I (1141) ESP_GMF_PORT: ACQ IN, new self payload:0x3c151b34, port:0x3c151990, el:0x3c151360-aud_dec
I (1150) ESP_ES_PARSER: The verion of es_parser is v1.0.0
W (1155) ESP_GMF_ASMP_DEC: Not enough memory for out, need:4608, old: 1024, new: 4608
I (1165) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151df8, ctx:0x3c151480, label:aud_ch_cvt_open]
I (1172) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151e48, ctx:0x3c1515d8, label:aud_bit_cvt_open]
I (1181) PLAY_SDCARD_MUSIC: CB: RECV Pipeline EVT: el: aud_rate_cvt-0x3c15172c, type: 3000, sub: ESP_GMF_EVENT_STATE_INITIALIZED, payload: 0x3fcecfd0, size: 16, 0x3fced37c
I (1196) PLAY_SDCARD_MUSIC: CB: RECV Pipeline EVT: el: aud_rate_cvt-0x3c15172c, type: 2000, sub: ESP_GMF_EVENT_STATE_RUNNING, payload: 0x0, size: 0, 0x3fced37c
I (1210) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151df8, ctx:0x3c15172c, label:aud_rate_cvt_open]
I (21180) ESP_GMF_FILE: No more data, ret: 0
I (21198) ESP_GMF_TASK: Job is done, [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x3c151b70, job:0x3c151360-aud_dec_proc]
I (21198) ESP_GMF_TASK: Job is done, [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x3c151e20, job:0x3c151480-aud_ch_cvt_proc]
I (21207) ESP_GMF_TASK: Job is done, [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x3c151e88, job:0x3c1515d8-aud_bit_cvt_proc]
I (21217) ESP_GMF_TASK: Job is done, [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x3c151ee0, job:0x3c15172c-aud_rate_cvt_proc]
I (21228) ESP_GMF_TASK: Finish, strategy action: 0, [tsk:0x3fcec084-TSK_0x3fcec084]
I (21235) ESP_GMF_FILE: CLose, 0x3c151888, pos = 231023/231023
I (21241) ESP_GMF_CODEC_DEV: CLose, 0x3c1519d0, pos = 3528000/0
I (21246) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151b58, ctx:0x3c151360, label:aud_dec_close]
I (21256) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151b80, ctx:0x3c151480, label:aud_ch_cvt_close]
I (21265) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151df8, ctx:0x3c1515d8, label:aud_bit_cvt_close]
I (21275) ESP_GMF_TASK: One times job is complete, del[wk:0x3c151e38, ctx:0x3c15172c, label:aud_rate_cvt_close]
I (21285) PLAY_SDCARD_MUSIC: CB: RECV Pipeline EVT: el: NULL-0x3c15131c, type: 2000, sub: ESP_GMF_EVENT_STATE_FINISHED, payload: 0x0, size: 0, 0x3fced37c
I (21298) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x0, run:0]
I (21306) ESP_GMF_TASK: Waiting to run... [tsk:TSK_0x3fcec084-0x3fcec084, wk:0x0, run:0]
I (21314) PLAY_SDCARD_MUSIC: [ 6 ] Destroy all the resources
W (21319) GMF_SETUP_AUD_CODEC: Unregistering default decoder
I (21330) BOARD_DEVICE: Deinit device audio_dac ref_count: 0 device_handle:0x3fcea024
I (21337) BOARD_DEVICE: Device audio_dac config found: 0x3c0de0d4 (size: 92)
I (21339) BOARD_PERIPH: Deinit peripheral i2s_audio_out ref_count: 0
E (21345) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
W (21353) PERIPH_I2S: Caution: Releasing TX (0x0).
W (21357) PERIPH_I2S: Caution: RX (0x3c14fca0) forced to stop.
E (21363) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
I (21370) BOARD_PERIPH: Deinit peripheral i2c_master ref_count: 0
I (21376) PERIPH_I2C: I2C master bus deinitialized successfully
I (21382) BOARD_MANAGER: Device audio_dac deinitialized
I (21387) BOARD_DEVICE: Deinit device fs_sdcard ref_count: 0 device_handle:0x3fce9a7c
I (21394) BOARD_DEVICE: Device fs_sdcard config found: 0x3c0de024 (size: 84)
I (21401) DEV_FS_FAT: Sub device 'sdmmc' deinitialized successfully
I (21407) BOARD_MANAGER: Device fs_sdcard deinitialized
I (21412) main_task: Returned from app_main()
```
## 故障排除
### 音频文件未找到或无法播放
若日志出现 `FILE_IO: The error is happened in reading data` 或 `Bad file number`,多为 microSD 卡中未找到对应文件。请确认已将音源重命名为 `test` 并带正确扩展名(如 `test.mp3`、`test.wav`)存入卡根目录,且卡已正确挂载。
## 技术支持
请按照下面的链接获取技术支持:
- 技术支持参见 [esp32.com](https://esp32.com/viewforum.php?f=20) 论坛
- 问题反馈与功能需求,请创建 [GitHub issue](https://github.com/espressif/esp-gmf/issues)
我们会尽快回复。
To create a project from this example, run:
idf.py create-project-from-example "espressif/gmf_examples=1.0.0~1:pipeline_play_sdcard_music"