VAD(Voice Activity Detection,语音活动检测) 是一种用于识别音频流中是否存在语音的技术。其核心目标是区分语音信号与非语音信号(如有声、静默等),应用于ASR语音识别、语音通信压缩、录音分段等场景。例如,在视频会议中,VAD 可以过滤掉静默或背景噪声,只传输有效语音数据,节省带宽;在送入语音识别之前,先通过 VAD切音并只送入有声片段,以提高识别效率。
VAD开源组件比较多,几乎都基于C+,或训练模型来完成。然嵌入到自己的应用工程中或许调试,应用会有一些磨合。
本文是基于VAD机制的原理,用java语言来实现通俗易懂的逻辑,不需要大量的代码过程,以达到清晰的熟知VAD原理的目的,且能用于切音的实时场景。对于想学习VAD原理的同学会有一定的帮助。
一、VAD有如下主要参数
# 最大连续停顿时长,毫秒。用户说话期间的停顿时长超过阈值,则表示用户一句话说完即可进行切音
vad.max.pause.ms=1000
# 最小说话时长,毫秒。说话时长小于阈值,则表示是无效声音忽略掉,从而不中断连续计算“vad.max.pause.ms”参数
# 超过最小说话时长才进入VAD切音处理
vad.min.speak.ms=200
# 最大说话时长,毫秒。说话时长(包括中通静默)超过阈值,则强行切音
vad.max.speak.ms=20000
# 最大静默时长,毫秒。超过阈值,则强行切音
vad.max.silence.ms=8000
# 最大静默电平值,电平单位dB,这里为负值。小于或等于此值即静默帧。
# 电平(Level)指音频信号的强弱程度
# 注意:电平数值为负值
# 设置的静默强度太高,即值越大,如-9、-8等值,则会出现漏音情况,会将有效的音频当成静默帧
# 设置的静默强度太低,即值越小,如-11、-12等值,则会出现将干扰音杂音当成有效音频而保留下来
vad.max.silence.db=-10
二、假设我已有一段pcm音频文件,(之所以要假设为pcm音频,是因为VAD应用在智能电话语音场景中比较多,而通信信道传输中,alaw、ulaw音频编码兼容性好,能在本地将其转换为pcm编码)
通过Audition软件打开一段pcm文件,如下图
该pcm文件的属性,采样率8k,采样位16,单声道
三、代码实现
大致过程是:首先对输入字节流按每帧来遍历,计算每帧的电平值dB,标记最小说话时长(minSpeakMS) 的起点帧。
再从起点帧开始重新遍历帧数组,标记连续静默帧数,连续有声帧数;当连续静默时长超过maxPauseFrames时,即开始切音。具体请看下面代码注解。
1、建立一个VAD类
import com.allen.common.util.AudioFloatConversion8S;
import java.io.File;
import java.io.FileInputStream;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class VADUtil {
// 每帧大小160字节,计算音频的信号强弱时以帧为单位
private static final int frameSize = 160;
// 采样率
private static final int sampleRate = 8000;
// 采样位
private static final int sampleSizeInBits = 8;
// 用于计算帧的电平值(信号强弱)
private static AudioFloatConversion8S audioConverter = new AudioFloatConversion8S();
// VAD的主要参数,详见上面说明
private int maxPauseMS;
private int minSpeakMS;
private int maxSpeakMS;
private int maxSilenceMS;
private float maxSilenceDB;
// 初始化参数,因在一段实时的音频流检测过程中,会不断的调用VAD,故事先实例化vad对象
public VADUtil(int maxPauseMS, int minSpeakMS, int maxSpeakMS, int maxSilenceMS, float maxSilenceDB) {
this.maxPauseMS = maxPauseMS;
this.minSpeakMS = minSpeakMS;
this.maxSpeakMS = maxSpeakMS;
this.maxSilenceMS = maxSilenceMS;
this.maxS