pipeline_loop_play_no_gap

Example of the component espressif/gmf_examples v0.8.0
# 无缝循环播放音乐

- [English Version](./README.md)
- 例程难度:⭐

## 例程简介

本示例展示了如何使用 GMF 任务策略函数实现 microSD 卡中音乐文件的无缝循环播放。

- 支持播放完当前文件后通过策略回调自动切换下一首,无需停止管道,避免循环时的停顿或间隙。
- 支持给定时间后停止(默认播放 60 秒后停止)。
- 本示例使用单管道架构:`io_file` → `aud_dec` → 声道/位深/采样率转换 → `io_codec_dev`,并通过 `esp_gmf_task_set_strategy_func` 注册策略函数,在播放完成时切换播放列表中的下一个文件。

### 典型场景

- 背景音乐、播报列表等需要多文件连续、无间隙循环播放的场景。

### 运行机制

- 架构原理

```mermaid
flowchart LR
    subgraph 单管道架构["单管道架构"]
        direction LR
        A1[io_file] --> A2[aud_dec] --> A3[aud_ch_cvt] --> A4[aud_bit_cvt] --> A5[aud_rate_cvt] --> A6[io_codec_dev] --> A7[🔊 speaker]
    end
```

- 策略函数工作流程

```mermaid
flowchart TD
    A[管道运行中] --> B{所有 Jobs 完成?}
    B -->|否| A
    B -->|是| C[调用策略函数]
    C --> D{stop_loop == false?}
    D -->|是| E[返回 RESET 动作]
    E --> F[关闭 IO]
    F --> G[重置 IO]
    G --> H[设置新 URI]
    H --> I[打开 IO]
    I --> J[继续播放]
    J --> A
    D -->|否| K[返回 DEFAULT 动作]
    K --> L[管道结束]
```

- 循环播放时序图

```mermaid
gantt
    title 无间隙循环播放时序
    dateFormat YYYY-MM-DD HH:mm:ss
    axisFormat %M:%S

    section Pipeline
    播放文件 1 :active, 2025-01-01 00:00:00, 2025-01-01 00:00:20
    播放文件 2 :active, 2025-01-01 00:00:20, 2025-01-01 00:00:40
    播放文件 1 :active, 2025-01-01 00:00:40, 2025-01-01 00:01:00
    播放文件 2 :active, 2025-01-01 00:01:00, 2025-01-01 00:01:20

    section 音频输出
    连续输出 :active, 2025-01-01 00:00:00, 2025-01-01 00:01:20
```

## 环境配置

### 硬件要求

- **开发板**:默认以 ESP32-S3-Korvo V3 为例,其他 ESP 音频板同样适用。
- **资源要求**:microSD 卡、Audio DAC、扬声器。

### 默认 IDF 分支

本例程支持 IDF release/v5.4 (>= v5.4.3) 与 release/v5.5 (>= v5.5.2) 分支。

### 软件要求

- 在 microSD 卡根目录放置与播放列表一致的音频文件(默认需 `test.mp3`、`test_short.mp3`)。
- 可通过修改代码中的 `play_urls[]` 和 `PLAYBACK_DURATION_MS` 调整列表与播放时长。

## 编译和下载

### 编译准备

