端点检测是语音信号处理中的重要一环,是各种语音任务的基础。WebRTC是谷歌开发的VAD,是当前最有效、最先进和免费的产品之一。webrtcvad是WebRTC语音活动检测器(VAD)的python接口,能够有效支持python2和python3,它能够将区分一段语音分割中的静音帧和非静音帧。本文将使用webrtcvad将非静音帧识别出来,并将非静音帧存为新的wav文件。
首先,你需要对你的机器安装pip:
# 下载文件
wget https://bootstrap.pypa.io/get-pip.py --no-check-certificate
# 执行安装
python get-pip.py
安装好pip好,你可以使用pip安装webrtcvad:
pip install webrtcvad
import webrtcvad
vad = webrtcvad.Vad()
假设,在你的/usr/origin目录下有一个test.wav,里面包含静音帧和非静音帧,现在,你想通过webrtcvad切除test.wav里面的静音帧,将非静音帧写到一个新的目录/usr/clean下:
# -*- coding: utf-8 -*-
import collections
import contextlib
import sys
import os
import wave
import webrtcvad
AGGRESSIVENESS = 3
def read_wave(path):
"""Reads wave file.
Takes the path, and returns (PCM audio data, sample rate).
"""
with contextlib.closing(wave.open(path, 'rb')) as wf:
num_channels = wf.getnchannels()
assert num_channels == 1
sample_width = wf.getsampwidth()
assert sample_width == 2
sample_rate = wf.getframerate()
assert sample_rate in (8000, 16000, 32000)
pcm_data = wf.readframes(wf.getnframes())
return pcm_data, sample_rate
def write_wave(path, audio, sample_rate):
"""Writes a .wav file.
Takes path, PCM audio data, and sample rate.
"""
with contextlib.closing(wave.open(path, 'wb')) as wf:
wf.setnchannels(1)
wf.setsampwidth(2)
wf.setframerate(sample_rate)
wf.writeframes(audio)
class Frame(object):
"""Represents a "frame" of audio data."""
def __init__(self, bytes, timestamp, duration):
self.bytes = bytes
self.timestamp = timestamp
self.duration = duration
def frame_generator(frame_duration_ms, audio, sample_rate):
"""Generates audio frames from PCM audio data.
Args:
frame_duration_ms: The desired frame duration in milliseconds.
audio: The PCM data.
sample_rate: The sample rate
"""
n = int(sample_rate * (frame_duration_ms / 1000.0) * 2)
offset = 0
timestamp = 0.0
duration = (float(n) / sample_rate) / 2.0
while offset + n < len(audio):
yield Frame(audio[offset:offset + n], timestamp, duration)
timestamp += duration
offset += n
def vad_collector(sample_rate, vad, frames):
"""Filters out non-voiced audio frames.
Args:
sample_rate: The audio sample rate, in Hz.
vad: An instance of webrtcvad.Vad.
frames: A source of audio frames (sequence or generator).
Returns: A generator that yields PCM audio data.
"""
voiced_frames = []
for idx, frame in enumerate(frames):
is_speech = vad.is_speech(frame.bytes, sample_rate)
if is_speech:
voiced_frames.append(frame)
return b''.join([f.bytes for f in voiced_frames])
def voiced_frames_expand(voiced_frames, duration=2):
total = duration * 8000 * 2
expanded_voiced_frames = voiced_frames
while len(expanded_voiced_frames) < total:
expand_num = total - len(expanded_voiced_frames)
expanded_voiced_frames += voiced_frames[:expand_num]
return expanded_voiced_frames
def filter(wavpath, out_dir, expand=False):
'''Apply vad with wave file.
Args:
wavpath: The input wave file.
out_dir: The directory that contains the voiced audio.
expand: Expand the frames or not, default False.
'''
print("wavpath:", wavpath)
audio, sample_rate = read_wave(wavpath)
print('sample rate:%d'%sample_rate)
vad = webrtcvad.Vad(AGGRESSIVENESS)
frames = frame_generator(30, audio, sample_rate)
frames = list(frames)
voiced_frames = vad_collector(sample_rate, vad, frames)
voiced_frames = voiced_frames_expand(voiced_frames, 2) if expand else voiced_frames
wav_name = wavpath.split('/')[-1]
save_path = out_dir + '/' + wav_name
write_wave(save_path, voiced_frames, sample_rate)
def main():
in_wav = '/usr/origin/test.wav'
out_dir = '/usr/clean'
filter(in_wav, out_dir, expand=False)
# 你会在你out_dir目录下得到经过vad的test.wav文件
if __name__ == '__main__':
main()