上篇(webRTC中语音降噪模块ANS细节详解(一))讲了维纳滤波的基本原理。本篇先给出webRTC中ANS的基本处理过程,然后讲其中两步(即时域转频域和频域转时域)中的一些处理细节。

ANS的基本处理过程如下图1:

图1

从图1可以看出,处理过程主要分6步,具体如下:

1)  把输入的带噪信号从时域转到频域,主要包括分帧、加窗和短时傅里叶变换(STFT)等

2)  做初始噪声估计,基于估计出的噪声算先验信噪比和后验信噪比

3)  计算分类特征,这些特征包括似然比检验(LRT)、频谱平坦度和频谱差异。根据这些特征确定语音/噪声概率,从而判定当前信号是语音还是噪声。

4)  根据算出来的语音/噪声概率去更新噪声估计

5)  基于维纳滤波去噪

6)  把去噪后的信号从频域转换回时域,主要包括短时傅里叶逆变换(ISTFT)、加窗和重叠相加等。

我用于理解和调试的版本是以前的C版本,里面又分为浮点和定点两种实现方式。对于算法理解来说,最好看浮点实现的版本,因为它能和算法原理中的数学表达式很好的联系起来。定点实现中有很多诸如定标等工程实现上的技巧,跟数学表达式很难直接联系。部署时如有load等制约因素,最好用定点的实现,因为通常定点实现的load比浮点实现的小不少。ANS支持8k/16k/32k HZ等三种采样率。对于语音来说,最常用的是16k HZ的,本文以及后续的均设定采用率为16k HZ。语音信号处理时以帧为单位,ANS中一帧为10 ms,可以算出一帧是160个采样点。语音信号处理又通常在频域下进行的,因此先要把时域信号变成频域信号,处理后再把频域信号变回时域信号。时域信号变频域信号在ANS降噪处理过程的开始部分,频域信号变时域信号在ANS降噪处理过程的结束部分,但它们是相对称的,且它们与降噪处理算法无关,因此把它们放在一起讲。下面讲讲时频互转中的一些细节。

