esp-adf
esp-adf copied to clipboard
pipeline_flash_tone例程在播放提示音时总会有POP音 (AUD-6512)
ESP-IDF:5.4.2 ESP-ADF:master 开发板:ESP32-Lyrat-Mini V1.2
使用的例程没有任何修改,flashTone分区烧录的例程目录下./tools/audio-esp.bin
在播放前或播放后总是会有一声POP音啪的一声,严重时会导致播放的第一个字完全听不见
应该如何处理这种情况?
建议默认是一个 关PA状态, 开始播放的时候开 PA, 播放结束再关PA
@shootao 在什么时机去做PA的开关比较合适?我这里尝试了但是没有怎么改善
// tone_player.c
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/queue.h"
#include "audio_pipeline.h"
#include "audio_event_iface.h"
#include "audio_element.h"
#include "tone_stream.h"
#include "mp3_decoder.h"
#include "i2s_stream.h"
#include "board.h"
#include "esp_log.h"
#include "es8311.h"
#include "tone_player.h"
#define TAG "TONE_PLAYER"
#define TONE_QUEUE_LEN 8
typedef struct
{
tone_type_t tone;
tone_cb_t callback;
bool preemptive;
} tone_request_t;
const char *tone_uri[] = {
"flash://tone/0_try.mp3",
};
static QueueHandle_t tone_queue = NULL;
static TaskHandle_t tone_task_handle = NULL;
static audio_pipeline_handle_t pipeline = NULL;
static audio_event_iface_handle_t evt = NULL;
static audio_element_handle_t tone_stream = NULL, mp3_decoder = NULL, i2s_stream = NULL;
static tone_request_t current_playing;
static bool is_playing = false;
static void stop_pipeline(void)
{
audio_pipeline_stop(pipeline);
audio_pipeline_wait_for_stop(pipeline);
audio_pipeline_reset_ringbuffer(pipeline);
audio_pipeline_terminate(pipeline);
}
static void prepare_and_start_playback(tone_request_t *req)
{
audio_element_set_uri(tone_stream, tone_uri[req->tone]);
audio_pipeline_reset_ringbuffer(pipeline);
audio_pipeline_reset_elements(pipeline);
audio_pipeline_run(pipeline);
is_playing = true;
}
static void tone_play_task(void *arg)
{
tone_request_t req;
while (1)
{
if (!is_playing && xQueueReceive(tone_queue, ¤t_playing, portMAX_DELAY) == pdTRUE)
{
prepare_and_start_playback(¤t_playing);
}
audio_event_iface_msg_t msg;
if (audio_event_iface_listen(evt, &msg, 100 / portTICK_PERIOD_MS) == ESP_OK)
{
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT &&
msg.source == (void *)mp3_decoder &&
msg.cmd == AEL_MSG_CMD_REPORT_MUSIC_INFO)
{
audio_element_info_t music_info = {0};
audio_element_getinfo(mp3_decoder, &music_info);
ESP_LOGI(TAG, "[ * ] Receive music info from mp3 decoder, sample_rates=%d, bits=%d, ch=%d",
music_info.sample_rates, music_info.bits, music_info.channels);
i2s_stream_set_clk(i2s_stream, music_info.sample_rates, music_info.bits, music_info.channels);
// 播放前打开PA
es8311_pa_power(true);
}
if (msg.source_type == AUDIO_ELEMENT_TYPE_ELEMENT &&
msg.source == (void *)i2s_stream &&
msg.cmd == AEL_MSG_CMD_REPORT_STATUS &&
((int)msg.data == AEL_STATUS_STATE_STOPPED || (int)msg.data == AEL_STATUS_STATE_FINISHED))
{
// 播放完成先关PA再关管道
es8311_pa_power(false);
stop_pipeline();
if (current_playing.callback)
current_playing.callback();
is_playing = false;
continue;
}
}
while (xQueueReceive(tone_queue, &req, 0) == pdTRUE)
{
if (req.preemptive)
{
stop_pipeline();
if (current_playing.callback)
current_playing.callback();
current_playing = req;
prepare_and_start_playback(&req);
break;
}
else
{
xQueueSendToBack(tone_queue, &req, 0);
break;
}
}
}
}
esp_err_t tone_player_init(void)
{
// 初始化时关闭PA
es8311_pa_power(false);
if (tone_queue)
return ESP_OK; // Already initialized
tone_queue = xQueueCreate(TONE_QUEUE_LEN, sizeof(tone_request_t));
if (!tone_queue)
return ESP_FAIL;
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);
if (!pipeline)
return ESP_FAIL;
tone_stream_cfg_t tone_cfg = TONE_STREAM_CFG_DEFAULT();
tone_cfg.type = AUDIO_STREAM_READER;
tone_stream = tone_stream_init(&tone_cfg);
mp3_decoder_cfg_t mp3_cfg = DEFAULT_MP3_DECODER_CONFIG();
mp3_decoder = mp3_decoder_init(&mp3_cfg);
i2s_stream_cfg_t i2s_cfg = I2S_STREAM_CFG_DEFAULT();
i2s_cfg.type = AUDIO_STREAM_WRITER;
i2s_stream = i2s_stream_init(&i2s_cfg);
audio_pipeline_register(pipeline, tone_stream, "tone");
audio_pipeline_register(pipeline, mp3_decoder, "mp3");
audio_pipeline_register(pipeline, i2s_stream, "i2s");
const char *link_tag[3] = {"tone", "mp3", "i2s"};
audio_pipeline_link(pipeline, &link_tag[0], 3);
audio_event_iface_cfg_t evt_cfg = AUDIO_EVENT_IFACE_DEFAULT_CFG();
evt = audio_event_iface_init(&evt_cfg);
audio_pipeline_set_listener(pipeline, evt);
xTaskCreate(tone_play_task, "tone_player_task", 4096, NULL, 5, &tone_task_handle);
ESP_LOGI(TAG, "Tone player initialized");
return ESP_OK;
}
esp_err_t play_tone(tone_type_t tone, tone_cb_t callback, bool preemptive)
{
if (!tone_queue)
return ESP_FAIL;
if (tone >= TONE_TYPE_MAX)
return ESP_ERR_INVALID_ARG;
tone_request_t req = {
.tone = tone,
.callback = callback,
.preemptive = preemptive};
return xQueueSendToBack(tone_queue, &req, pdMS_TO_TICKS(100)) == pdTRUE ? ESP_OK : ESP_FAIL;
}
@laodi-chen 这个具体的原因是由于 i2s set clk 的时候 i2s 驱动关了 DMA 导致的。以下有几个方案
- 让 app_main 的 task 优先级高于 i2s stream(i2s_stream 默认的23),确保I2S 的数据在 set clk 之后 write
- 使用 rsp 的方式, 不使用 set clk (Ref) 3.如果是提示音,尽量将所有的音频改为统一格式, 在 i2s init 的时候就配置好, 不需要调用 i2s set clk
@laodi-chen 你提示音是什么采样率?