如何在 Python 中进行实时语音 activity 检测?
How can I do real-time voice activity detection in Python?
我正在对录制的音频文件执行语音 activity 检测,以检测波形中的语音与非语音部分。
分类器的输出看起来像(突出显示的绿色区域表示语音):
我在这里面临的唯一问题是让它适用于音频输入流(例如:来自麦克风)并在规定的时间范围内进行实时分析。
我知道PyAudio
可以用来动态记录麦克风的语音,还有一些波形、频谱、频谱图等的实时可视化示例,但找不到任何与携带相关的东西以近乎实时的方式进行特征提取。
音频的比特率通常较低,因此我认为将您的代码完全写成 numpy
and python
. And if you need low-level array access consider numba
. Also profile your code e.g. with line_profiler
. ALso note there is scipy.signal
以获得更高级的信号处理没有任何问题。
通常音频处理工作在样本中。所以你为你的过程定义了一个样本大小,然后 运行 一种方法来决定该样本是否包含语音。
import numpy as np
def main_loop():
stream = <create stream with your audio library>
while True:
sample = stream.readframes(<define number of samples / time to read>)
print(is_speech(sample))
def is_speech(sample):
audio = np.array(sample)
< do you processing >
# e.g. simple loudness test
return np.any(audio > 0.8):
这应该会让你走得更远。
我认为这里有两种方法,
- 阈值方法
- 小型、可部署的神经网络。方法
第一个很快,可行并且可以实施并且测试非常快。而第二个实施起来有点困难。我想你已经对第二个选项有点熟悉了。
在第二种方法的情况下,您将需要一个语音数据集,这些语音数据集被标记为 二元分类 序列,例如 00000000111111110000000011110000
。神经网络应该很小,并针对移动设备等边缘设备上的 运行 进行了优化。
您可以从 TensorFlow
查看 this
This 是语音 activity 检测器。我认为这是为了你的目的。
另外,看看这些。
https://github.com/eesungkim/Voice_Activity_Detector
https://github.com/pyannote/pyannote-audio
当然,您应该比较上述工具包和模型的性能与实现可行性 的移动设备。
您应该尝试使用 Python 绑定到 webRTC VAD from Google。它基于 GMM 建模,轻巧、快速并提供非常合理的结果。由于决策是按帧提供的,因此延迟最小。
# Run the VAD on 10 ms of silence. The result should be False.
import webrtcvad
vad = webrtcvad.Vad(2)
sample_rate = 16000
frame_duration = 10 # ms
frame = b'\x00\x00' * int(sample_rate * frame_duration / 1000)
print('Contains speech: %s' % (vad.is_speech(frame, sample_rate))
此外,this 文章可能对您有用。
发现LibROSA could be one of the solutions to your problem. There's a simple tutorial on Medium关于使用Microphone streaming实现实时预测
让我们使用短时傅立叶变换 (STFT) 作为特征提取器,作者解释道:
To calculate STFT, Fast Fourier transform window size(n_fft) is used
as 512. According to the equation n_stft = n_fft/2 + 1, 257 frequency
bins(n_stft) are calculated over a window size of 512. The window is
moved by a hop length of 256 to have a better overlapping of the
windows in calculating the STFT.
stft = np.abs(librosa.stft(trimmed, n_fft=512, hop_length=256, win_length=512))
# Plot audio with zoomed in y axis
def plotAudio(output):
fig, ax = plt.subplots(nrows=1,ncols=1, figsize=(20,10))
plt.plot(output, color='blue')
ax.set_xlim((0, len(output)))
ax.margins(2, -0.1)
plt.show()
# Plot audio
def plotAudio2(output):
fig, ax = plt.subplots(nrows=1,ncols=1, figsize=(20,4))
plt.plot(output, color='blue')
ax.set_xlim((0, len(output)))
plt.show()
def minMaxNormalize(arr):
mn = np.min(arr)
mx = np.max(arr)
return (arr-mn)/(mx-mn)
def predictSound(X):
clip, index = librosa.effects.trim(X, top_db=20, frame_length=512, hop_length=64) # Empherically select top_db for every sample
stfts = np.abs(librosa.stft(clip, n_fft=512, hop_length=256, win_length=512))
stfts = np.mean(stfts,axis=1)
stfts = minMaxNormalize(stfts)
result = model.predict(np.array([stfts]))
predictions = [np.argmax(y) for y in result]
print(lb.inverse_transform([predictions[0]])[0])
plotAudio2(clip)
CHUNKSIZE = 22050 # fixed chunk size
RATE = 22050
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32, channels=1,
rate=RATE, input=True, frames_per_buffer=CHUNKSIZE)
#preprocessing the noise around
#noise window
data = stream.read(10000)
noise_sample = np.frombuffer(data, dtype=np.float32)
print("Noise Sample")
plotAudio2(noise_sample)
loud_threshold = np.mean(np.abs(noise_sample)) * 10
print("Loud threshold", loud_threshold)
audio_buffer = []
near = 0
while(True):
# Read chunk and load it into numpy array.
data = stream.read(CHUNKSIZE)
current_window = np.frombuffer(data, dtype=np.float32)
#Reduce noise real-time
current_window = nr.reduce_noise(audio_clip=current_window, noise_clip=noise_sample, verbose=False)
if(audio_buffer==[]):
audio_buffer = current_window
else:
if(np.mean(np.abs(current_window))<loud_threshold):
print("Inside silence reign")
if(near<10):
audio_buffer = np.concatenate((audio_buffer,current_window))
near += 1
else:
predictSound(np.array(audio_buffer))
audio_buffer = []
near
else:
print("Inside loud reign")
near = 0
audio_buffer = np.concatenate((audio_buffer,current_window))
# close stream
stream.stop_stream()
stream.close()
p.terminate()
代码来源:Chathuranga Siriwardhana
可以找到完整代码 here。
我正在对录制的音频文件执行语音 activity 检测,以检测波形中的语音与非语音部分。
分类器的输出看起来像(突出显示的绿色区域表示语音):
我在这里面临的唯一问题是让它适用于音频输入流(例如:来自麦克风)并在规定的时间范围内进行实时分析。
我知道PyAudio
可以用来动态记录麦克风的语音,还有一些波形、频谱、频谱图等的实时可视化示例,但找不到任何与携带相关的东西以近乎实时的方式进行特征提取。
音频的比特率通常较低,因此我认为将您的代码完全写成 numpy
and python
. And if you need low-level array access consider numba
. Also profile your code e.g. with line_profiler
. ALso note there is scipy.signal
以获得更高级的信号处理没有任何问题。
通常音频处理工作在样本中。所以你为你的过程定义了一个样本大小,然后 运行 一种方法来决定该样本是否包含语音。
import numpy as np
def main_loop():
stream = <create stream with your audio library>
while True:
sample = stream.readframes(<define number of samples / time to read>)
print(is_speech(sample))
def is_speech(sample):
audio = np.array(sample)
< do you processing >
# e.g. simple loudness test
return np.any(audio > 0.8):
这应该会让你走得更远。
我认为这里有两种方法,
- 阈值方法
- 小型、可部署的神经网络。方法
第一个很快,可行并且可以实施并且测试非常快。而第二个实施起来有点困难。我想你已经对第二个选项有点熟悉了。
在第二种方法的情况下,您将需要一个语音数据集,这些语音数据集被标记为 二元分类 序列,例如 00000000111111110000000011110000
。神经网络应该很小,并针对移动设备等边缘设备上的 运行 进行了优化。
您可以从 TensorFlow
查看 thisThis 是语音 activity 检测器。我认为这是为了你的目的。
另外,看看这些。
https://github.com/eesungkim/Voice_Activity_Detector
https://github.com/pyannote/pyannote-audio
当然,您应该比较上述工具包和模型的性能与实现可行性 的移动设备。
您应该尝试使用 Python 绑定到 webRTC VAD from Google。它基于 GMM 建模,轻巧、快速并提供非常合理的结果。由于决策是按帧提供的,因此延迟最小。
# Run the VAD on 10 ms of silence. The result should be False.
import webrtcvad
vad = webrtcvad.Vad(2)
sample_rate = 16000
frame_duration = 10 # ms
frame = b'\x00\x00' * int(sample_rate * frame_duration / 1000)
print('Contains speech: %s' % (vad.is_speech(frame, sample_rate))
此外,this 文章可能对您有用。
发现LibROSA could be one of the solutions to your problem. There's a simple tutorial on Medium关于使用Microphone streaming实现实时预测
让我们使用短时傅立叶变换 (STFT) 作为特征提取器,作者解释道:
To calculate STFT, Fast Fourier transform window size(n_fft) is used as 512. According to the equation n_stft = n_fft/2 + 1, 257 frequency bins(n_stft) are calculated over a window size of 512. The window is moved by a hop length of 256 to have a better overlapping of the windows in calculating the STFT.
stft = np.abs(librosa.stft(trimmed, n_fft=512, hop_length=256, win_length=512))
# Plot audio with zoomed in y axis
def plotAudio(output):
fig, ax = plt.subplots(nrows=1,ncols=1, figsize=(20,10))
plt.plot(output, color='blue')
ax.set_xlim((0, len(output)))
ax.margins(2, -0.1)
plt.show()
# Plot audio
def plotAudio2(output):
fig, ax = plt.subplots(nrows=1,ncols=1, figsize=(20,4))
plt.plot(output, color='blue')
ax.set_xlim((0, len(output)))
plt.show()
def minMaxNormalize(arr):
mn = np.min(arr)
mx = np.max(arr)
return (arr-mn)/(mx-mn)
def predictSound(X):
clip, index = librosa.effects.trim(X, top_db=20, frame_length=512, hop_length=64) # Empherically select top_db for every sample
stfts = np.abs(librosa.stft(clip, n_fft=512, hop_length=256, win_length=512))
stfts = np.mean(stfts,axis=1)
stfts = minMaxNormalize(stfts)
result = model.predict(np.array([stfts]))
predictions = [np.argmax(y) for y in result]
print(lb.inverse_transform([predictions[0]])[0])
plotAudio2(clip)
CHUNKSIZE = 22050 # fixed chunk size
RATE = 22050
p = pyaudio.PyAudio()
stream = p.open(format=pyaudio.paFloat32, channels=1,
rate=RATE, input=True, frames_per_buffer=CHUNKSIZE)
#preprocessing the noise around
#noise window
data = stream.read(10000)
noise_sample = np.frombuffer(data, dtype=np.float32)
print("Noise Sample")
plotAudio2(noise_sample)
loud_threshold = np.mean(np.abs(noise_sample)) * 10
print("Loud threshold", loud_threshold)
audio_buffer = []
near = 0
while(True):
# Read chunk and load it into numpy array.
data = stream.read(CHUNKSIZE)
current_window = np.frombuffer(data, dtype=np.float32)
#Reduce noise real-time
current_window = nr.reduce_noise(audio_clip=current_window, noise_clip=noise_sample, verbose=False)
if(audio_buffer==[]):
audio_buffer = current_window
else:
if(np.mean(np.abs(current_window))<loud_threshold):
print("Inside silence reign")
if(near<10):
audio_buffer = np.concatenate((audio_buffer,current_window))
near += 1
else:
predictSound(np.array(audio_buffer))
audio_buffer = []
near
else:
print("Inside loud reign")
near = 0
audio_buffer = np.concatenate((audio_buffer,current_window))
# close stream
stream.stop_stream()
stream.close()
p.terminate()
代码来源:Chathuranga Siriwardhana
可以找到完整代码 here。