pipeline_howl

Example of the component espressif/gmf_examples v1.0.0
# Pipeline HOWL(啸叫抑制)

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

## 示例说明

本示例展示了一个卡拉 OK 音频处理流程:采用 **三条 GMF pipeline**,**两路音源**(伴奏、麦克风)分别处理后,经 ringbuf 送入混音 pipeline,由 `aud_mixer` 混音后经 DAC 播放。

- 伴奏 pipeline:`io_file`(SDCard)→ `aud_dec` → `aud_rate_cvt` → `aud_bit_cvt` → `aud_ch_cvt`
- 麦克风 pipeline:`io_codec_dev`(ADC)→ `aud_howl`
- 混音输出 pipeline:`aud_mixer` → `io_codec_dev`(DAC)

最终混音播放格式为 **16 kHz / 单声道 / 16 bit**。

### 典型场景

- KTV/卡拉 OK 伴奏 + 人声实时混音
- 麦克风近场扩声场景中的啸叫抑制验证
- 多路音频 pipeline 连接(databus/ringbuf)参考示例

## 环境配置

### 硬件要求

- **开发板**:ESP32-S3-Korvo V3 等带音频 ADC + DAC 的 ESP 音频开发板。
- **外设**:麦克风、扬声器、SD 卡。

### 默认 IDF 版本

与其它 GMF 基础示例一致,支持 IDF release/v5.4(≥ v5.4.3)与 release/v5.5(≥ v5.5.2)。

### 软件要求

- 准备一张 microSD 卡,将自备音源重命名为 `test`(扩展名与格式一致,如 `test.mp3`)并存入卡中。可通过 `DEFAULT_PLAY_URL` 更改播放文件路径。支持伴奏音频格式为 MP3、WAV、FLAC、AAC、M4A、TS、AMRNB、AMRWB 等,默认 MP3。
- 若需修改最终播放格式,可在 `main/pipeline_howl.c` 调整:
  - `HOWL_SAMPLE_RATE`
  - `HOWL_CHANNELS`
  - `HOWL_BITS`

## 编译和下载

### 编译准备

编译前请先完成 ESP-IDF 环境配置;若已配置可跳过。

```bash
./install.sh
. ./export.sh
```

下面是简略步骤:

- 进入本例程目录:

```bash
cd $YOUR_GMF_PATH/gmf_examples/basic_examples/pipeline_howl
```