编译本例程前需先确保已配置 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_loop_play_no_gap
```

- 执行预编译脚本,根据提示选择编译芯片,自动设置 IDF Action 扩展,通过 `esp_board_manager` 选择支持的开发板,如需选择自定义开发板,详情参考:[自定义板子](https://github.com/espressif/esp-gmf/blob/main/packages/esp_board_manager/README.md#custom-board)

在 Linux / macOS 中运行以下命令:
```bash/zsh
source prebuild.sh
```

在 Windows 中运行以下命令:
```powershell
.\prebuild.ps1
```

### 项目配置

- 若需调整音频效果相关参数(采样率、声道、位深等),可在 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 卡并初始化音频输出,在指定时间内(默认 60 秒,由 `PLAYBACK_DURATION_MS` 控制)循环播放 `play_urls[]` 中的文件。到达时间后设置停止标志,当前文件播完后管道停止并释放资源。播放过程中可听到文件之间的无缝切换。
- 播放列表与时长可在 `main/play_music_without_gap.c` 中修改 `play_urls[]` 和 `PLAYBACK_DURATION_MS`。

### 日志输出

- 正常流程会依次打印:注册元素与设置音频信息、创建 pipeline、绑定任务与加载 jobs、监听事件、启动 pipeline、循环播放各文件,最后等待停止事件并销毁资源。关键步骤以 `[ 1 ]`~`[ 6 ]` 及 `Play file:` 标出。

```c
I (914) main_task: Calling app_main()
I (915) PLAY_MUSIC_NO_GAP: [ 1 ] Mount sdcard and setup audio codec
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 (994) PLAY_MUSIC_NO_GAP: [ 2 ] Register all the elements and set audio information to play codec device
I (996) PLAY_MUSIC_NO_GAP: [ 3 ] Create audio pipeline
I (998) PLAY_MUSIC_NO_GAP: [ 3.1 ] Create gmf task, bind task to pipeline and load linked element jobs to the bind task
I (1009) PLAY_MUSIC_NO_GAP: [ 3.2 ] Create event group and listen events from pipeline
I (1016) PLAY_MUSIC_NO_GAP: [ 4 ] Start audio_pipeline
I (1021) PLAY_MUSIC_NO_GAP: [ 4.1 ] Playing 60000ms before change strategy
I (1024) PLAY_MUSIC_NO_GAP: CB: RECV Pipeline EVT: el: NULL-0x3c128d18, type: 2000, sub: ESP_GMF_EVENT_STATE_OPENING, payload: 0x0, size: 0, 0x3fcec33c
W (1044) ESP_GMF_ASMP_DEC: Not enough memory for out, need:2304, old: 1024, new: 2304
I (1191) PLAY_MUSIC_NO_GAP: CB: RECV Pipeline EVT: el: aud_rate_cvt-0x3c129118, type: 3000, sub: ESP_GMF_EVENT_STATE_INITIALIZED, payload: 0x3c12a2e0, size: 16, 0x3fcec33c
I (1195) PLAY_MUSIC_NO_GAP: CB: RECV Pipeline EVT: el: aud_rate_cvt-0x3c129118, type: 2000, sub: ESP_GMF_EVENT_STATE_RUNNING, payload: 0x0, size: 0, 0x3fcec33c
I (8873) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test_short.mp3
I (16586) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test.mp3
I (24308) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test_short.mp3
I (32018) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test.mp3
I (39731) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test_short.mp3
I (47453) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test.mp3
I (55166) PLAY_MUSIC_NO_GAP: Play file: /sdcard/test_short.mp3
I (61043) PLAY_MUSIC_NO_GAP: [ 5 ] Wait stop event to the pipeline and stop all the pipeline
I (62885) PLAY_MUSIC_NO_GAP: CB: RECV Pipeline EVT: el: NULL-0x3c128d18, type: 2000, sub: ESP_GMF_EVENT_STATE_FINISHED, payload: 0x0, size: 0, 0x3fcec33c
I (62888) PLAY_MUSIC_NO_GAP: [ 6 ] Destroy all the resources
W (62894) GMF_SETUP_AUD_CODEC: Unregistering default decoder
E (62909) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
W (62910) PERIPH_I2S: Caution: Releasing TX (0x0).
W (62911) PERIPH_I2S: Caution: RX (0x3c127fb4) forced to stop.
E (62916) i2s_common: i2s_channel_disable(1262): the channel has not been enabled yet
```

## 故障排除

### 音频文件未找到

若日志出现如下错误,请确认 microSD 卡根目录下存在播放列表中的文件(默认需 `test.mp3`、`test_short.mp3`),且 `play_urls[]` 中的路径与卡内文件名一致:

```c
E (1133) ESP_GMF_FILE: Failed to open on read, path: /sdcard/test.mp3, err: No such file or directory
E (1140) ESP_GMF_IO: esp_gmf_io_open(71): esp_gmf_io_open failed
```

### 音频格式不支持

- 请使用例程支持的格式(如 MP3、FLAC、AAC 等),播放列表中所有文件需为同一格式。可参考 [esp_audio_codec](https://github.com/espressif/esp-adf-libs/tree/master/esp_audio_codec)。

## 技术支持

请按照下面的链接获取技术支持:

- 技术支持参见 [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=0.8.0:pipeline_loop_play_no_gap"

or download archive (~19.99 KB)