在字级边界处分割音频信号
Posted
技术标签:
【中文标题】在字级边界处分割音频信号【英文标题】:Audio signal split at word level boundary 【发布时间】:2020-10-01 10:33:47 【问题描述】:我正在使用 webrtcvad 和 pydub 处理音频文件。任何片段的分裂都是通过句子的沉默来实现的。 有什么方法可以在字级边界条件下进行拆分? (在每个口语之后)? 如果 librosa/ffmpeg/pydub 有这样的功能,每个人声可以拆分吗?但是在拆分之后,我需要人声的开始和结束时间正是那个人声部分在原始文件中的位置。 一种由 ffmpeg 拆分的简单解决方案或方法也定义为:
https://gist.github.com/vadimkantorov/00bf4fbe4323360722e3d2220cc2915e
但这也是通过静默分割,并且对于每个填充数或帧大小,分割是不同的。我正在尝试按人声分割。 例如,我手动完成了原始文件、拆分词及其在 json 中的时间位置,位于链接下此处提供的文件夹中:
www.mediafire.com/file/u4ojdjezmw4vocb/attached_problem.tar.gz
【问题讨论】:
您的输入信号是语音(不是唱歌)吗?您能否提供一个示例音频文件,说明您想要分割点的位置? 我可以为您提供任何示例。作为音频。我对人声感兴趣,而不是特别是背景音乐。所以,即使有任何轻微的音乐。就像音频有两个句子。彼得是一名工程师。他每天工作八小时。通常情况下,分裂通常是在一个句子之后作为打破沉默。但我想像“Peter”、“is”、“an”、“Engineer”一样拆分。 “他”、“作品”、“八点”、“小时”、“每日”。忽略逗号这只是为了解释。但是,分割后的导出 wav 也应该有原始文件中那个人声的时间。 好的,这是一个语音分割任务,在单词边界 任何库都可以做到这一点吗?如果你能指导我怎么做。 如果您可以提供带有预期边界标记的示例文件,那么也许我(或其他人)可以提供一些示例代码 【参考方案1】:在将音频预处理为合适的特征之后,可以使用隐马尔可夫模型来处理简单的音频分割问题。语音的典型特征是声级、声音活动/浊音。为了获得词级分割(与句子相反),这需要具有相当高的时间分辨率。不幸的是,pyWebRTCVAD 没有可调整的时间平滑,因此它可能不适合该任务。
在您的音频样本中,有一位电台主持人用德语说得很快。 查看您标记的单词边界的声级很明显,在某些单词之间,声级并没有真正下降。这就排除了一个简单的声级分割模型。
总而言之,对一般语音信号获得良好的结果可能非常困难。但幸运的是,这已经得到了很好的研究,并且有现成的解决方案可用。 这些通常使用声学模型(单词和音素的发音方式)以及语言模型(可能的单词顺序),通过数小时的音频学习。
使用语音识别库进行分词
所有这些功能都包含在语音识别框架中,并且许多功能允许获得带有时间的单词级输出。下面是一些使用 Vosk 的工作代码。
Vosk 的替代品是PocketSphinx。或者使用来自 Google Cloud、Amazon Web Services、Azure Cloud 等的在线语音识别服务。
import sys
import os
import subprocess
import json
import math
# tested with VOSK 0.3.15
import vosk
import librosa
import numpy
import pandas
def extract_words(res):
jres = json.loads(res)
if not 'result' in jres:
return []
words = jres['result']
return words
def transcribe_words(recognizer, bytes):
results = []
chunk_size = 4000
for chunk_no in range(math.ceil(len(bytes)/chunk_size)):
start = chunk_no*chunk_size
end = min(len(bytes), (chunk_no+1)*chunk_size)
data = bytes[start:end]
if recognizer.AcceptWaveform(data):
words = extract_words(recognizer.Result())
results += words
results += extract_words(recognizer.FinalResult())
return results
def main():
vosk.SetLogLevel(-1)
audio_path = sys.argv[1]
out_path = sys.argv[2]
model_path = 'vosk-model-small-de-0.15'
sample_rate = 16000
audio, sr = librosa.load(audio_path, sr=16000)
# convert to 16bit signed PCM, as expected by VOSK
int16 = numpy.int16(audio * 32768).tobytes()
# XXX: Model must be downloaded from https://alphacephei.com/vosk/models
# https://alphacephei.com/vosk/models/vosk-model-small-de-0.15.zip
if not os.path.exists(model_path):
raise ValueError(f"Could not find VOSK model at model_path")
model = vosk.Model(model_path)
recognizer = vosk.KaldiRecognizer(model, sample_rate)
res = transcribe_words(recognizer, int16)
df = pandas.DataFrame.from_records(res)
df = df.sort_values('start')
df.to_csv(out_path, index=False)
print('Word segments saved to', out_path)
if __name__ == '__main__':
main()
使用 .WAV 文件和输出文件的路径运行程序。
python vosk_words.py attached_problem/main.wav out.csv
脚本以 CSV 格式输出单词及其时间。然后可以使用这些时间来分割音频文件。这是示例输出:
conf,end,start,word
0.618949,1.11,0.84,also
1.0,1.32,1.116314,eine
1.0,1.59,1.32,woche
0.411941,1.77,1.59,des
将输出(底部)与您提供的示例文件(顶部)进行比较,看起来还不错。
它实际上在 42.25 秒时选择了一个您的注释未包含的单词,“und”。
【讨论】:
亲爱的乔纳。你真的教会了我一门出色的技术。我无话可说谢谢你。我在 2020 年年底前学到的最大的一课就是学习 VOSK。我想问两件事。由于这是Apache 2.0,所以它的使用应该没有问题?另外,我可以用 vosk 训练我自己的模型吗?我看过一些关于它的文字,可以使用自己的数据自定义和训练引擎。 VOSK 似乎是 Apache 2.0 是的。但它使用了许多其他项目,因此您可能还想查看那些获得许可的项目。 至于用 VOSK 训练自己的模型,我真的不知道该怎么做。这个问题最好直接问他们 事实上,这对我来说是很好的向你学习。非常感谢。 !!! 该死,为什么我以前没有听说过 VOSK?!!邪恶的谷歌:)【参考方案2】:分隔单词超出了音频领域,需要一种智能。手动操作很容易,因为我们很聪明,并且确切地知道我们在寻找什么,但是自动化这个过程很困难,因为正如您已经注意到的那样,沉默不是(不仅,并非总是)一个单词分隔符。
在音频级别,我们只能找到一种解决方案,这需要分析信号的幅度并添加一些时间机制。例如,Protools 提供了一个名为 Strip Silence 的好工具,它可以根据信号幅度自动剪切音频区域。它始终将素材保持在时间线中的原始位置,自然每个区域都知道自己的持续时间。除了以 dB 为单位的阈值之外,为了防止创建太多区域,它在时域中提供了几个有用的参数:创建区域的最小长度、切割前的延迟(延迟从幅度通过的点计算低于阈值),重新打开门之前的反向延迟(延迟从幅度超过阈值的点向后计算)。
这对您来说可能是一个很好的起点。实施这样的系统可能不会 100 % 成功,但如果对扬声器的设置进行了很好的调整,您可以获得相当不错的比率。即使不完美,也将大大减少对手工工作的需求。
【讨论】:
以上是关于在字级边界处分割音频信号的主要内容,如果未能解决你的问题,请参考以下文章