forked from xiaozhi/xiaozhi-esp32
fix: ESP-HI audio sampling problem (#1207)
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
#include "adc_pdm_audio_codec.h"
|
#include "adc_pdm_audio_codec.h"
|
||||||
|
|
||||||
#include <esp_log.h>
|
#include <esp_log.h>
|
||||||
|
#include <esp_timer.h>
|
||||||
#include <driver/i2c.h>
|
#include <driver/i2c.h>
|
||||||
#include <driver/i2c_master.h>
|
#include <driver/i2c_master.h>
|
||||||
#include <driver/i2s_tdm.h>
|
#include <driver/i2s_tdm.h>
|
||||||
@@ -11,6 +12,7 @@
|
|||||||
#include "hal/rtc_io_hal.h"
|
#include "hal/rtc_io_hal.h"
|
||||||
#include "hal/gpio_ll.h"
|
#include "hal/gpio_ll.h"
|
||||||
#include "settings.h"
|
#include "settings.h"
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
static const char TAG[] = "AdcPdmAudioCodec";
|
static const char TAG[] = "AdcPdmAudioCodec";
|
||||||
|
|
||||||
@@ -71,7 +73,7 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
|||||||
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, NULL));
|
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, NULL));
|
||||||
|
|
||||||
i2s_pdm_tx_config_t pdm_cfg_default = BSP_I2S_DUPLEX_MONO_CFG((uint32_t)output_sample_rate, pdm_speak_p);
|
i2s_pdm_tx_config_t pdm_cfg_default = BSP_I2S_DUPLEX_MONO_CFG((uint32_t)output_sample_rate, pdm_speak_p);
|
||||||
pdm_cfg_default.clk_cfg.up_sample_fs = output_sample_rate / 100;
|
pdm_cfg_default.clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS;
|
||||||
pdm_cfg_default.slot_cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
pdm_cfg_default.slot_cfg.sd_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||||
pdm_cfg_default.slot_cfg.hp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
pdm_cfg_default.slot_cfg.hp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||||
pdm_cfg_default.slot_cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
pdm_cfg_default.slot_cfg.lp_scale = I2S_PDM_SIG_SCALING_MUL_4;
|
||||||
@@ -112,10 +114,27 @@ AdcPdmAudioCodec::AdcPdmAudioCodec(int input_sample_rate, int output_sample_rate
|
|||||||
esp_rom_gpio_connect_out_signal(pdm_speak_n, I2SO_SD_OUT_IDX, 1, 0); //反转输出 SD OUT 信号
|
esp_rom_gpio_connect_out_signal(pdm_speak_n, I2SO_SD_OUT_IDX, 1, 0); //反转输出 SD OUT 信号
|
||||||
gpio_set_drive_capability(pdm_speak_n, GPIO_DRIVE_CAP_0);
|
gpio_set_drive_capability(pdm_speak_n, GPIO_DRIVE_CAP_0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 初始化输出定时器
|
||||||
|
esp_timer_create_args_t output_timer_args = {
|
||||||
|
.callback = &AdcPdmAudioCodec::OutputTimerCallback,
|
||||||
|
.arg = this,
|
||||||
|
.dispatch_method = ESP_TIMER_TASK,
|
||||||
|
.name = "output_timer"
|
||||||
|
};
|
||||||
|
ESP_ERROR_CHECK(esp_timer_create(&output_timer_args, &output_timer_));
|
||||||
|
|
||||||
ESP_LOGI(TAG, "AdcPdmAudioCodec initialized");
|
ESP_LOGI(TAG, "AdcPdmAudioCodec initialized");
|
||||||
}
|
}
|
||||||
|
|
||||||
AdcPdmAudioCodec::~AdcPdmAudioCodec() {
|
AdcPdmAudioCodec::~AdcPdmAudioCodec() {
|
||||||
|
// 删除定时器
|
||||||
|
if (output_timer_) {
|
||||||
|
esp_timer_stop(output_timer_);
|
||||||
|
esp_timer_delete(output_timer_);
|
||||||
|
output_timer_ = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
ESP_ERROR_CHECK(esp_codec_dev_close(output_dev_));
|
||||||
esp_codec_dev_delete(output_dev_);
|
esp_codec_dev_delete(output_dev_);
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
ESP_ERROR_CHECK(esp_codec_dev_close(input_dev_));
|
||||||
@@ -161,11 +180,27 @@ void AdcPdmAudioCodec::EnableOutput(bool enable) {
|
|||||||
};
|
};
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs));
|
ESP_ERROR_CHECK(esp_codec_dev_open(output_dev_, &fs));
|
||||||
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_));
|
ESP_ERROR_CHECK(esp_codec_dev_set_out_vol(output_dev_, output_volume_));
|
||||||
|
|
||||||
|
// 强制按板卡配置重配PDM TX时钟,覆盖第三方库在set_fmt中的默认up_sample_fs
|
||||||
|
// 若通道已启用,先禁用再重配,最后再启用
|
||||||
|
ESP_ERROR_CHECK_WITHOUT_ABORT(i2s_channel_disable(tx_handle_));
|
||||||
|
i2s_pdm_tx_clk_config_t clk_cfg = I2S_PDM_TX_CLK_DEFAULT_CONFIG((uint32_t)output_sample_rate_);
|
||||||
|
clk_cfg.up_sample_fs = AUDIO_PDM_UPSAMPLE_FS;
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_reconfig_pdm_tx_clock(tx_handle_, &clk_cfg));
|
||||||
|
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
||||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||||
gpio_set_level(pa_ctrl_pin_, 1);
|
gpio_set_level(pa_ctrl_pin_, 1);
|
||||||
}
|
}
|
||||||
|
// 启用输出时启动定时器
|
||||||
|
if (output_timer_) {
|
||||||
|
esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
// 禁用输出时停止定时器
|
||||||
|
if (output_timer_) {
|
||||||
|
esp_timer_stop(output_timer_);
|
||||||
|
}
|
||||||
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
if(pa_ctrl_pin_ != GPIO_NUM_NC){
|
||||||
gpio_set_level(pa_ctrl_pin_, 0);
|
gpio_set_level(pa_ctrl_pin_, 0);
|
||||||
}
|
}
|
||||||
@@ -183,6 +218,11 @@ int AdcPdmAudioCodec::Read(int16_t* dest, int samples) {
|
|||||||
int AdcPdmAudioCodec::Write(const int16_t* data, int samples) {
|
int AdcPdmAudioCodec::Write(const int16_t* data, int samples) {
|
||||||
if (output_enabled_) {
|
if (output_enabled_) {
|
||||||
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
ESP_ERROR_CHECK_WITHOUT_ABORT(esp_codec_dev_write(output_dev_, (void*)data, samples * sizeof(int16_t)));
|
||||||
|
// 重置输出定时器
|
||||||
|
if (output_timer_) {
|
||||||
|
esp_timer_stop(output_timer_);
|
||||||
|
esp_timer_start_once(output_timer_, TIMER_TIMEOUT_US);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return samples;
|
return samples;
|
||||||
}
|
}
|
||||||
@@ -195,9 +235,15 @@ void AdcPdmAudioCodec::Start() {
|
|||||||
output_volume_ = 10;
|
output_volume_ = 10;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESP_ERROR_CHECK(i2s_channel_enable(tx_handle_));
|
|
||||||
|
|
||||||
EnableInput(true);
|
EnableInput(true);
|
||||||
EnableOutput(true);
|
EnableOutput(true);
|
||||||
ESP_LOGI(TAG, "Audio codec started");
|
ESP_LOGI(TAG, "Audio codec started");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 定时器回调函数实现
|
||||||
|
void AdcPdmAudioCodec::OutputTimerCallback(void* arg) {
|
||||||
|
AdcPdmAudioCodec* codec = static_cast<AdcPdmAudioCodec*>(arg);
|
||||||
|
if (codec && codec->output_enabled_) {
|
||||||
|
codec->EnableOutput(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include <esp_codec_dev.h>
|
#include <esp_codec_dev.h>
|
||||||
#include <esp_codec_dev_defaults.h>
|
#include <esp_codec_dev_defaults.h>
|
||||||
|
#include <esp_timer.h>
|
||||||
|
|
||||||
class AdcPdmAudioCodec : public AudioCodec {
|
class AdcPdmAudioCodec : public AudioCodec {
|
||||||
private:
|
private:
|
||||||
@@ -12,6 +13,13 @@ private:
|
|||||||
esp_codec_dev_handle_t input_dev_ = nullptr;
|
esp_codec_dev_handle_t input_dev_ = nullptr;
|
||||||
gpio_num_t pa_ctrl_pin_ = GPIO_NUM_NC;
|
gpio_num_t pa_ctrl_pin_ = GPIO_NUM_NC;
|
||||||
|
|
||||||
|
// 定时器相关成员变量
|
||||||
|
esp_timer_handle_t output_timer_ = nullptr;
|
||||||
|
static constexpr uint64_t TIMER_TIMEOUT_US = 120000; // 120ms = 120000us
|
||||||
|
|
||||||
|
// 定时器回调函数
|
||||||
|
static void OutputTimerCallback(void* arg);
|
||||||
|
|
||||||
virtual int Read(int16_t* dest, int samples) override;
|
virtual int Read(int16_t* dest, int samples) override;
|
||||||
virtual int Write(const int16_t* data, int samples) override;
|
virtual int Write(const int16_t* data, int samples) override;
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,9 @@
|
|||||||
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
#define AUDIO_INPUT_SAMPLE_RATE 16000
|
||||||
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
#define AUDIO_OUTPUT_SAMPLE_RATE 24000
|
||||||
|
|
||||||
|
// 配置PDM上采样fs参数(取值范围<=480)。部分设备在441时表现更稳定
|
||||||
|
#define AUDIO_PDM_UPSAMPLE_FS 441
|
||||||
|
|
||||||
#define AUDIO_ADC_MIC_CHANNEL 2
|
#define AUDIO_ADC_MIC_CHANNEL 2
|
||||||
#define AUDIO_PDM_SPEAK_P_GPIO GPIO_NUM_6
|
#define AUDIO_PDM_SPEAK_P_GPIO GPIO_NUM_6
|
||||||
#define AUDIO_PDM_SPEAK_N_GPIO GPIO_NUM_7
|
#define AUDIO_PDM_SPEAK_N_GPIO GPIO_NUM_7
|
||||||
|
|||||||
@@ -25,7 +25,6 @@
|
|||||||
"CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048",
|
"CONFIG_LWIP_TCPIP_TASK_STACK_SIZE=2048",
|
||||||
"CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y",
|
"CONFIG_MBEDTLS_DYNAMIC_FREE_CONFIG_DATA=y",
|
||||||
"CONFIG_NEWLIB_NANO_FORMAT=y",
|
"CONFIG_NEWLIB_NANO_FORMAT=y",
|
||||||
"CONFIG_MMAP_FILE_NAME_LENGTH=25",
|
|
||||||
"CONFIG_ESP_CONSOLE_NONE=y",
|
"CONFIG_ESP_CONSOLE_NONE=y",
|
||||||
"CONFIG_USE_ESP_WAKE_WORD=y",
|
"CONFIG_USE_ESP_WAKE_WORD=y",
|
||||||
"CONFIG_COMPILER_OPTIMIZATION_SIZE=y"
|
"CONFIG_COMPILER_OPTIMIZATION_SIZE=y"
|
||||||
|
|||||||
@@ -48,6 +48,8 @@ EmojiPlayer::EmojiPlayer(esp_lcd_panel_handle_t panel, esp_lcd_panel_io_handle_t
|
|||||||
.task = ANIM_PLAYER_INIT_CONFIG()
|
.task = ANIM_PLAYER_INIT_CONFIG()
|
||||||
};
|
};
|
||||||
|
|
||||||
|
player_cfg.task.task_priority = 1;
|
||||||
|
player_cfg.task.task_stack = 4096;
|
||||||
player_handle_ = anim_player_init(&player_cfg);
|
player_handle_ = anim_player_init(&player_cfg);
|
||||||
|
|
||||||
const esp_lcd_panel_io_callbacks_t cbs = {
|
const esp_lcd_panel_io_callbacks_t cbs = {
|
||||||
|
|||||||
@@ -38,6 +38,8 @@ public:
|
|||||||
|
|
||||||
virtual void SetEmotion(const char* emotion) override;
|
virtual void SetEmotion(const char* emotion) override;
|
||||||
virtual void SetStatus(const char* status) override;
|
virtual void SetStatus(const char* status) override;
|
||||||
|
virtual void SetChatMessage(const char* role, const char* content) override {}
|
||||||
|
|
||||||
anim::EmojiPlayer* GetPlayer()
|
anim::EmojiPlayer* GetPlayer()
|
||||||
{
|
{
|
||||||
return player_.get();
|
return player_.get();
|
||||||
|
|||||||
@@ -397,11 +397,6 @@ public:
|
|||||||
InitializeSpi();
|
InitializeSpi();
|
||||||
InitializeLcdDisplay();
|
InitializeLcdDisplay();
|
||||||
InitializeTools();
|
InitializeTools();
|
||||||
|
|
||||||
DeviceStateEventManager::GetInstance().RegisterStateChangeCallback([this](DeviceState previous_state, DeviceState current_state) {
|
|
||||||
ESP_LOGD(TAG, "Device state changed from %d to %d", previous_state, current_state);
|
|
||||||
this->GetAudioCodec()->EnableOutput(current_state == kDeviceStateSpeaking);
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual AudioCodec* GetAudioCodec() override
|
virtual AudioCodec* GetAudioCodec() override
|
||||||
|
|||||||
Reference in New Issue
Block a user