我想编写一个自动同步未同步字幕的程序。我想到的一个解决方案是用某种算法找到人类的语言,并根据它调整细微之处。我发现的API (Google Speech API, Yandex SpeechKit)与服务器(这对我来说不是很方便)一起工作,并且(可能)做了很多不必要的工作来确定到底说了什么,而我只需要知道已经说了什么。
换句话说,我想给它一个音频文件并得到像这样的东西:
[(00:12, 00:26), (01:45, 01:49) ... , (25:21, 26:11)]
是否有一个解决方案(最好是python),只发现人类语音和运行在本地机器上?
您正在尝试做的技术术语称为语音活动检测(VAD)。有一个叫做SPEAR的python库可以做到这一点。
webrtcvad是谷歌优秀的WebRTC语音活动检测(VAD)实现的Python包装-它在我使用的任何VAD中做得最好,就正确分类人类语音而言,即使是嘈杂的音频。
为了达到你的目的,你可以这样做:
- 将文件转换为8khz或16khz, 16位,单声道格式。这是WebRTC代码要求的。
- 创建VAD对象:
vad = webrtcvad.Vad()
- 将音频分成30毫秒的块。
- 检查每个块是否包含语音:
vad.is_speech(chunk, sample_rate)
VAD输出可能是"嘈杂的",如果它将单个30毫秒的音频块分类为语音,您真的不想为此输出时间。你可能想要查看过去0.3秒(或左右)的音频,看看这段时间内的30毫秒块是否被归类为语音。如果是,那么输出0.3秒周期的开始时间作为语音的开始。然后你做一些类似的事情来检测语音何时结束:等待0.3秒的音频周期,其中大部分30毫秒的块没有被VAD分类为语音——当这种情况发生时,输出结束时间作为语音结束。
你可能需要稍微调整一下时间来达到你的目的——也许你决定在触发前需要0.2秒的音频,其中超过30%的块被VAD分类为语音,在触发前需要1.0秒的音频,其中超过50%的块被分类为非语音。
环缓冲区(Python中的collections.deque
)是一种有用的数据结构,用于跟踪音频的最后N个块及其分类。
你可以在你的音频文件上运行一个窗口,并尝试提取总信号中人类声音频率的功率比例(基本频率介于50和300 Hz之间)。以下是给人的直观感受,未经真实音频测试。
import scipy.fftpack as sf
import numpy as np
def hasHumanVoice(X, threshold, F_sample, Low_cutoff=50, High_cutoff= 300):
""" Searching presence of frequencies on a real signal using FFT
Inputs
=======
X: 1-D numpy array, the real time domain audio signal (single channel time series)
Low_cutoff: float, frequency components below this frequency will not pass the filter (physical frequency in unit of Hz)
High_cutoff: float, frequency components above this frequency will not pass the filter (physical frequency in unit of Hz)
F_sample: float, the sampling frequency of the signal (physical frequency in unit of Hz)
threshold: Has to be standardized once to say how much power must be there in real vocal signal frequencies.
"""
M = X.size # let M be the length of the time series
Spectrum = sf.rfft(X, n=M)
[Low_cutoff, High_cutoff, F_sample] = map(float, [Low_cutoff, High_cutoff, F_sample])
#Convert cutoff frequencies into points on spectrum
[Low_point, High_point] = map(lambda F: F/F_sample * M, [Low_cutoff, High_cutoff])
totalPower = np.sum(Spectrum)
fractionPowerInSignal = np.sum(Spectrum[Low_point : High_point])/totalPower # Calculating fraction of power in these frequencies
if fractionPowerInSignal > threshold:
return 1
else:
return 0
voiceVector = []
for window in fullAudio: # Run a window of appropriate length across the audio file
voiceVector.append (hasHumanVoice( window, threshold, samplingRate)