先看从时域信号变成频域信号。主要步骤是分帧、加窗和做短时傅里叶变换(STFT)。分帧上面说过,10 ms一帧,每帧160个采样点。加窗的目的是避免频谱泄漏。有多种窗函数,常见的有矩形窗、三角窗、汉宁(hanning)窗和海明(hamming)窗等。语音处理中常用的是汉宁窗和海明窗。ANS中用的是汉宁窗和矩形窗混在一起的混合窗。做STFT要求点数是2的N次方,现在每帧160个点,大于160的最近的2的N次方是256,所以STFT一次处理256个点(这也是代码中256(#define ANAL_BLOCKL_MAX  256)的由来)。现在每帧160个点,需要补成256个点。一种做法是在160个点后面补零补成256个点。ANS用了一种更好的方法。用上一帧的尾部的96个点来补从而形成256个点。这样从时域信号变成频域信号的处理流程如下图2:

图2

因为对256点做STFT,所以加窗的点数也是256。ANS用的是窗是汉宁和矩形混合窗。汉宁窗函数是w(n) = 0.5 * (1 + cos(2*pi*n / (N-1))),范围是(0,1),波形如下图3。

图3

这个混合窗是把192(96*2)点的汉宁窗在顶点处插入64点的幅值为1的矩形窗,从而形成256(256 = 192 + 64)点的混合窗,波形如下图4。

图4

至于为什么要这么做,后面讲频域转换到时域时再说。256个点的值与相应的窗函数相乘,得到要送进STFT处理的值。STFT处理后得到256个频点的值,这些值除了第0点和第N/2点(N=256,即第128点)点是实数外,其余点都是复数,且关于第N/2点共轭对称。因为共轭对称,一个点知道了,它的对称点就可以求出来。所以STFT处理后有(N/2 + 1)个点的值。这里N=256,STFT的输出是129个点的值。这也是代码中129(#define HALF_ANAL_BLOCKL  129)的由来。得到129个频点的值后还要算每个频点的幅度谱和能量等,用于后面降噪算法,具体处理如下面代码,已给出详细的注释,就不细说了。

在频域做完降噪处理后需要把信号从频域变回时域,即信号的重建或者合成,主要步骤是做短时傅里叶反变换(ISTFT)、加窗和重叠相加(overlap add, OLA)等,处理流程如下图5。

图5

先做ISTFT(短时傅里叶反变换),得到256点的实数值。这256点包括上一帧的尾部的96点,即有重叠。该怎么拼接保证声音连贯呢?上面讲从时域到频域变换时用的窗是汉宁矩形混合窗,汉宁窗前半部分(头部96点)类似于做正弦操作,后半部分(尾部96点)类似于做余弦操作。重叠部分是在上一帧的尾部,加窗做的是类余弦操作,在当前帧是头部,加窗做的是类正弦操作。信号重建叠加时一般要求能量或者幅值不变,能量是幅值的平方。那些重叠的点(假设幅值为m)在上一帧中加窗时做了类余弦操作,加窗后幅值变成了m*cosθ,在当前帧中加窗时做了类正弦操作,加窗后幅值变成了m*sinθ,能量和为m2*cos2θ + m2*sin2θ, 正好等于m2(原信号的能量),这说明只要把重叠部分相加就可以保证语音信号的连贯了。这就解释了代码中把ISTFT后的值再做一次加窗操作并把重叠部分相加的原因。具体代码见下图6。

图6

至于矩形窗部分,幅值为1,即加窗后信号幅值不变,因而不需要做处理,直接填上就可以了。需要注意的是图6中还有一个能量缩放因子factor。它在前200帧默认为1,后续帧按如下逻辑关系得到。

图7给出了做完ISTFT后数据拼接的示意图。做完ISTFT后有256点数据,当前帧的头部96点数据与上一帧的尾部96点数据相加,中间64点数据不变,当前帧尾部96点数据与下一帧的头部96点数据相加,这样就能很好的拼接处连贯的语音数据了。

图7

下篇将讲噪声的初始估计以及基于估计出来的噪声算先验信噪比和后验信噪比。

webRTC中语音降噪模块ANS细节详解(二)的更多相关文章

  1. webRTC中语音降噪模块ANS细节详解(三)

    上篇(webRTC中语音降噪模块ANS细节详解(二))讲了ANS的处理流程和语音在时域和频域的相互转换.本篇开始讲语音降噪的核心部分,首先讲噪声的初始估计以及基于估计出来的噪声算先验信噪比和后验信噪比 ...

  2. webRTC中语音降噪模块ANS细节详解(四)

    上篇(webRTC中语音降噪模块ANS细节详解(三))讲了噪声的初始估计方法以及怎么算先验SNR和后验SNR. 本篇开始讲基于带噪语音和特征的语音和噪声的概率计算方法和噪声估计更新以及基于维纳滤波的降 ...

  3. webRTC中语音降噪模块ANS细节详解(一)

    ANS(adaptive noise suppression) 是webRTC中音频相关的核心模块之一,为众多公司所使用.从2015年开始,我在几个产品中使用了webRTC的3A(AEC/ANS/AG ...

  4. python中argparse模块用法实例详解

    python中argparse模块用法实例详解 这篇文章主要介绍了python中argparse模块用法,以实例形式较为详细的分析了argparse模块解析命令行参数的使用技巧,需要的朋友可以参考下 ...

  5. Python中random模块生成随机数详解

    Python中random模块生成随机数详解 本文给大家汇总了一下在Python中random模块中最常用的生成随机数的方法,有需要的小伙伴可以参考下 Python中的random模块用于生成随机数. ...

  6. Java中堆内存和栈内存详解2

    Java中堆内存和栈内存详解   Java把内存分成两种,一种叫做栈内存,一种叫做堆内存 在函数中定义的一些基本类型的变量和对象的引用变量都是在函数的栈内存中分配.当在一段代码块中定义一个变量时,ja ...

  7. angular-ngSanitize模块-$sanitize服务详解

    本篇主要讲解angular中的$sanitize这个服务.此服务依赖于ngSanitize模块. 要学习这个服务,先要了解另一个指令: ng-bing-html. 顾名思义,ng-bind-html和 ...

  8. angular-ngSanitize模块-linky过滤器详解

    本篇主要讲解angular中的linky这个过滤器.此过滤器依赖于ngSanitize模块. linky能找出文本中的链接,然后把它转换成html链接.什么意思,就是说,一段文本里有一个链接,但是这个 ...

  9. View绘制详解(五),draw方法细节详解之View的滚动/滑动问题

    关于View绘制系列的文章已经完成了四篇了,前面四篇文章主要带小伙伴们熟悉一下View的体系的整体框架.View的测量以及布局等过程,从本篇博客开始,我们就来看看View的绘制过程.View的绘制涉及 ...

随机推荐

  1. Spring事务管理回滚问题

    Spring事务管理不能回滚问题 在前段时间学习SpringMVC的练习中,碰到声明式事务管理时,事务不能回滚的情况,通过查看博客和资料,解决了问题. 原因 导致Spring事务管理不能回滚的原因有两 ...

  2. 缩减Centos7xfs磁盘空间

    问题描述:df -h查看 root目录仅有20G空间,其余300G空间全在home目录下.xfs不可以直接缩减,所以只能删除xfs盘然后重新添加. 解决办法: 1. 注释想要删除的磁盘,此处以cent ...

  3. docker学习笔记(二)--配置镜像加速器

    前提:docker已经安装好 配置过程 进入至阿里云开发中心,https://dev.aliyun.com/,点击管理中心 管理中心中,点击左侧镜像加速器. 修改配置文件,使用加速器,根据我们目前Do ...

  4. elementUITable的多选框:分页选择数据回显,分页保存选中的数据。

    <template> <el-table @selection-change="handleSelectionChange" :row-key="get ...

  5. STM32F103C8T6使用SPI接口驱动WS2812b灯条

    之前一篇文章写了使用IO控制WS2812b操作原理,但是由于IO的输出比较慢,所以现在改用了硬件SPI控制WS2812b灯条 把SPI的mosi线接到ws2812b的数据线,SPI的速率可达十几Mbi ...

  6. Python程序调用摄像头实现人脸识别

    使用简单代码实现摄像头进行在线人脸识别 import cv2 import sys import logging as log import datetime as dt from time impo ...

  7. 快乐中秋,SQL小白入门指南

    目录 创建表 最基本的创建 怎么查看一个已经建好的表的信息呢 修改字段 插入数据 修改和删除数据 修改 删除 第一个查询 条件语句 使用age的大小比较,查看大于16岁的学生: 使用多个条件并联,大于 ...

  8. Influxdb数据库 - 基本操作

    InfluxDB数据库的简介 InfluxDB是一个用于存储和分析时间序列数据的开源数据库,是一个基于 golang 编写,用于记录 metrics.events,进行数据分析. 主要特性有: 内置H ...

  9. spring入门3-jdbcTemplate简单使用和声明式事务

    1.JdbcTemplate简单使用 1.1.引入相关依赖包 <dependency> <groupId>mysql</groupId> <artifactI ...

  10. phpmyadmin 设置密码

    例如 xampp 安装路径为 /opt/lampp/, copy 一份默认的配置 cp /opt/lampp/phpmyadmin/libraries/config.default.php /opt/ ...