本示例使用 [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
ℹ️  Main Boards:
  [1] dual_eyes_board_v1_0
  [2] esp32_c3_lyra
  [3] esp32_c5_spot
  [4] esp32_p4_function_ev
  [5] esp32_s3_korvo2_v3
  [6] esp32_s3_korvo2l
  [7] esp_box_3
  [8] esp_box_lite
  [9] esp_hi
```

- 选择开发板:

```bash
idf.py bmgr -b <board_index|board_name>
```

例如选择 `esp32_s3_korvo2_v3`:

```bash
idf.py bmgr -b 5
# 或
idf.py bmgr -b esp32_s3_korvo2_v3
```

> [!NOTE]
> 如果切换为其他 `esp_board_manager` 支持的开发板,请按相同步骤执行并替换板型名称/索引。
> 自定义开发板请参考 [自定义开发板指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/docs/how_to_customize_board_cn.md)。
> `esp_board_manager` 更多信息请参考 [ESP_BOARD_MANAGER 入门指南](https://github.com/espressif/esp-board-manager/blob/main/esp_board_manager/README_CN.md)

### 项目配置(可选)

- 若需调整音频效果相关参数(mixer的weight, howl的判别参数等),可在 menuconfig 中配置 GMF Audio 相关选项:

```bash
idf.py menuconfig
```

在 menuconfig 中进行以下配置:

1) 关于 HOWL 的配置
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `HOWL PAPR Threshold × 10`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `HOWL PHPR Threshold × 10`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `HOWL PNPR Threshold × 10`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `HOWL IMSD Threshold × 10`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Enable HOWL IMSD`

2) 关于 MIXER 的配置
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Source1 Parameters`
- `ESP GMF Loader` → `GMF Audio Configurations` → `GMF Audio Effects` → `Source12 Parameters`

> 配置完成后按 `s` 保存,然后按 `Esc` 退出。

### SD 卡音乐文件

- 请将伴奏文件放到 SD 卡,并在 `main/pipeline_howl.c` 中设置:
  - `HOWL_MUSIC_URI`
- 当前代码默认路径:`/sdcard/test.mp3`

## 编译与烧录

- 编译示例程序

```
idf.py build
```

- 烧录程序并运行 monitor 工具来查看串口输出 (替换 PORT 为端口名称):

```
idf.py -p PORT flash monitor
```

- 退出调试界面使用 `Ctrl-]`

## 如何使用例程

### 功能和用法

- 上电后示例会初始化 DAC、ADC 与 SD 卡,创建三条 pipeline,并自动开始:
  - 伴奏解码 + 重采样/位深/声道转换
  - 麦克风采集 + HOWL 处理
  - 混音后播放
- 当伴奏文件播放结束后,示例会停止全部 pipeline 并释放资源。

### 日志输出

- 正常流程会依次打印挂载 SD 卡、注册元素、创建 pipeline、设置 url、绑定任务、监听事件、启动 pipeline、等待结束并销毁资源。关键步骤以 `[ 1 ]`~`[ 7 ]` 标出。

```c
I (906) main_task: Calling app_main()
I (908) PIPELINE_HOWL: [ 1 ] Init peripherals
I (913) PERIPH_I2S: I2S[0] STD,  TX, ws: 45, bclk: 9, dout: 8, din: 10
I (918) PERIPH_I2S: I2S[0] initialize success: 0x3c1709bc
I (924) DEV_AUDIO_CODEC: DAC is ENABLED
I (927) PERIPH_I2C: I2C master bus initialized successfully
I (938) ES8311: Work in Slave mode
I (941) DEV_AUDIO_CODEC: Successfully initialized codec: audio_dac
I (941) DEV_AUDIO_CODEC: Create esp_codec_dev success, dev:0x3fceb574, chip:es8311
I (949) BOARD_MANAGER: Device audio_dac initialized
I (953) BOARD_DEVICE: Device handle audio_dac found, Handle: 0x3fce9a80 TO: 0x3fce9a80
I (961) I2S_IF: No paired data, current mode: playback
I (966) I2S_IF: STD: TX, data_bit: 16, slot_bit: 16, ws_width: 16, slot_mode: MONO, slot_mask: 0x1
I (974) I2S_IF: STD: TX, sample_rate_hz: 16000, mclk_multiple: 256, clk_src: 6
I (997) Adev_Codec: Open codec device OK
I (997) PERIPH_I2S: I2S[0] STD, RX, ws: 45, bclk: 9, dout: 8, din: 10
I (997) PERIPH_I2S: I2S[0] initialize success: 0x3c170b78
I (1001) DEV_AUDIO_CODEC: ADC over I2S is enabled
I (1005) BOARD_PERIPH: Reuse periph: i2c_master, ref_count=2
I (1016) ES8311: Work in Slave mode
I (1019) DEV_AUDIO_CODEC: Successfully initialized codec: audio_adc
I (1020) DEV_AUDIO_CODEC: Create esp_codec_dev success, dev:0x3fceb0f4, chip:es8311
I (1027) BOARD_MANAGER: Device audio_adc initialized
I (1032) BOARD_DEVICE: Device handle audio_adc found, Handle: 0x3fcea6e0 TO: 0x3fcea6e0
I (1040) I2S_IF: Current mode(record) and peer mode have same sample_rate 16000, channel 2, bits_per_sample 16
I (1049) I2S_IF: STD: RX, data_bit: 16, slot_bit: 16, ws_width: 16, slot_mode: MONO, slot_mask: 0x1
I (1058) I2S_IF: STD: RX, sample_rate_hz: 16000, mclk_multiple: 256, clk_src: 6
I (1079) Adev_Codec: Open codec device OK
I (1079) DEV_FS_FAT_SUB_SDMMC: slot_config: cd=-1, wp=-1, clk=6, cmd=7, d0=4, d1=-1, d2=-1, d3=-1, d4=-1, d5=-1, d6=-1, d7=-1, width=1, flags=0x1
W (1128) SD_HOST: input line delay not supported, fallback to 0 delay
Name: SD16G
Type: SDHC
Speed: 40.00 MHz (limit: 40.00 MHz)
Size: 15238MB
CSD: ver=2, sector_size=512, capacity=31207424 read_bl_len=9
SSR: bus_width=1
I (1136) DEV_FS_FAT: Filesystem mounted, base path: /sdcard
I (1142) BOARD_MANAGER: Device fs_sdcard initialized
I (1146) PIPELINE_HOWL: [ 2 ] Register pool
I (1151) ESP_GMF_POOL: Registered items on pool:0x3c172a24, app_main-165
I (1157) ESP_GMF_POOL: IO, Item:0x3c172ab0, H:0x3c172cc4, TAG:io_codec_dev
I (1163) ESP_GMF_POOL: IO, Item:0x3c172e50, H:0x3c172d80, TAG:io_codec_dev
I (1170) ESP_GMF_POOL: IO, Item:0x3c172f6c, H:0x3c172e60, TAG:io_file
I (1176) ESP_GMF_POOL: IO, Item:0x3c173088, H:0x3c172f7c, TAG:io_file
I (1182) ESP_GMF_POOL: IO, Item:0x3c1731a8, H:0x3c173098, TAG:io_http
I (1188) ESP_GMF_POOL: IO, Item:0x3c1732b8, H:0x3c1731b8, TAG:io_embed_flash
I (1195) ESP_GMF_POOL: EL, Item:0x3c1733d8, H:0x3c1732c8, TAG:aud_dec
I (1201) ESP_GMF_POOL: EL, Item:0x3c1734d4, H:0x3c1733e8, TAG:aud_alc
I (1207) ESP_GMF_POOL: EL, Item:0x3c1735bc, H:0x3c1734e4, TAG:aud_ch_cvt
I (1214) ESP_GMF_POOL: EL, Item:0x3c1736a0, H:0x3c1735cc, TAG:aud_bit_cvt
I (1220) ESP_GMF_POOL: EL, Item:0x3c17378c, H:0x3c1736b0, TAG:aud_rate_cvt
I (1227) ESP_GMF_POOL: EL, Item:0x3c1738bc, H:0x3c17379c, TAG:aud_mixer
I (1233) ESP_GMF_POOL: EL, Item:0x3c1739d0, H:0x3c1738cc, TAG:aud_howl
I (1240) PIPELINE_HOWL: [ 3 ] Build pipelines
I (1244) PIPELINE_HOWL: [ 3.1 ] Config music source and decoder
I (1249) PIPELINE_HOWL: [ 3.2 ] Config howl parameters
I (1254) PIPELINE_HOWL: [ 3.3 ] Config mixer parameters
I (1259) PIPELINE_HOWL: [ 3.4 ] Connect music/mic pipelines to mixer
I (1265) NEW_DATA_BUS: New ringbuffer:0x3c174660, num:10, item_cnt:1024, db:0x3c176e8c
I (1273) NEW_DATA_BUS: New ringbuffer:0x3c176ef8, num:10, item_cnt:1024, db:0x3c179724
I (1280) PIPELINE_HOWL: [ 4 ] Create tasks and load jobs
I (1286) ESP_GMF_TASK: Waiting to run... [tsk:howl_music-0x3fcec480, wk:0, run:0]
I (1293) ESP_GMF_TASK: Waiting to run... [tsk:howl_music-0x3fcec480, wk:0x3c1798d0, run:0]
I (1301) ESP_GMF_TASK: Waiting to run... [tsk:howl_mic-0x3fced67c, wk:0, run:0]
W (1301) ESP_GMF_PIPELINE: Element[aud_howl-0x3c1740cc] not ready to register job, ret:0xffffdff8
I (1316) ESP_GMF_TASK: Waiting to run... [tsk:howl_mix-0x3fcee878, wk:0, run:0]
W (1323) ESP_GMF_PIPELINE: Element[aud_mixer-0x3c174350] not ready to register job, ret:0xffffdff8
I (1332) ESP_GMF_TASK: Waiting to run... [tsk:howl_mic-0x3fced67c, wk:0x3c179998, run:0]
I (1332) ESP_GMF_TASK: Waiting to run... [tsk:howl_mix-0x3fcee878, wk:0x3c179a20, run:0]
I (1332) PIPELINE_HOWL: [ 5 ] Run howl pipelines
I (1352) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: NULL-0x3c17430c, type: 2000, sub: ESP_GMF_EVENT_STATE_OPENING, payload: 0, size: 0, ctx:0x3fceeac0
I (1365) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_mixer-0x3c174350, type: 3000, sub: ESP_GMF_EVENT_STATE_INITIALIZED, payload: 0x3fcaaf70, size: 16, ctx:0x3fceeac0
I (1380) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_mixer-0x3c174350, type: 2000, sub: ESP_GMF_EVENT_STATE_RUNNING, payload: 0, size: 0, ctx:0x3fceeac0
I (1393) ESP_GMF_TASK: One times job is complete, del[wk:0x3c179a20, ctx:0x3c174350, label:aud_mixer_open]
I (1403) ESP_GMF_PORT: ACQ IN, new self payload:0x3c179a20, port:0x3c1797d0, el:0x3c174350-aud_mixer
I (1412) ESP_GMF_FILE: Open, dir:1, uri:/sdcard/test.mp3
I (1417) PIPELINE_HOWL: [ 6 ] Wait music end then stop all pipelines
I (1417) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: -0x3c174088, type: 2000, sub: ESP_GMF_EVENT_STATE_OPENING, payload: 0, size: 0, ctx:0x3fceea98
I (1442) ESP_GMF_PORT: ACQ IN, new self payload:0x3c17aa04, port:0x3c179860, el:0x3c174350-aud_mixer
I (1447) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_howl-0x3c1740cc, type: 3000, sub: ESP_GMF_EVENT_STATE_INITIALIZED, payload: 0x3fcee590, size: 16, ctx:0x3fceea98
I (1448) ESP_GMF_FILE: File size: 3664281 byte, file position: 0
I (1459) ESP_GMF_HOWL: Howl opened, 0x3c1740cc, frame_bytes 1024
I (1465) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: -0x3c1739e0, type: 2000, sub: ESP_GMF_EVENT_STATE_OPENING, payload: 0, size: 0, ctx:0x3fceea70
I (1471) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_howl-0x3c1740cc, type: 2000, sub: ESP_GMF_EVENT_STATE_RUNNING, payload: 0, size: 0, ctx:0x3fceea98
I (1483) ESP_GMF_TASK: One times job is complete, del[wk:0x3c1798d0, ctx:0x3c173a24, label:aud_dec_open]
I (1497) ESP_GMF_TASK: One times job is complete, del[wk:0x3c179998, ctx:0x3c1740cc, label:aud_howl_open]
I (1506) ESP_GMF_PORT: ACQ IN, new self payload:0x3c1798d0, port:0x3c174048, el:0x3c173a24-aud_dec
I (1515) ESP_GMF_PORT: ACQ IN, new self payload:0x3c179998, port:0x3c1742cc, el:0x3c1740cc-aud_howl
I (1525) ESP_ES_PARSER: The version of es_parser is v1.0.1
W (1539) ESP_GMF_ASMP_DEC: Not enough memory for out, need:4608, old: 1024, new: 4608
I (1689) ESP_GMF_TASK: One times job is complete, del[wk:0x3c17ac1c, ctx:0x3c173b34, label:aud_rate_cvt_open]
I (1690) ESP_GMF_TASK: One times job is complete, del[wk:0x3c184260, ctx:0x3c173c90, label:aud_bit_cvt_open]
I (1697) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_ch_cvt-0x3c173de4, type: 3000, sub: ESP_GMF_EVENT_STATE_INITIALIZED, payload: 0x3fced3a0, size: 16, ctx:0x3fceea70
I (1712) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: aud_ch_cvt-0x3c173de4, type: 2000, sub: ESP_GMF_EVENT_STATE_RUNNING, payload: 0, size: 0, ctx:0x3fceea70
I (1726) ESP_GMF_TASK: One times job is complete, del[wk:0x3c1842b0, ctx:0x3c173de4, label:aud_ch_cvt_open]
I (230320) ESP_GMF_FILE: No more data, ret: 0
I (230322) ESP_GMF_TASK: Job is done, [tsk:howl_music-0x3fcec480, wk:0x3c17990c, job:0x3c173a24-aud_dec_proc]
I (230324) ESP_GMF_TASK: Job is done, [tsk:howl_music-0x3fcec480, wk:0x3c183f24, job:0x3c173b34-aud_rate_cvt_proc]
I (230333) ESP_GMF_TASK: Job is done, [tsk:howl_music-0x3fcec480, wk:0x3c184288, job:0x3c173c90-aud_bit_cvt_proc]
I (230376) ESP_GMF_TASK: Job is done, [tsk:howl_music-0x3fcec480, wk:0x3c1842d8, job:0x3c173de4-aud_ch_cvt_proc]
I (230376) ESP_GMF_TASK: Finish, strategy action: 0, [tsk:0x3fcec480-howl_music]
I (230382) ESP_GMF_FILE: CLose, 0x3c173f3c, pos = 3664281/3664281
I (230388) ESP_GMF_TASK: One times job is complete, del[wk:0x3c183f24, ctx:0x3c173a24, label:aud_dec_close]
I (230398) ESP_GMF_TASK: One times job is complete, del[wk:0x3c184260, ctx:0x3c173b34, label:aud_rate_cvt_close]
I (230407) ESP_GMF_TASK: One times job is complete, del[wk:0x3c184288, ctx:0x3c173c90, label:aud_bit_cvt_close]
I (230417) ESP_GMF_TASK: One times job is complete, del[wk:0x3c1798f4, ctx:0x3c173de4, label:aud_ch_cvt_close]
I (230427) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: -0x3c1739e0, type: 2000, sub: ESP_GMF_EVENT_STATE_FINISHED, payload: 0, size: 0, ctx:0x3fceea70
I (230440) ESP_GMF_TASK: Waiting to run... [tsk:howl_music-0x3fcec480, wk:0, run:0]
I (230447) ESP_GMF_TASK: Waiting to run... [tsk:howl_music-0x3fcec480, wk:0, run:0]
I (230463) ESP_GMF_CODEC_DEV: CLose, 0x3c1741d0, pos = 7327744/0
I (230463) ESP_GMF_TASK: One times job is complete, del[wk:0x3c1799bc, ctx:0x3c1740cc, label:aud_howl_close]
I (230470) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: -0x3c174088, type: 2000, sub: ESP_GMF_EVENT_STATE_STOPPED, payload: 0, size: 0, ctx:0x3fceea98
I (230483) ESP_GMF_TASK: Waiting to run... [tsk:howl_mic-0x3fced67c, wk:0, run:0]
I (230490) ESP_GMF_TASK: Waiting to run... [tsk:howl_mic-0x3fced67c, wk:0, run:0]
I (230496) ESP_GMF_CODEC_DEV: CLose, 0x3c174470, pos = 7328640/0
I (230503) ESP_GMF_TASK: One times job is complete, del[wk:0x3c179a44, ctx:0x3c174350, label:aud_mixer_close]
I (230513) PIPELINE_HOWL: CB: RECV Pipeline EVT: el: NULL-0x3c17430c, type: 2000, sub: ESP_GMF_EVENT_STATE_STOPPED, payload: 0, size: 0, ctx:0x3fceeac0
I (230526) ESP_GMF_TASK: Waiting to run... [tsk:howl_mix-0x3fcee878, wk:0, run:0]
I (230533) ESP_GMF_TASK: Waiting to run... [tsk:howl_mix-0x3fcee878, wk:0, run:0]
I (230540) PIPELINE_HOWL: [ 7 ] Destroy resources
W (230545) GMF_SETUP_AUD_CODEC: Unregistering default decoder
I (230551) BOARD_DEVICE: Deinit device fs_sdcard ref_count: 0 device_handle:0x3fceb144
I (230558) BOARD_DEVICE: Device fs_sdcard config found: 0x3c1107dc (size: 84)
I (230565) DEV_FS_FAT: Sub device 'sdmmc' deinitialized successfully
I (230571) BOARD_MANAGER: Device fs_sdcard deinitialized
I (230577) I2S_IF: Pending out channel for in channel running
I (230581) BOARD_DEVICE: Deinit device audio_dac ref_count: 0 device_handle:0x3fce9a80
I (230590) BOARD_DEVICE: Device audio_dac config found: 0x3c1109b0 (size: 384)
I (230596) BOARD_PERIPH: Deinit peripheral i2s_audio_out ref_count: 0
W (230602) PERIPH_I2S: Caution: Releasing TX (0).
I (230607) BOARD_PERIPH: Deinit peripheral i2c_master ref_count: 1
W (230612) BOARD_PERIPH: Peripheral i2c_master still has 1 references, not deinitializing
I (230620) BOARD_MANAGER: Device audio_dac deinitialized
I (230630) BOARD_DEVICE: Deinit device audio_adc ref_count: 0 device_handle:0x3fcea6e0
I (230639) BOARD_DEVICE: Device audio_adc config found: 0x3c110830 (size: 384)
I (230640) BOARD_PERIPH: Deinit peripheral i2s_audio_in ref_count: 0
E (230646) i2s_common: i2s_channel_disable(1290): the channel has not been enabled yet
W (230654) PERIPH_I2S: Caution: Releasing RX (0).
I (230658) BOARD_PERIPH: Deinit peripheral i2c_master ref_count: 0
I (230664) PERIPH_I2C: I2C master bus deinitialized successfully
I (230670) BOARD_MANAGER: Device audio_adc deinitialized
I (230675) main_task: Returned from app_main()
```

## 故障排查

### SD 卡文件找不到或无法播放

- 检查 SD 卡是否挂载成功;
- 检查 `HOWL_MUSIC_URI` 路径与文件扩展名是否匹配;
- 可先用 `pipeline_play_sdcard_music` 示例验证板级 SD 卡与解码链路。

### 麦克风路超时(`HOWL_mic` timeout)

- 检查 ADC 是否初始化成功;
- 确认麦克风路上报的 `ESP_GMF_INFO_SOUND` 与采集参数一致(本示例为 16k/1ch/16bit);
- 确认 HOWL 已启用且正常打开;
- 确认混音输入端未出现长期阻塞(ringbuf 连接和等待参数)。

## 技术支持

- 论坛:[esp32.com](https://esp32.com/viewforum.php?f=20)
- 问题反馈:[esp-gmf GitHub Issues](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:pipeline_howl"

or download archive (~17.69 KB)