2019-03-13 16:31:16 weiqiwu1986 阅读数 3556

噪声问题一直是语音识别的一个老大难的问题,在理想的实验室的环境下,识别效果已经非常好了,之前听很多音频算法工程师抱怨,在给识别做降噪时,经常发现WER不降反升,降低了识别率,有点莫名其妙,又无处下手。

  刚好,前段时间调到了AIlab部门,有机会接触这块,改善语音识别的噪声问题,虽然在此之前,询问过同行业的朋友,单通道近场下,基本没有太大作用,有时反而起到反作用,但是自己还是想亲身实践一下,至少找到这些方法失效的原因,看看是否在这些失败的原因里面,改进下思路,可能有新的发现;同时去Ailab,顺带把深度学习降噪实践一把,就算在ASR没有效果,以后还能用在语音通信这个领域。

  任务的要求是保证声学模型不变动的情况下,即不重新训练声学模型,即单纯利用降噪来改善那些环境恶劣的样本,同时保证不干扰纯净语音或者弱噪声的语音场景,所以非常具有挑战性。

  为了赶项目,用自己非常熟悉的各种传统的降噪方法:包括最小值跟踪噪声估计,MCRA, IMCRA,等各种噪声估计方法,以及开源项目 webrtc NS, AFE(ETSI ES 202 050 Advanced DSR Front-end Codec, two stages of Wiener filtering),剩下的任务就是调参,经过很多次努力,基本没有什么效果,相反WER还会有1%点左右的增加。

分析对比了降噪和没有降噪的识别文本对比和频谱分析,总结了以下这些原因,希望对后面的人有些参考意义:

  1.DNN本身就有很强的抗噪性,在弱噪声和纯净语音下,基本都不是问题。

通常场景下,这点噪声,用线上数据或者刻意加噪训练,是完全可以吸收掉的,只有在20db以下,含噪样本的频谱特征和纯净样本的频谱特征差异太大,用模型学习收敛就不太好,这时需要降噪前端。

  2.降噪对于纯净语音或者弱噪声环境下,不可避免的对语音有所损伤,只有在恶劣的环境下,会起到非常明显的作用。

传统降噪是基于统计意义上面的一个处理,难以做到瞬时噪声的精准估计,这个本身就是一个近似的,粗略模糊化的一个处理,即不可避免的对噪声欠估计或者过估计,本身难把握,保真语音,只去噪,如果噪声水平很弱,这个降噪也没有什么用或者说没有明显作用,去噪力度大了,又会破坏语音。可以预见,根据测试集进行调参,就像是在绳子上面玩杂技。

我们的测试样本集,90%的样本都在在20db以上,只有200来条的样子,环境比较恶劣。所以通常起来反作用。

  3.降噪里面的很多平滑处理,是有利于改善听感的,但是频谱也变得模糊,这些特征是否能落到正确的类别空间里面,也是存在疑问的。所以在前端降噪的基础上,再过一遍声学模型重新训练,应该是有所作用的,但是训练一个声学模型都要10来天,损失太大,也不满足任务要求。

  4. 传统降噪,通常噪声初始化会利用初始的前几帧,而如果开头是语音,那就会失真很明显。

  5.估计出噪声水平,在SNR低的情况下降噪,SNR高时,不处理或者进行弱处理,在中间水平,进行软处理,这个思路似乎可以行的通。

  6.用基于声学特征的传统降噪方法,尝试过,在测试集里面,有不到1%的WER降低。

  7.到底用什么量来指导降噪过程?既然降噪没法做好很好的跟踪,处理的很理想。即不可能处理的很干净,同时不能保证语音分量不会被损伤,即降噪和保证语音分量是个相互矛盾,同时也是一个权衡问题。那其实换个角度,降噪主要是改善了声学特征,让原来受噪声影响错分类的音素落到正确的音素类别,即降低CE。那么应该直接将降噪和CE做个关联,用CE指导降噪过程参数的自适应变化,在一个有代表性的数据集里面,有统计意义上的效果,可能不一定能改善听感,处理的很干净,但是在整体意义上,有能改善识别的。所以说语音去噪模块必须要和声学前端联合起来优化,目标是将去噪后的数据投影到声学模块接受的数据空间,而不是改善听感,即优化的目标是降低声学模型的CE,或者说是降低整条链路的wer,所以用降噪网络的LOSS除了本身的损失量,还应绑定CE的LOSS自适应去训练学习是比较合理的方案。也可以将降噪网络看成和声学模型是一个大网络,为CE服务,当然,这不一定是降噪网络,也可以是传统的自适应降噪方法,但是如果是基于kaldi开发,里面涉及到的工程量是很大的。

  8.在整个语音识别体系中,由于声学模型的强抗噪性,所以单通道下的前端降噪似乎没有什么意义,1%左右的wer的改变,基本不影响整个大局,所以想要搞识别这块的朋友,应该先把重要的声学模型,语言模型,解码器,搞完之后,再来撸撸这块,因为即便没有单独的前端,整个识别大多数场景都是OK的,恶劣的场景比较少,一般场景大不了扩增各种带噪数据训练,大不了扩增各种带噪数据,也是可以的。

