NoAudioCodecDuplex::NoAudioCodecDuplex(int input_sample_rate, int output_sample_rate, gpio_num_t bclk, gpio_num_t ws, gpio_num_t dout, gpio_num_t din) {
duplex_ = true;
input_sample_rate_ = input_sample_rate;
output_sample_rate_ = output_sample_rate;
i2s_chan_config_t chan_cfg = {
.id = I2S_NUM_0,
.role = I2S_ROLE_MASTER,
.dma_desc_num = AUDIO_CODEC_DMA_DESC_NUM,
.dma_frame_num = AUDIO_CODEC_DMA_FRAME_NUM,
.auto_clear_after_cb = true, //true
.auto_clear_before_cb = false,
.intr_priority = 0,
};
ESP_ERROR_CHECK(i2s_new_channel(&chan_cfg, &tx_handle_, &rx_handle_));
// 假设原始可用的 TX 设置(左对齐)
i2s_std_config_t tx_std_cfg = {
.clk_cfg = {
.sample_rate_hz = (uint32_t)output_sample_rate_, // 输出采样率
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
#ifdef I2S_HW_VERSION_2
.ext_clk_freq_hz = 0,
#endif
},
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_LEFT,
.ws_width = I2S_DATA_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift = false,
.left_align = true, // 这是以前能出声的关键
.big_endian = false,
.bit_order_lsb = false
},
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = bclk,
.ws = ws,
.dout = dout,
.din = din,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false
}
}
};
// 正确的 RX 设置(标准 I2S)
i2s_std_config_t rx_std_cfg = {
.clk_cfg = {
.sample_rate_hz = (uint32_t)input_sample_rate_, // 麦克风采样率
.clk_src = I2S_CLK_SRC_DEFAULT,
.mclk_multiple = I2S_MCLK_MULTIPLE_256,
#ifdef I2S_HW_VERSION_2
.ext_clk_freq_hz = 0,
#endif
},
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_32BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_32BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_LEFT,//I2S_STD_SLOT_LEFT, // 检查麦克风 L/R 引脚
.ws_width = I2S_DATA_BIT_WIDTH_32BIT,
.ws_pol = false,
.bit_shift =false,// true, // 标准 I2S 特征
.left_align = true ,
.big_endian = false,
.bit_order_lsb = false
},
/*
.slot_cfg = {
.data_bit_width = I2S_DATA_BIT_WIDTH_16BIT,
.slot_bit_width = I2S_SLOT_BIT_WIDTH_16BIT,
.slot_mode = I2S_SLOT_MODE_MONO,
.slot_mask = I2S_STD_SLOT_LEFT, // 检查麦克风 L/R 引脚
.ws_width = I2S_DATA_BIT_WIDTH_16BIT,
.ws_pol = false,
.bit_shift =false,// true, // 标准 I2S 特征
.left_align = true ,
.big_endian = false,
.bit_order_lsb = false
},
*/
.gpio_cfg = {
.mclk = I2S_GPIO_UNUSED,
.bclk = bclk,
.ws = ws,
.dout = dout,
.din = din,
.invert_flags = {
.mclk_inv = false,
.bclk_inv = false,
.ws_inv = false
}
}
};
// 分别初始化
ESP_ERROR_CHECK(i2s_channel_init_std_mode(tx_handle_, &tx_std_cfg));
ESP_ERROR_CHECK(i2s_channel_init_std_mode(rx_handle_, &rx_std_cfg));
return;
===========================================================================
raw_buf = (int32_t *)malloc(RECV2_BUF_SIZE*2);//变为4个字节的了,所以多采集2倍,才能保持原样本数
===========================================================================
// 1. 读取一帧麦克风数据
memset(mic_data, 0, RECV2_BUF_SIZE);
ret = i2s_channel_read(ref_audioCodec->rx_handle_, raw_buf, RECV2_BUF_SIZE*2, &bytes_read, 1000);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "[feed_wakeup_data] i2s read failed, %s", err_reason[ret == ESP_ERR_TIMEOUT]);
vTaskDelay(10 / portTICK_PERIOD_MS);
continue;
}
// ----- 播放状态下丢弃所有麦克风数据 -----
if (is_playback_active()) {
vTaskDelay(10 / portTICK_PERIOD_MS);
continue;
}
int samples_read = bytes_read / sizeof(int32_t);
for (int i = 0; i < samples_read; i++) {
// 关键补偿:左移 1 位,让 MSB 回归最高位
int32_t corrected = raw_buf[i] << 1;
// 取高 16 位作为最终样本
int32_t sample_16 = corrected >> 16;//24位数据右移 16 位
// 限幅(防止左移后溢出,但通常不会)
if (sample_16 > 32767) sample_16 = 32767;
if (sample_16 < -32768) sample_16 = -32768;
mic_data[i] = (int16_t)sample_16;
}
===========================================================================
#define AUDIO_CODEC_DMA_DESC_NUM 4//6
#define AUDIO_CODEC_DMA_FRAME_NUM 120//240
===========================================================================
for (int i = 0; i < output_samples1; i++) {
// 将 16 位有符号数左移 16 位,放入 32 位高半部
tx_buf_32[i] = ((int32_t)pcm_output_2[i]) << 16;
}
esp_err_t err = i2s_channel_write(ref_audioCodec->tx_handle_, (char *)tx_buf_32, output_samples1*4, &bytes_write,portMAX_DELAY );//pdMS_TO_TICKS(50)