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);
核心问题分析
- IFFT内置缩放:CMSIS-DSP的
arm_rfft_fast_f32执行IFFT时,会自动将结果除以FFT长度BUFFER_LENGTH,直接导致输出幅值被大幅缩小。 - 音频数据转换错误:原代码将2字节PCM数据错误转为32位整数再转float,未做归一化,数值范围完全错误;转回16位时直接对float做移位操作,逻辑无效。
- 无溢出保护:浮点转整数时没有限幅,容易出现溢出导致失真或幅值异常。
修正后的代码实现
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
相关产品推荐
相关产品推荐

