【SpeexDSP库】SpeexDSP库初探,与STM32运行ANS算法踩坑记录
本文记录了在STM32上移植speexDSP 3A算法实现实时音频噪声抑制(ANS)的优化过程。主要内容包括:1) 修改内存分配为片内SRAM,避免片外SDRAM导致的性能下降;2) 调整降噪参数;3) 通过屏蔽线性频域运算,改为仅计算bark域值来提升处理速度;4) 尝试循环展开和快速赋值等运算优化;5) 测试替换为CMSIS-DSP库函数的效果,发现可能导致失真。实验表明,关键优化在于合理管理
简介
使用开源音频处理库speexDSP里的3A算法,在STM32上实现实时的ANS(音频噪声抑制);
该文章用于记录本人在调试过程中踩过的坑,和一些对于性能有限的嵌入式系统算法优化的方案
优化内容
站内有speexDSP库移植stm32的教程,这里就不重复了
1 初始化
SpeexPreprocessState *st;
st = speex_preprocess_state_init(REC_SAI_RX_DMA_BUF_SIZE / 4, REC_SAMPLERATE);
初始化函数第一个参数为一次输入的数据量,第二个参数为采样率
static inline void *speex_alloc (int size)
{
/* WARNING: this is not equivalent to malloc(). If you want to use malloc()
or your own allocator, YOU NEED TO CLEAR THE MEMORY ALLOCATED. Otherwise
you will experience strange bugs */
// return calloc(size,1);
return mymalloc(SRAMIN, size);
}
初始化部分会申请很多内存,这里可以修改为你自己的malloc函数,我这里用的正点原子的
2 修改滤波器参数
i=-40;
speex_preprocess_ctl(st, SPEEX_PREPROCESS_SET_NOISE_SUPPRESS, &i);
这段代码仅演示修改降噪深度的方法,还有很多其他参数可以定位到函数第二个参数的宏定义处
3 修改线性刻度到bark刻度
这个的目的是牺牲一点降噪效果换取稍快一点的处理速度,如果你的系统资源并不吃紧则没必要做
/* Compute a posteriori SNR */
for (i=N;i<N+M;i++) //修改了起始位置,从i=0改成i=N,直接改成了只计算bark域的值
/* Recursive average of the a priori SNR. A bit smoothed for the psd components */
/*屏蔽了线性频域的更新,只更新bark域的值*/
// st->zeta[0] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[0]), MULT16_16(QCONST16(.3f,15),st->prior[0])),15);
// for (i=1;i<N-1;i++)
// st->zeta[i] = PSHR32(ADD32(ADD32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.15f,15),st->prior[i])),
// MULT16_16(QCONST16(.075f,15),st->prior[i-1])), MULT16_16(QCONST16(.075f,15),st->prior[i+1])),15);
for (i=N-1;i<N+M;i++)
st->zeta[i] = PSHR32(ADD32(MULT16_16(QCONST16(.7f,15),st->zeta[i]), MULT16_16(QCONST16(.3f,15),st->prior[i])),15);
/* Convert the EM gains and speech prob to linear frequency */
// filterbank_compute_psd16(st->bank,st->gain2+N, st->gain2);
// filterbank_compute_psd16(st->bank,st->gain+N, st->gain);
/* Use 1 for linear gain resolution (best) or 0 for Bark gain resolution (faster) */
/*默认使用线性域计算增益,本人手动改成bark域*/
if (0)
以上使用//注释掉的为原来的代码,注释掉这些部分意为屏蔽线性刻度的部分运算,能在一定程度提高速率
4 运算优化
从结果上来说,这部分几乎没啥用处,但还是记录一下
// for (i=0;i<N+M;i++)
// st->echo_noise[i] = 0;
memset(&st->echo_noise[0], 0, (N+M)*sizeof(float));
for (i=0;i<2*N;i+=4){
st->frame[i] = MULT16_16_Q15(st->frame[i] , st->window[i]);
st->frame[i + 1] = MULT16_16_Q15(st->frame[i + 1], st->window[i + 1]);
st->frame[i + 2] = MULT16_16_Q15(st->frame[i + 2], st->window[i + 2]);
st->frame[i + 3] = MULT16_16_Q15(st->frame[i + 3], st->window[i + 3]);
}
快速赋值和循环展开,理论上确实比一个一个处理效率要高,但如果你CPU资源并不吃紧也没必要这么大费周章改代码(但听说arm_math做运算喜欢这么搞)
5 替换speex数学运算函数为CMSIS-DSP库函数
这部分从博主使用的硬件来说对性能提升也几乎没有,反而导致了语音信号的失真,个人推测与博主本人前面做的基于CMSIS-DSP库的频域滤波出现的问题是差不多的
【STM32学习笔记】基于频域FFT滤波的音频均衡器方案验证与效果展示
这里还是贴上代码,其中st->ft的内存需要加大一倍,因为arm_cfft输入要求实部0、虚部0、实部1、虚部1这样存储嘛
st->frame = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
st->window = (spx_word16_t*)speex_alloc(2*N*sizeof(spx_word16_t));
st->ft = (spx_word16_t*)speex_alloc(4*N*sizeof(spx_word16_t));
/* Perform FFT */
spx_fft(st->fft_lookup, st->frame, st->ft);
// for(i=0;i<2*N;i++)
// {
// st->ft[2*i] = st->frame[i] / N / 2;
// st->ft[2*i+1] = 0.0f;
// }
// arm_cfft_f32(&arm_cfft_sR_f32_len1024, st->ft, 0, 1); //单次输入音频数据量*2作为FFT点数
6 malloc内存管理
这里博主踩了个坑,特此记录一下
return mymalloc(SRAMIN, size);
// return mymalloc(SRAMEX, size);
因为一开始设的buffer长度太大,导致片内的内存不够,于是博主为speex的数据申请了外部SDRAM的内存,结果运算速度慢的出奇,才有了上面一系列的优化方案
所以,尽量不要给你需要频繁存取的数据申请片外的存储空间,速度会很慢
资源共享
代码 https://gitee.com/creator-len/study_speex
效果展示
古法语音去噪ANS,speexDSP+stm32实现
更多推荐



所有评论(0)