基于MCRA-OMLSA的语音降噪(二):实现

上篇文章(基于MCRA-OMLSA的语音降噪(一):原理)讲了基于MCRA-OMLSA降噪的原理,本篇讲怎么做软件实现。软件实现有多种方式。单纯看降噪效果可用python,因为python有丰富的库可用,可节省不少时间,把主要精力放在降噪效果提升上。如果要把算法用在产品上就得用其他语言。我们是芯片公司,且我们team偏底层,最常用的语言是C,所以我又用C实现了该算法。本文先讲讲在python下的实现,再讲讲在C下的实现。

一,python下的实现

Python有丰富的库,音频文件读取的librosa/soundfile等,数学库的numpy(里面也包含了信号处理的fft等),连算指数积分的都有了(scipy.special.exp1)。算法原理搞清楚了后先画软件流程图,如下图:

根据流程图并且基于现成的python库很快就能把算法实现了。关键是调优,要有好的降噪效果。算法里参数较多,主要有\alpha _{d}\alpha _{s}\alpha _{p}等,其中有些有推荐的经验值,有些需要自己tuning。参数tuning过程中降噪效果评估依旧用权威的PESQ。将干净语音和噪声以指定的SNR(通常有0db/5db/10db/15db等)叠加后得到带噪语音,用降噪算法对这个带噪语音做降噪得到降噪后的语音。用PESQ工具分别将带噪语音和降噪后的语音与原来的干净语音做比较,可以得到MOS分提高了多少。花了一些时间tuning后有了一个相对不错的降噪效果,以叠加的是白噪声为例,降噪后在各种SNR下的MOS分提升如下表:

二,C语言下的实现

C语言下的实现要用在产品中,算法的运算量(即 CPU load)是一个要重点考虑的因素。实现通常分两个阶段。第一阶段是基于C中已有数学库的浮点实现,在参数值一样的情况下降噪效果要和python实现的保持一致。如果运算量较大或者处理器不支持浮点运算,需要进入第二阶段。首先将用到的数学库函数用自己写的函数(函数里只有加减乘除等)代替,然后再将整个算法定点化,使运算量降下来。

1,第一阶段

首先根据算法和流程图定义结构体和API。这里简单把API列一下,如下图:

从上图看出,MCRA和OMLSA各有两个API,相对简洁,一个是初始化,一个是算法处理。实现时遇到的第一个问题是C语言数学库里没有提供指数积分函数,需要自己实现。在网上搜了下,书《常用算法程序集(C语言)第三版》的14.15节讲了怎么算指数积分,这里简单介绍下。令

其中γ为欧拉常数(γ = 0.577215664901532860606512)。

可以通过该书9.7节的勒让德-高斯求积分法来求。对勒让德-高斯求积分法感兴趣的可以找相关资料来看,这里就不详细介绍了。把Ei(x)求出后再取反就是算法中所要的

的值。取几个值比较这个实现与python里scipy.special.exp1的结果,如下表,可以看出精度还是挺高的。

算法代码写好后,还需要FFT相关的代码才能调试,FFT相关的选用了CMSIS里浮点实现的代码。调试时带噪语音文件依旧用的是python实现调试的那个,这样结果好对齐,方便比较各个环节的输出,如FFT的输出、噪声估计的输出等。调试时要一级一级的调,在误差允许的范围内,如果上一级的输出一致而本级的输出不一致,则问题就出在这一级里。在这个方法下很快就调试好了,在误差允许的范围内,每帧各个频点降噪后的幅度谱与python里的保持一致。

2,第二阶段

第二阶段可分两个子阶段,一是把数学库的函数用自己写的函数(函数里只有加减乘除等运算)替代,二是把整个算法定点化。目前第一个子阶段已完成,第二个子阶段完成后有机会也会写怎么对算法做定点化的。

算法用到的数学库函数主要是自然指数(exp())和一般指数(pow()),再加上做完FFT后是复数,需要用求平方根(sqrt())算得幅度谱,所以需要写出这几个库函数对应的函数。对于一般的求指数而言,它可以转换成求自然指数和自然对数,具体关系如下: ,所以只要实现了自然指数和自然对数就可以了。这样最终实现的函数是3个:自然指数,自然对数,求平方根。至于怎么实现的这3个函数,细节较多,会在后面专门写一篇文章讲。实现完这些函数后与标准库里的做比较,误差在允许的范围内即可。再把这些函数用在算法中,拿降噪后的幅度谱与python里的比较,误差也在允许的范围内。

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
MCRA算法是一种基于循环滤波器的噪声估计算法,主要用于语音信号处理领域。以下是一个简单的Verilog代码实现: ```verilog module mcra_noise_estimation( input signed [15:0] audio_in, input clk, input rst, output reg signed [15:0] noise_out ); parameter L = 128; // 块长度 parameter N = 4; // 循环滤波器阶数 parameter alpha = 0.98; // 短时平均能量加权系数 parameter beta = 0.9995; // 长时平均能量加权系数 reg signed [15:0] audio_buf [L-1:0]; reg signed [15:0] short_energy; reg signed [15:0] long_energy; reg signed [15:0] noise_energy; reg signed [15:0] x [N-1:0]; reg signed [15:0] y [N-1:0]; reg signed [15:0] b [N-1:0]; reg signed [15:0] a [N-1:0]; integer i, j; always @(posedge clk) begin if (rst) begin for (i = 0; i < L; i = i + 1) begin audio_buf[i] <= 0; end short_energy <= 0; long_energy <= 0; noise_energy <= 0; for (i = 0; i < N; i = i + 1) begin x[i] <= 0; y[i] <= 0; b[i] <= 0; a[i] <= 0; end end else begin // 更新循环滤波器系数 for (i = 0; i < N; i = i + 1) begin b[i] <= (i == 0) ? 1 : (alpha**i - alpha**(i-1)); a[i] <= (i == 0) ? 1 : (2*alpha*cos(2*PI*i/N) - alpha**2); end // 更新短时平均能量 short_energy <= (short_energy * alpha + (1 - alpha) * audio_in ** 2); // 更新长时平均能量 long_energy <= (long_energy * beta + (1 - beta) * audio_in ** 2); // 计算噪声能量估计值 noise_energy <= (short_energy - long_energy > 0) ? (short_energy - long_energy) : 0; // 更新循环滤波器延时线 for (i = N-2; i >= 0; i = i - 1) begin x[i+1] <= x[i]; y[i+1] <= y[i]; end x[0] <= audio_in; for (i = 0; i < N; i = i + 1) begin y[0] <= y[0] + b[i] * x[i] - a[i] * y[i]; end // 更新噪声能量估计值 noise_out <= y[0] ** 2 - noise_energy; // 更新音频数据缓存 for (i = L-2; i >= 0; i = i - 1) begin audio_buf[i+1] <= audio_buf[i]; end audio_buf[0] <= audio_in; end end endmodule ``` 该Verilog代码实现MCRA算法中的核心部分,具体实现原理请参考相关文献。在使用时,需要将音频信号作为`audio_in`输入,时钟信号作为`clk`输入,复位信号作为`rst`输入,噪声能量估计结果作为`noise_out`输出。该代码中的块长度、循环滤波器阶数、加权系数等参数可以根据具体应用场景进行调整。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值