You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

基于ESP32-S3的16位转24位音频I2S传输失真问题排查

ESP32-S3 16位转24位音频I2S传输失真问题排查与修复

核心错误分析

你的16位转24位代码存在三个关键问题,直接导致音频失真:

  1. 变量初始化顺序错误bytes_to_write先被用于计算num_samples,但此时该变量尚未赋值,属于未定义行为,会导致采样数量计算错误。
  2. 16位采样处理逻辑错误:原代码将16位采样当作无符号字节拼接,未正确处理有符号采样的符号扩展,且字节序拼接方向错误。
  3. 目标缓冲区索引错误:循环中给current_pos_16赋值时使用i*2作为起始索引,但24位采样每个占3字节,导致数据错位覆盖。

修正后的转换代码

uint8_t* current_pos = (uint8_t*)buf + total_sent_bytes;
// 先基于原始16位音频字节数计算采样数量
size_t num_samples = bytes_to_write / 2;
// 计算转换为24位后的总字节数
size_t target_bytes = num_samples * 3; 
uint8_t *current_pos_16 = heap_caps_malloc(target_bytes, MALLOC_CAP_SPIRAM);
if (!current_pos_16) {
    // 内存分配失败时的处理逻辑
    return;
}

for (int i = 0; i < num_samples; i++) {
    // 按小端字节序读取16位有符号采样(匹配多数音频格式)
    int16_t sample_16 = *(int16_t*)(current_pos + i * 2);
    // 左移8位扩展为24位,自动完成符号扩展
    int32_t sample_24 = (int32_t)sample_16 << 8;
    
    // 按小端字节序写入24位采样到目标缓冲区
    current_pos_16[i * 3] = (uint8_t)(sample_24 & 0xFF);
    current_pos_16[i * 3 + 1] = (uint8_t)((sample_24 >> 8) & 0xFF);
    current_pos_16[i * 3 + 2] = (uint8_t)((sample_24 >> 16) & 0xFF);
}

ESP_ERROR_CHECK(i2s_channel_write(*tx_chan, current_pos_16, target_bytes, &written_bytes, 1000));
total_sent_bytes += written_bytes;
free(current_pos_16);

关键修正说明

  • 变量顺序调整:先通过原始bytes_to_write(16位音频总字节数)计算采样数,再推导24位音频的总字节数,避免未定义行为。
  • 符号扩展处理:将16位采样以int16_t类型读取,左移8位时会自动将符号位扩展到24位的高位,保证负数信号的正确转换。
  • 索引与字节序修正:每个24位采样占用3字节,使用i*3作为起始索引;按小端字节序拆分数据,若你的I2S配置为大端,可调整移位顺序(如先写入高位字节)。
  • 内存安全检查:增加内存分配失败的判断,避免空指针访问导致的系统异常。

内容的提问来源于stack exchange,提问作者Emirhan Üzüm

火山引擎 最新活动