如何实现语音通话中的声音降噪?(附源码)

无论是在音视频通话系统、或是视频会议系统中,对从麦克风采集到的说话的声音数据进行预处理,都是是非常必要的。语音数据预处理主要包括:​​降噪(Noise Reduction)、静音检测(Silence Detection/VAD)、自动增益(Automatic Gain Control, AGC)​​ 。

一. 语音预处理的作用

那么,降噪、静音检测、自动增益,这些语音预处理分别起什么作用了?

(1)降噪

      降噪,用于消除背景噪声,比如马路车流声、环境杂音等,以保留清晰的说话人声。

      更高级的,结合AI模型训练,还可以消除电脑的风扇声、键盘敲击声等等。

(2)静音检测

      静音检测,又称为语音活动检测,用于识别音频流中的静音片段(没有讲话人声),这样可以简化后续的编码等环节,并可以节省传递所需要的带宽。

(3)自动增益

      自动增益,用于动态调整说话声音的音量,使输出电平保持稳定,以避免讲话的声音忽大忽小。

二. 实现语音预处理

接下来,我们使用C#实现一个Demo,这个Demo将从麦克风采集声音数据,然后进行语音预处理,并且将处理后的声音数据实时播放出来。Demo的运行效果如下图所示:

      

Demo 功能很简单,那我们来具体看看代码是如何实现的。

1. 创建采集器、预处理器、播放器

麦克风声音数据采样率我们选择16K、单声道。 

WaveSampleRate sr = WaveSampleRate.S16k;
int channelCount = 1;

//创建语音预处理器,开启降噪、自动增益、静音检测
this.voicePreprocessor = CapturerFactory.CreateVoicePreprocessor(sr, channelCount, true ,true);
//创建麦克风采集器
this.microphoneCapturer = CapturerFactory.CreateMicrophoneCapturer(int.Parse(this.textBox_mic.Text), sr);
this.microphoneCapturer.AudioCaptured += new ESBasic.CbGeneric<byte[]>(microphoneCapturer_AudioCaptured);
//创建声音播放器
this.audioPlayer = PlayerFactory.CreateAudioPlayer(int.Parse(this.textBox_speaker.Text), (int)sr, channelCount, 16, 2);                

this.microphoneCapturer.Start();

CreateVoicePreprocessor 方法的最后两个参数可以指定在降噪的同时,是否开启静音检测和自动增益功能。

2. 预处理语音数据

语音预处理器每次处理10ms的声音数据,而现在的麦克风采集器每次采集的是20ms的PCM数据,所以,我们将其拆成两个10ms数据,再提交给预处理器处理。

void microphoneCapturer_AudioCaptured(byte[] audioData)
{
    if (this.checkBox_enabled.Checked)
    {
        //麦克风每次采集20ms数据,降噪器每次处理10ms数据。
        byte[] frame10ms1 = new byte[audioData.Length / 2];
        byte[] frame10ms2 = new byte[audioData.Length / 2];
        Buffer.BlockCopy(audioData, 0, frame10ms1, 0, frame10ms1.Length);
        Buffer.BlockCopy(audioData, frame10ms1.Length, frame10ms2, 0, frame10ms2.Length);
        this.HandleData(frame10ms1);
        this.HandleData(frame10ms2);
        return;
    }
 
    this.audioPlayer.Play(audioData);
}

(1)通过一个CheckBox勾选框来实时控制是否启用语音预处理,这样在测试时,就可以很方便的对比体验开启了语音预处理的效果。

(2)调用IVoicePreprocessor 的 Process 方法,就可以完成一帧语音数据(10ms)的预处理。如下所示:

private void HandleData(byte[] frame10ms)
{
    byte[] res = this.voicePreprocessor.Process(frame10ms);            
    if (res == null) //静音帧
    {
        ++this.silenceFrameCountTotal;
        this.audioPlayer.Play(this.voicePreprocessor.SlienceFrame);
    }
    else
    {
        this.audioPlayer.Play(res);
    }
}

如果Process 方法返回的是null,表示检测到该帧是静音帧,于是,将内置的10ms静音帧 SlienceFrame 提交给播放器去播放。

3. 统计静音帧数量

一个语音帧是10ms,那么1秒钟就有100个语音帧,程序中,我们统计了上一秒出现了多少个静音帧,并在UI左下方显示出来。

private volatile int silenceFrameCountTotal = 0;
private volatile int silenceFrameCountPre = 0;
private void timer1_Tick(object sender, EventArgs e)
{
    int delt = this.silenceFrameCountTotal - this.silenceFrameCountPre;
    this.silenceFrameCountPre = this.silenceFrameCountTotal;
    //显示上一秒静音帧数量。
    this.label_silenceFrameCount.Text = delt.ToString();
}

实际测试时可以发现,当不说话时,UI实时显示1秒钟出现的静音帧是100个。

三. Demo源码下载

源码下载:VoicePreprocessDemo.rar

如果不想打开VS,可以直接到Debug目录下,双击 Oraycn.VoicePreprocessDemo.exe 即可运行Demo,开始体验语音降噪、静音检测、自动增益的处理效果。

建议使用耳麦测试,对比效果会更明显。当开启预处理时,能立即感觉到背景噪音消失了,而且说话的声音变大了(AGC),不说话时,UI显示静音帧的数量变多。

来下载试试语音预处理的效果吧。

目前,语音降噪算法有很多种。频谱减法有原理简单、容易实现的优点,是 语音降噪的常用算法。但是频谱减法也有如下两个缺点:一是频谱减法性能的好 坏主要依赖于噪声估计,而噪声估计又依赖于端点检测算法。在噪声水平强度高 时,一般的端点检测算法会失效,无法检测出信号中噪声帧的具体位置,从而影 响了噪声估计值的准确性;二是带噪信号经过频谱减法降噪后,由于在谱减时减 去的是同一噪声估计值,就使得信号会随机出现分离的谱区,这些谱区就形成了 容易让人耳听觉疲惫的“音乐噪声”。 针对频谱减法上述的两个缺点,本文对其进行了改进。第一:为了使得噪声 端点检测算法在噪声水平高时也能获得正确的检测,我们求带噪信号的幅度值均 值,并根据这个均值与带噪信号开始数帧的幅度均值大小来判断带噪信号是以噪 声开始还是以带噪语音信号开始。然后根据连续两帧信号的差值的变化来判断噪 声帧和语音帧的起始位置,同时我们在判断的同时把得到的均值做为噪声估计值, 这样既考虑到了连续前后两帧信号的相关性又能够衰减噪声。除此之外,基于本 文改进的噪声端点检测方法的噪声估计值能够在整个带噪语音信号上快速的更新 噪声估计值,提高频谱减法的实时处理能力。第二:为了减少频谱减法所引入的 音乐噪声,我们实现了用 LMS 算法在时域上进行语音增强,来处理谱减后的降噪 信号。LMS 算法能够在降低噪声水平的同时把音乐噪声转换为能量更低的白噪声, 减少了音乐噪声对人耳的刺激,有助于提高处理后的音频的语音质量,提高主客 观评价效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值