You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

STM32F407使用CMSIS DSP IFFT处理音频输入输出音量极低问题

STM32F407 I2S音频FFT/IFFT后输出音量极低问题解决

背景

使用STM32F407G-DISC1开发板,通过PMOD I2S2读取音频输入,硬件功能已验证正常,验证代码如下:

for (int i=0; i<BUFFER_LENGTH*4; i=i+4) {
    txBuf[i] = rxBuf[i];
    txBuf[i+1] = rxBuf[i+1];
    txBuf[i+2] = 0;
    txBuf[i+3] = 0;
}

尝试对输入缓冲区执行FFT、变调处理后再IFFT输出,但运行后音量极低(几乎听不到),调整缩放无效,尝试代码如下:

for (int i=0; i<BUFFER_LENGTH*4; i=i+4) {
    fft_in_buf[fft_in_ptr] = (float) ((int) (rxBuf[i]<<16)|rxBuf[i+1]);
    fft_in_ptr++;
}

arm_rfft_fast_f32(&fft_handler, fft_in_buf, fft_out_buf, 0);
arm_rfft_fast_f32(&fft_handler, fft_out_buf, fft_in_buf, 1);

fft_in_ptr = 0;

for (int i=0; i<BUFFER_LENGTH*4; i=i+4) {
        txBuf[i] = (fft_in_buf[fft_in_ptr] >> 16) & 0xFF; // MSB
        txBuf[i + 1] = fft_in_buf[fft_in_ptr] & 0xFF;     // LSB
        txBuf[i + 2] = 0;
        txBuf[i + 3] = 0;

        fft_in_ptr++;
}

主初始化已执行arm_rfft_fast_init_f32(&fft_handler, BUFFER_LENGTH);


核心问题分析

  1. IFFT内置缩放:CMSIS-DSP的arm_rfft_fast_f32执行IFFT时,会自动将结果除以FFT长度BUFFER_LENGTH,直接导致输出幅值被大幅缩小。
  2. 音频数据转换错误:原代码将2字节PCM数据错误转为32位整数再转float,未做归一化,数值范围完全错误;转回16位时直接对float做移位操作,逻辑无效。
  3. 无溢出保护:浮点转整数时没有限幅,容易出现溢出导致失真或幅值异常。

修正后的代码实现

1. 音频数据转FFT输入(归一化处理)

for (int i=0; i<BUFFER_LENGTH*4; i=i+4) {
    // 解析16位有符号PCM数据(确认I2S字节序为MSB在前)
    int16_t pcm_in = (int16_t)((rxBuf[i] << 8) | rxBuf[i+1]);
    // 归一化到[-1.0, 1.0]区间,适配FFT输入要求
    fft_in_buf[fft_in_ptr] = (float)pcm_in / 32768.0f;
    fft_in_ptr++;
}

2. FFT + IFFT处理(补缩放因子)

// 执行正向FFT
arm_rfft_fast_f32(&fft_handler, fft_in_buf, fft_out_buf, 0);

// ----------------------
// 在此插入变调算法处理
// 例如:频率域频谱重采样、相位修正等操作
// ----------------------

// 执行逆向IFFT
arm_rfft_fast_f32(&fft_handler, fft_out_buf, fft_in_buf, 1);

// 乘以FFT长度,抵消IFFT内置的缩放
for (int i=0; i<BUFFER_LENGTH; i++) {
    fft_in_buf[i] *= BUFFER_LENGTH;
}

3. IFFT输出转回16位PCM数据

fft_in_ptr = 0;
for (int i=0; i<BUFFER_LENGTH*4; i=i+4) {
    float val = fft_in_buf[fft_in_ptr];
    // 限幅,防止超出16位有符号整数范围
    val = (val > 1.0f) ? 1.0f : val;
    val = (val < -1.0f) ? -1.0f : val;
    // 转回16位有符号PCM
    int16_t pcm_out = (int16_t)(val * 32767.0f);
    
    // 拆分字节写入发送缓冲区(保持与硬件验证一致的字节序)
    txBuf[i] = (pcm_out >> 8) & 0xFF;
    txBuf[i+1] = pcm_out & 0xFF;
    txBuf[i+2] = 0;
    txBuf[i+3] = 0;

    fft_in_ptr++;
}

额外优化建议

  • 加窗处理:在FFT前对输入数据加汉明窗,IFFT后加窗并采用重叠相加法,减少频谱泄露导致的音质下降。
  • 变调算法实现:频率域变调需对FFT后的复数频谱进行重采样(如线性插值补零/抽点),同时修正相位避免相位不连续,可参考相位声码器原理。
  • 缓冲区长度:确保BUFFER_LENGTH是2的幂次(如256、512),arm_rfft_fast_f32仅支持此类长度的变换。

内容的提问来源于stack exchange,提问作者Luke Galea

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.24 22:22:47