python 语音学习-Vad检测+滑动平均(剔除语音空白)

功能

将一段语音中空白处剔除

代码详解

from scipy.ndimage.morphology import binary_dilation
import librosa
import numpy as np
import struct
import librosa.display
import webrtcvad
import soundfile as sf

// ** a的b次方  32767
int16_max = (2 ** 15) - 1
#输入
wav, source_sr = librosa.load("chunk123.wav", sr=None)

// 计算语音检测窗口大小  //为整除 30秒X16000=总帧长
samples_per_window = (30 * 16000) // 1000

// 修剪音频的结尾,使其具有窗口大小的倍数。使wav的长度能被 samples_per_window整除
wav = wav[:len(wav) - (len(wav) % samples_per_window)]

// 浮点数波形转换为16位单声道PCM  *:接收到的参数会形成一个元组,**:接收到的参数会形成一个字典。如下代码。
// webrtcvad 的 is_speech 接收的是buf 所以这里需要转换
pcm_wave = struct.pack("%dh" % len(wav), *(np.round(wav * int16_max)).astype(np.int16))

// 执行语音激活检测
voice_flags = []
//  这里共有三种帧长可以用到,分别是80/10ms,160/20ms,240/30ms。其它采样率
// 的48k,32k,24k,16k会重采样到8k来计算VAD。之所以选择上述三种帧长度,是因为语
// 音信号是短时平稳信号,其在10ms~30ms之间可看成平稳信号,高斯马尔科夫等比较
// 的信号处理方法基于的前提是信号是平稳的,在10ms~30ms,平稳信号处理方法是可
// 以使用的。
//   从vad的代码中可以看出,实际上,系统只处理默认10ms,20ms,30ms长度的数据,
// 其它长度的数据没有支持,笔者修改过可以支持其它在10ms-30ms之间长度的帧长度
// 发现也是可以的。
//   vad检测共四种模式,用数字0~3来区分,激进程度与数值大小正相关。
// 0: Normal,1:low Bitrate, 2:Aggressive;3:Very Aggressive 可以根据实际的使用
vad = webrtcvad.Vad(mode=3)
for window_start in range(0, len(wav), samples_per_window):
    window_end = window_start + samples_per_window
    // append 进来的都是Boolean  这里以samples_per_windowx2 的长度去检测是否为人声
    voice_flags.append(vad.is_speech(pcm_wave[window_start * 2:window_end * 2],
                                     sample_rate=16000))
voice_flags = np.array(voice_flags)
// 𝑣_𝑏𝑖𝑎𝑠𝑒𝑑𝑡=𝑣𝑡/(1−𝛽𝑡)
// 滑动平均计算
def moving_average(array, width):
    // 拼接 bool 二值化
    // width 执行滑动平均平滑时,帧的平均数。
    // 该值越大,VAD变化必须越大才能平滑。
    array_padded = np.concatenate((np.zeros((width - 1) // 2), array, np.zeros(width // 2)))
    // 一维数组累加
    ret = np.cumsum(array_padded, dtype=float)
    ret[width:] = ret[width:] - ret[:-width]
    return ret[width - 1:] / width

//滑动平均计算
audio_mask = moving_average(voice_flags, 8)
//将平均数四舍五入 转bool
audio_mask = np.round(audio_mask).astype(np.bool)
// 扩张浊音区 使用多维二元膨胀 是数学形态学的方法 类似opencv 也有开闭运算 腐蚀膨胀
// 举个栗子
// >>> struct1 = ndimage.generate_binary_structure(2, 1)
//     >>> struct1
//     array([[False,  True, False],
//            [ True,  True,  True],
//            [False,  True, False]], dtype=bool)
//     >>> # 3x3 structuring element with connectivity 2
//     >>> struct2 = ndimage.generate_binary_structure(2, 2)
//     >>> struct2
//     array([[ True,  True,  True],
//            [ True,  True,  True],
//            [ True,  True,  True]], dtype=bool)
//     >>> ndimage.binary_dilation(a, structure=struct1).astype(a.dtype)
//     array([[ 0.,  0.,  0.,  0.,  0.],
//            [ 0.,  0.,  1.,  0.,  0.],
//            [ 0.,  1.,  1.,  1.,  0.],
//            [ 0.,  0.,  1.,  0.,  0.],
//            [ 0.,  0.,  0.,  0.,  0.]])
//     >>> ndimage.binary_dilation(a, structure=struct2).astype(a.dtype)
//     array([[ 0.,  0.,  0.,  0.,  0.],
//            [ 0.,  1.,  1.,  1.,  0.],
//            [ 0.,  1.,  1.,  1.,  0.],
//            [ 0.,  1.,  1.,  1.,  0.],
//            [ 0.,  0.,  0.,  0.,  0.]])
audio_mask = binary_dilation(audio_mask, np.ones(6 + 1))
//使其与wav一样大小
audio_mask = np.repeat(audio_mask, samples_per_window)
//通过这个遮罩扣掉没有声音那部分
res=wav[audio_mask == True]
// 输出
sf.write("1234.wav", res.astype(np.float32), 16000, subtype='PCM_24')
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值