如何在VOSK语音识别中使用Wave文件作为输入
问题背景
我手头的项目需要获取录音文件,经代码处理后提取文件中的文本内容,再将提取得到的文本与其他文本进行匹配校验。当前遇到的问题是:代码无法正常读取、使用已录制的音频文件。
代码核心逻辑说明:
init函数是代码的核心基础逻辑,负责参数解析、模型加载、配置初始化verify函数用于确认语音与文本的匹配结果
原有实现代码
import argparse import json import os import queue import random import sys from difflib import SequenceMatcher import numpy as np import sounddevice as sd import vosk q = queue.Queue() def int_or_str(text): """Helper function for argument parsing.""" try: return int(text) except ValueError: return text def callback(indata, frames, time, status): """This is called (from a separate thread) for each audio block.""" if status: print(status, file=sys.stderr) q.put(bytes(indata)) def init(): parser = argparse.ArgumentParser(add_help=False) parser.add_argument( '-l', '--list-devices', action='store_true', help='show list of audio devices and exit') args, remaining = parser.parse_known_args() if args.list_devices: print(sd.query_devices()) parser.exit(0) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[parser]) parser.add_argument( '-f', '--filename', type=str, metavar='FILENAME', help='audio file to store recording to') parser.add_argument( '-m', '--model', type=str, metavar='MODEL_PATH', help='Path to the model') parser.add_argument( '-d', '--device', type=int_or_str, help='input device (numeric ID or substring)') parser.add_argument( '-r', '--samplerate', type=int, help='sampling rate') args = parser.parse_args(remaining) try: if args.model is None: args.model = "model" if not os.path.exists(args.model): print("Please download a model for your language from vosk official model page") print("and unpack as 'model' in the current folder.") parser.exit(0) if args.samplerate is None: device_info = sd.query_devices(args.device, 'input') # soundfile expects an int, sounddevice provides a float: args.samplerate = int(device_info['default_samplerate']) model = vosk.Model(args.model) if args.filename: dump_fn = open(args.filename, "wb") else: dump_fn = None except KeyboardInterrupt: print('\nDone') parser.exit(0) except Exception as e: parser.exit(type(e).__name__ + ': ' + str(e)) return model, args def verify(random_sentence, model, args): num, T_num, F_num, num_word = 0, 0, 0, 1 with sd.RawInputStream(samplerate=args.samplerate, blocksize=8000, device=args.device, dtype='int16', channels=1, callback=callback): rec = vosk.KaldiRecognizer(model, args.samplerate) print("{}) ".format(num_word), random_sentence, end='\n') print('=' * 30, end='\n') run = True while run: data = q.get() if rec.AcceptWaveform(data): res = json.loads(rec.FinalResult()) res['text'] = res['text'].replace('ي', 'ی') if SequenceMatcher(None, random_sentence, res['text']).ratio() > 0.65: T_num, num, num_word += 1 else: F_num, num, num_word += 1 run = False print('=' * 30) print('True Cases : {}\n False Cases : {}'.format(T_num, F_num)) if __name__ == "__main__": model, args = init() verify(random_sentences, model, args)
问题原因
原有代码仅支持从麦克风实时采集音频做识别,没有实现本地已录制音频文件的读取逻辑,所以无法直接使用本地音频文件做校验。
修复方案
前置依赖安装
先安装音频文件读取库:
pip install soundfile
核心修改点
- 移除原有实时流相关的队列、callback逻辑,新增本地音频文件读取逻辑
- 修改参数配置,新增本地音频文件路径必填参数
- 自动适配音频格式为vosk要求的16位单声道格式,兼容不同采样率的音频文件
修复后的完整代码
import argparse import json import os import sys from difflib import SequenceMatcher import soundfile as sf import vosk import numpy as np def int_or_str(text): """参数解析辅助函数""" try: return int(text) except ValueError: return text def init(): parser = argparse.ArgumentParser(add_help=False) parser.add_argument( '-l', '--list-devices', action='store_true', help='显示音频设备列表并退出') args, remaining = parser.parse_known_args() # 保留原有设备列表查询能力,不需要可删除 if args.list_devices: import sounddevice as sd print(sd.query_devices()) parser.exit(0) parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, parents=[parser]) parser.add_argument( '-a', '--audio-path', type=str, metavar='AUDIO_PATH', required=True, help='待识别的本地音频文件路径') parser.add_argument( '-m', '--model', type=str, metavar='MODEL_PATH', help='Vosk模型路径') parser.add_argument( '-r', '--samplerate', type=int, help='采样率,不填则自动读取音频文件的采样率') args = parser.parse_args(remaining) try: if args.model is None: args.model = "model" if not os.path.exists(args.model): print("请下载对应语言的Vosk模型,解压后放置在当前目录下命名为model文件夹") parser.exit(0) # 读取音频文件获取采样率 if args.samplerate is None: with sf.SoundFile(args.audio_path) as f: args.samplerate = f.samplerate model = vosk.Model(args.model) except KeyboardInterrupt: print('\n已退出') parser.exit(0) except Exception as e: parser.exit(type(e).__name__ + ': ' + str(e)) return model, args def verify(random_sentence, audio_path, model, args): T_num, F_num = 0, 0 rec = vosk.KaldiRecognizer(model, args.samplerate) rec.SetWords(True) # 逐块读取音频文件 with sf.SoundFile(audio_path) as f: # 自动转成单声道16位整型 if f.channels > 1: data = np.mean(f.read(dtype='int16'), axis=1, dtype='int16') else: data = f.read(dtype='int16') # 分块送入识别器 block_size = 8000 for i in range(0, len(data), block_size): block = data[i:i+block_size].tobytes() if rec.AcceptWaveform(block): break # 获取最终识别结果 res = json.loads(rec.FinalResult()) res['text'] = res['text'].replace('ي', 'ی') # 匹配校验 if SequenceMatcher(None, random_sentence, res['text']).ratio() > 0.65: T_num += 1 print("识别结果匹配:", res['text']) else: F_num += 1 print("识别结果不匹配:", res['text']) print('=' * 30) print('匹配成功数: {}\n匹配失败数: {}'.format(T_num, F_num)) if __name__ == "__main__": model, args = init() # 这里替换成你实际要匹配的目标文本 target_sentence = "你要匹配的目标文本内容" verify(target_sentence, args.audio_path, model, args)
使用方式
运行时通过参数指定音频路径即可:
python 脚本名.py -a 你的音频文件路径.wav
支持wav、flac等无压缩音频格式,若是mp3等压缩格式需要先转成wav格式再使用。
内容的提问来源于stack exchange,提问作者Ebrahim Mohammadi
相关产品推荐
相关产品推荐