我们的线上数据,影响识别的因素排序是口语化,方言,短词,其次才是噪声,另外,少量混响,语速,音量,也是影响因素之一,以上是自己的一点片面之言,希望对大家有参考意义,少走弯路。

 

2018-08-07 10:28:53 audio_algorithm 阅读数 1263

一、语音识别简介

    语音识别的一般框架一般包含几个部分:声学模型、语音模型、以及词典。语音信号(波形)经过前级处理(包括降噪,语音增强,人声检测等)后,提取特征,送入解码模块,进行解析得到识别结果。而解码模块则由 声学模型、语言模型映射、链接组成的网络。目前主流的语音模型一般采用 n-gram 语言模型,声学模型采样隐马尔科夫模型(HMM),这些模型都需要经过预先训练得到。

    上图框架中,发音字典是指系统所能处理的单词的集合,并标明了其发音。通过发音字典得到声学模型的建模单元和语言模型建模单元间的映射关系,从而把声学模型和语言模型连接起来,组成一个搜索的状态空间用于解码器进行解码工作。

二、语音识别开源项目

                                           

    CMU Sphinix,显而易见,从它的名字就能看出来是卡内基梅隆大学的产物。它已经以某些形式存在了 20 年了,现在它在 Github(C (https://github.com/cmusphinx/pocketsphinx) 版本和 Java (https://github.com/cmusphinx/sphinx4) 版本)和 SourceForge (https://sourceforge.net/projects/cmusphinx/) 上都开源了,而且两个平台上都有活动。Github 上的 Java 版本和 C 版本都只有一个贡献者,但是这并不影响此项目的历史真实性(在 SourceForge repo 上有 9 个管理人员还有很多开发者)

    Kaldi 从 2009 年的研讨会起就有它的学术根基了,现在已经在 GitHub (https://github.com/kaldi-asr/kaldi) 上开源,有 121 名贡献者。HTK 始于 1989 年的剑桥大学,已经商用一段时间了,但是现在它的版权又回到了剑桥大学并且已经不是开源软件了。它的版本更新于 2015 年 12 月,先前发布于 2009 年。Julius (http://julius.osdn.jp/en_index.php) 起源于 1997 年,最后一个主要版本发布于 2016 年 9 月,有些活跃的 Github repo 包含三个贡献者,现在已经不大可能反应真实情况了。ISIP 是第一个型的开源语音识别系统,源于密西西比州立大学。它主要发展于 1996 到 1999 年间,最后版本发布于 2011 年,但是这个项目在 Github 出现前就已经不复存在了。

三、PocketSphinx编译运行

3.1.准备

操作系统:windows 7 64Bit SP1

编译器:Viual Studio 2013

pocketsphinx版本:5prealpha

在Sphinx官网下有如下几个下载目录:

  • Pocketsphinx — lightweight recognizer library written in C  (C语言开发的轻量级语音识别引擎)
  • Sphinxtrain — acoustic model training tools (声学模型训练工具)
  • Sphinxbase — support library required by Pocketsphinx and Sphinxtrain (Pocketsphinx和Sphinxtrain的基础类库 )
  • Sphinx4 — adjustable, modifiable recognizer written in Java (Java语言开发的可调节、可修改的语音识别引擎)

这里我们下载Pocketsphinx和Sphinxbase,下载地址:

   https://sourceforge.net/projects/cmusphinx/files/sphinxbase/5prealpha/

   https://sourceforge.net/projects/cmusphinx/files/pocketsphinx/5prealpha/

3.2.编译

1)pocketsphinx依赖于sphinxbase,因此需要先编译sphinxbase。

使用VS2013打开sphinxbase.sln,直接点击生成解决方案即可

 

输出结果

 

2)使用VS2013打开pocketsphinx.sln,直接点击生成解决方案,结果报错了。。。

 

将sphinxbase的头文件目录以及.lib文件目录加入工程中,看错误估计是路径不对,继续编译就通过了。

至此,pocketsphinx的编译工作结束了

 

3.3.运行

将sphinxbase.dll复制到pocketsphinx运行目录下,不然会报缺少DLL的错误。

 

如果有麦克风可以运行一下命令:

pocketsphinx_continuous.exe -inmic yes -hmm model\en-us\en-us -lm model\en-us\en-us.lm.bin -dict model\en-us\cmudict-en-us.dict

 

也可以用文件来运行:

pocketsphinx_continuous.exe -infile C:\Users\Administrator\Desktop\pocketsphinx-5prealpha-win32\pocketsphinx\test\data\speech61-70968-0017.wav  -backtrace yes -hmm C:\Users\Administrator\Desktop\pocketsphinx-5prealpha-win32\pocketsphinx\model\en-us\en-us -lm C:\Users\Administrator\Desktop\pocketsphinx-5prealpha-win32\pocketsphinx\model\en-us\en-us.lm.bin -dict C:\Users\Administrator\Desktop\pocketsphinx-5prealpha-win32\pocketsphinx\model\en-us\cmudict-en-us.dict

运行结果:

截图中倒数第7行,即为识别结果,输入音频文件的原句如下:

   I COULD NOT SEE MY BOY INJURED EXCELLENCE FOR BUT DOING HIS DUTY AS ONE OF CUMBERLAND'S SONS

四 、模型的获取

1、在线生成语言模型和词典的工具 http://www.speech.cs.cmu.edu/tools/lmtool-new.html 

2、已经训练好的模型下载地址:https://sourceforge.net/projects/cmusphinx/files/Acoustic%20and%20Language%20Models/

  其中Mandarin为中文普通话,下载下来之后我们可以看到
  声学模型:zh_broadcastnews_16k_ptm256_8000.tar.bz2
  语言模型:zh_broadcastnews_64000_utf8.DMP
  拼音字典:zh_broadcastnews_utf8.dic


zh_broadcastnews_ptm256_8000目录结构
├── feat.params   //HMM模型的特征参数
├── mdef   //模型定义文件(为每个即将进行训练的HMM的每一状态定义一个独特的数字标识)
├── means  //混合高斯模型的均值
├── mixture_weights   //混合权重
├── noisedict    //噪声也就是非语音字典
├── sendump  //用来从声学模型中获取mixture_weights文件的?
├── transition_matrices  //HMM模型的状态转移矩阵
└── variances  //混合高斯模型的方差

其他的中文声学模型还有tdt_sc_8k,该模型可以在pocketsphinx-0.8-win32中找到。

2019-06-03 18:12:33 wangxunweihua 阅读数 581

最小均方 (LMS) 算法的自适应数字滤波器原理12

设横向自适应数字滤波器的输入为 x(n)x(n),理想输入为 d(n)d(n),实际输出为 y(n)y(n),滤波器的加权系数为 ωi(n),(i=0,1,...,M1)\omega_{i}(n),(i=0,1,...,M-1),那么 LMS 算法为:

y(n)=i=0M1ωi(n)x(ni)e(n)=d(n)y(n)ωi(n+1)=ωi(n)+2μe(n)x(ni),i=0,1,...,M1 \begin{aligned} y(n) & =\sum\limits_{i=0}^{M-1}\omega_{i}(n)x(n-i) \\ e(n) &=d(n)-y(n) \\ \omega_{i}(n+1)&=\omega_{i}(n)+2\mu e(n)x(n-i), i=0,1,...,M-1 \end{aligned}

其中 μ\mu 是收敛因子(学习率)。

MATLAB 编程

使用 matlab 对语音进行 LMS 降噪,这样的程序可以找到很多。由于 matlab 在工程方面的应用性能欠佳,最终还是使用 C 语言实现 LMS 的代码更好一些。但是为了对比,我们先写出 matlab 版本的代码,同时对该算法进行说明。

首先我们构造一个正弦信号并给它加上高斯白噪声。输入信号 x(i)x(i) 和理想的输出信号 d(i)d(i) 分别为:

d(n)=2sin2πi20+v(i)x(i)=d(i1) \begin{aligned} d(n) & =\sqrt2 \sin \frac{2\pi i}{20}+v(i) \\ x(i) &=d(i-1) \\ \end{aligned}

其中 v(i)v(i) 是均值为零、方差为 1 的高斯白噪声,正弦信号的数字频率为 0.05,信噪比 SNR = 1,x(i)x(i)d(i)d(i) 延迟一个采样间隔。为什么要这样做呢?有一些代码中,会专门给出一个纯净的信号,并以这个纯净的信号作为参考信号。但是这在现实中是很难获得的。如果你都已经获得纯净信号了,还要滤波有什么意义?所以很多实际应用的程序里面,会将包含噪声的输入信号做延迟,并将延迟后的信号作为输入信号,而原先包含噪声的信号作为纯净信号,一样可以达到自适应滤波的效果。我们这里选取样本数 n=500,滤波器长度 m=20,收敛因子 μ=0.0005\mu=0.0005

这个信号我们等会会给出构建程序,现在假定它已经被构造好,保存在了lms.txt文件中。我们就从这个文件里读取。对信号读取并处理的 MATLAB 代码如下:

close all;
clear all; 
clc; 

aaa = textread('lms.txt');             %读取文件
s = aaa(:,2);                          %这个文件中第二列才是真实的信号
s_expand=[s;0];                        %在信号序列最后加一个0
r1=s_expand;
s2 =[0;s];                             %在信号序列最前面加一个0,即把原始信号向后延迟1个点
M=20;                                  % 设置M和mu,M 是滤波器阶数
mu=0.0005;                             % mu 是自适应滤波里面的参数
itr=length(r1);
[W,e,y2]=LMS(s2,r1,M,mu,itr);
plot(s,'b','LineWidth',1); ylabel('幅值') 
ylim([-3.5 3.5 ]); title('原始语音信号');
hold on;
plot(y2,'r','LineWidth',2); 
ylim([-3.5 3.5 ]); title('LMS滤波输出语音信号');
xlabel('时间/s'); ylabel('幅值')

我们注意到其中使用了 LMS 函数用于滤波的处理,这个函数代码如下:

%LMS函数
function [W,en,y]=LMS(xn,dn,M,mu,itr)
% LMS(Least Mean Squre)算法
% 输入参数:
%     xn   输入的信号序列      (列向量)
%     dn   期望信号           (列向量)
%     M    滤波器的阶数        (标量)
%     mu   收敛因子(步长)      (标量)     要求大于0,小于xn的相关矩阵最大特征值的倒数    
%     itr  迭代次数           (标量)     默认为xn的长度,M<itr<length(xn)
% 输出参数:
%     W    滤波器的权值矩阵     (矩阵)
%          大小为M x itr,
%     en   误差序列(itr x 1)    (列向量)  
%     y    输出序列             (列向量)
% 初始化参数
en = zeros(itr,1);             % 误差序列,en(k)表示第k次迭代时预期输出与实际输入的误差
W  = zeros(M,itr);             % 每一行代表一个加权参量,每一列代表-次迭代,初始为0
% 迭代计算
for k = M:(itr-1)                  % 第k次迭代
    x = xn(k:-1:k-M+1);        % 滤波器M个抽头的输入
    y(k) = W(:,k).' * x;        % 滤波器的输出
    en(k) = dn(k) - y(k) ;        % 第k次迭代的误差
    % 滤波器权值计算的迭代式
    W(:,k+1) = W(:,k) + 2*mu*en(k)*x;
end

滤波结果画图如下,其中蓝色曲线是滤波之前的,红色曲线是滤波之后的。可以明显看出,滤波以后噪声有所减小。
在这里插入图片描述
我们查看输出 y 的数据如下:
在这里插入图片描述

C 语言编程

上面讲解了 MATLAB 的程序,现在来介绍一下对应的 C 语言程序。上面所提到的 lms.txt 这个文件中数据的产生,也包含在本 C 语言程序中。

#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include "lms.h"
#include "gauss.h"

int main(void)
{
    int i,m,n;
    long seed;
    double mu,pi,mean,sigma;
    static double d[501],x[501],y[501],w[50];
    FILE *fp;
    pi = 4.0*atan(1.0);
    mean = 0.0;
    sigma = 1.0;
    seed = 13579l;
    n = 500;
    for (i=0;i<n;i++)
    {
        d[i] = sqrt(2.0)*sin(2*pi*i/20.0);
        d[i]+= gauss(mean,sigma,&seed);
    }
    for (i=0;i<(n-1);i++)
    {
        x[i+1]=d[i];
    }
    fp = fopen("lms.txt","w");
    for (i=0;i<n;i++)
    {
        fprintf(fp,"%d %lf\n",i,d[i]);
    }
    fclose(fp);
    m = 20;
    mu = 0.0005;
    lms(x,d,y,n,w,m,mu);
    printf("\n The Coefficients of Adaptive Filter\n");
    for (i=0;i<m;i+=4)
    {
        printf("    %10.7f    %10.7f",w[i],w[i+1]);
        printf("    %10.7f    %10.7f",w[i+2],w[i+3]);
        printf("\n");
    }
    fp = fopen("lmsy.txt","w");
    for (i=0;i<n;i++)
    {
        fprintf(fp,"%d   %lf\n",i,y[i]);
    }
    fclose(fp);
	getchar();
    return 0;
}

我们留意到其中有一个 gauss() 函数,这个函数就是用于产生高斯噪声的,这个函数的源代码如下:

#include "uniform.h"

double gauss(double mean, double sigma, long int* seed)
{
	int i;
	double x, y;
	for (x=0, i=0; i<12; i++)
		x += uniform(0, 1, seed);
	x = x - 6;
	y = mean + x * sigma;
	return (y);
}

我们发现,其中又调用了 uniform 这个函数,这个函数的源代码如下:

double uniform(double a, double b, long int *seed)
{
	double t;
	*seed = 2045 * (*seed) + 1;
	*seed = *seed - (*seed / 1048576) * 1048576;
	t = (*seed) / 1048576.0;
	t = a + (b - a) * t;
	return (t);
}

下面给出 LMS 函数的源代码,这个源代码和 MATLAB 的代码尽可能保持一致, 方便我们进行对比。

void lms(double x[], double d[], double y[], int n, double w[], int m, double mu)
{
    double e[500];
    int i,k;
    for (i=0;i<m;i++)
    {
        w[i] = 0.0;
    }
	/*   //这一段即使不要,也可以自适应滤波
    for (k=0;k<m;k++)
    {
        y[k] = 0.0;
        for (i=0;i<=k;i++)
        {
            y[k]+=x[k-i]*w[i];
        }
        e = d[k]-y[k];
        for (i=0;i<=k;i++)
        {
            w[i]+=2.0*mu*e*x[k-i];
        }
    }
	*/
    for (k=(m-1);k<n;k++)  //原始程序里这里就是m,但是改成m-1好像也没问题
    {
        y[k] = 0.0;
        for (i=0;i<m;i++)
        {
            y[k]+=x[k-i]*w[i];
        }
        e[k] = d[k]-y[k];
        for (i=0;i<m;i++)
        {
            w[i]+=2.0*mu*e[k]*x[k-i];
        }
    }

}

C 语言的输出放在 lmsy.txt 这个函数中。我们打开这个文件,看到其中的数据:
在这里插入图片描述
通过逐行对比,我们发现这两种方法的结果是完全一样的,这证明我们的 C 语言程序是正确的。


  1. 数字信号处理C语言程序集,殷福亮,宋爱军,辽宁科技出版社,1997. ↩︎

  2. 语音信号处理实验教程,梁瑞宇,赵力,魏昕,机械工业出版社,2016. ↩︎

2018-12-13 21:56:59 openliu 阅读数 876

speex是一套主要针对语音处理的开源代码,它包括语音编解码器,VAD(语音活动检测),AEC(回声消除),NS(语音降噪)等模块。由于免费、无专利保护和开源的特性,又属于产品常用的基本算法,同时包括了浮点和对应的定点实现,Speex声名远扬成为广大音频算法开发者的入门武功。
        网上文档大多是简要介绍或者侧重于对其应用接口的介绍。 打算分两部分详细讲解Speex降噪代码,提供给对该算法感兴趣的读者做参考。考虑文章太长需要更多的写作时间,也不利于读者的碎片化阅读,因此每次文章不会太长。初步计划拆分成两个系列:系列一先讲解代码的实现细节,比如FFT函数的调用方法和数据排列特点,又比如math_approx.h包含的sin/cos/log/sqrt/atan/exp/pow等定点函数实现。系列二讲解算法细节,涉及噪声估计算法MMSE和语音存在概率估计算法,线性频率/Bark频率的互相转换等等。
        工具准备:
1、Speex经过多年的维护,版本较多。本文基于官网的SpeexDSP 1.2rc3,下载地址:https://www.speex.org/downloads/。Speex和SpeexDSP最新版本都是通过Git维护,下载地址:https://git.xiph.org/?p=speexdsp.git;a=summary。
2、编译器选择CodeBlocks或者VS2017 community版本。
3、安装Matlab或者Octave,或者在线运行https://octave-online.net/。

目前已完成代码如下,发布在微信公众号“音频算法与工程实践”。

Speex降噪代码分析2——FFT
Speex降噪代码分析3——定点代码spx_cos( )
Speex降噪代码分析4——再谈定点代码spx_cos( )
Speex降噪代码分析5——spx_acos( )代码
Speex降噪代码分析6——spx_acos( )原理    
Speex降噪代码分析7——_spx_cos_pi_2( )代码
Speex降噪代码分析8——spx_cos_norm
Speex降噪代码分析9——spx_atan01
Speex降噪代码分析10——spx_atan

 

2017-03-03 16:18:18 iTaacy 阅读数 12693

谱减法语音降噪的Python实现

本文是参考 [投稿]谱减法语音降噪原理 Matlab版本的代码,采用Python实现的。具体的降噪原理请参阅上文。

20190427 修改了部分逻辑错误,感谢 Jarvissshcjdnxjsjj

先来张图看看效果:
在这里插入图片描述
下面是谱减法语音降噪的Python实现

文件speech_enhanced.py

#!/usr/bin/env python
import numpy as np
import wave
import nextpow2
import math

# 打开WAV文档
f = wave.open("input_file.wav")
# 读取格式信息
# (nchannels, sampwidth, framerate, nframes, comptype, compname)
params = f.getparams()
nchannels, sampwidth, framerate, nframes = params[:4]
fs = framerate
# 读取波形数据
str_data = f.readframes(nframes)
f.close()
# 将波形数据转换为数组
x = np.fromstring(str_data, dtype=np.short)
# 计算参数
len_ = 20 * fs // 1000 # 样本中帧的大小
PERC = 50 # 窗口重叠占帧的百分比
len1 = len_ * PERC // 100  # 重叠窗口
len2 = len_ - len1   # 非重叠窗口
# 设置默认参数
Thres = 3
Expnt = 2.0
beta = 0.002
G = 0.9
# 初始化汉明窗
win = np.hamming(len_)
# normalization gain for overlap+add with 50% overlap
winGain = len2 / sum(win)

# Noise magnitude calculations - assuming that the first 5 frames is noise/silence
nFFT = 2 * 2 ** (nextpow2.nextpow2(len_))
noise_mean = np.zeros(nFFT)

j = 0
for k in range(1, 6):
    noise_mean = noise_mean + abs(np.fft.fft(win * x[j:j + len_], nFFT))
    j = j + len_
noise_mu = noise_mean / 5

# --- allocate memory and initialize various variables
k = 1
img = 1j
x_old = np.zeros(len1)
Nframes = len(x) // len2 - 1
xfinal = np.zeros(Nframes * len2)

# =========================    Start Processing   ===============================
for n in range(0, Nframes):
    # Windowing
    insign = win * x[k-1:k + len_ - 1]
    # compute fourier transform of a frame
    spec = np.fft.fft(insign, nFFT)
    # compute the magnitude
    sig = abs(spec)

    # save the noisy phase information
    theta = np.angle(spec)
    SNRseg = 10 * np.log10(np.linalg.norm(sig, 2) ** 2 / np.linalg.norm(noise_mu, 2) ** 2)


    def berouti(SNR):
        if -5.0 <= SNR <= 20.0:
            a = 4 - SNR * 3 / 20
        else:
            if SNR < -5.0:
                a = 5
            if SNR > 20:
                a = 1
        return a


    def berouti1(SNR):
        if -5.0 <= SNR <= 20.0:
            a = 3 - SNR * 2 / 20
        else:
            if SNR < -5.0:
                a = 4
            if SNR > 20:
                a = 1
        return a

    if Expnt == 1.0:  # 幅度谱
        alpha = berouti1(SNRseg)
    else:  # 功率谱
        alpha = berouti(SNRseg)
    #############
    sub_speech = sig ** Expnt - alpha * noise_mu ** Expnt;
    # 当纯净信号小于噪声信号的功率时
    diffw = sub_speech - beta * noise_mu ** Expnt
    # beta negative components

    def find_index(x_list):
        index_list = []
        for i in range(len(x_list)):
            if x_list[i] < 0:
                index_list.append(i)
        return index_list

    z = find_index(diffw)
    if len(z) > 0:
        # 用估计出来的噪声信号表示下限值
        sub_speech[z] = beta * noise_mu[z] ** Expnt
        # --- implement a simple VAD detector --------------
    if SNRseg < Thres:  # Update noise spectrum
        noise_temp = G * noise_mu ** Expnt + (1 - G) * sig ** Expnt  # 平滑处理噪声功率谱
        noise_mu = noise_temp ** (1 / Expnt)  # 新的噪声幅度谱
    # flipud函数实现矩阵的上下翻转,是以矩阵的“水平中线”为对称轴
    # 交换上下对称元素
    sub_speech[nFFT // 2 + 1:nFFT] = np.flipud(sub_speech[1:nFFT // 2])
    x_phase = (sub_speech ** (1 / Expnt)) * (np.array([math.cos(x) for x in theta]) + img * (np.array([math.sin(x) for x in theta])))
    # take the IFFT

    xi = np.fft.ifft(x_phase).real
    # --- Overlap and add ---------------
    xfinal[k-1:k + len2 - 1] = x_old + xi[0:len1]
    x_old = xi[0 + len1:len_]
    k = k + len2
# 保存文件
wf = wave.open('en_outfile.wav', 'wb')
# 设置参数
wf.setparams(params)
# 设置波形文件 .tostring()将array转换为data
wave_data = (winGain * xfinal).astype(np.short)
wf.writeframes(wave_data.tostring())
wf.close()




以上代码均可以去我的Github上找到。

以上代码中用到了nextpow2,其中n = nextpow2(x) 表示最接近x的2的n次幂,这里就不在贴出,有需要的朋友直接去Github查看。

语音识别的前端处理

阅读数 15578

没有更多推荐了,返回首页