1
0
Fork 0
mirror of https://github.com/EDeev/api_processor.git synced 2026-10-07 20:49:34 +03:00

Распознавание речи: исправлены ошибки

- любой файл, кроме WAV 16 кГц моно, падал с NameError: вызывался несуществующий
  convert.convert_audio_to_wav;
- из длинной записи оставалась только последняя фраза: бралось лишь FinalResult(), а Vosk
  отдаёт текст по фразам через Result();
- общий временный audio.wav в текущей папке — одновременные запросы перезаписывали друг
  другу аудио; теперь свой временный файл на запрос;
- ffmpeg — из PATH (или FFMPEG_BINARY) вместо Windows-пути к ffmpeg.exe;
- модель загружается один раз на процесс, а не на каждый запрос;
- ошибка конвертации возвращается клиенту, а не печатается и не превращается в пустой текст.
This commit is contained in:
Деев Егор Викторович 2026-10-06 00:38:57 +00:00
parent c004e79119
commit fc2f750cee
2 changed files with 63 additions and 33 deletions

View file

@ -1,51 +1,81 @@
import os, wave, vosk, ffmpeg
import json
import logging
import os
import tempfile
import threading
import wave
MODEL_PATH = r"models/vosk-model-small-ru-0.22"
FFMPEG_PATH = r"models/ffmpeg/bin/ffmpeg.exe"
import ffmpeg
import vosk
from django.conf import settings
def convert_audio_to_wav(input_file, output_file, FFMPEG_PATH):
logger = logging.getLogger(__name__)
vosk.SetLogLevel(-1)
_model = None
_model_lock = threading.Lock()
class RecognitionError(Exception):
pass
def get_model():
"""Модель Vosk загружается один раз на процесс (раньше — на каждый запрос, это секунды и сотни МБ)"""
global _model
with _model_lock:
if _model is None:
if not os.path.isdir(settings.VOSK_MODEL_PATH):
raise RecognitionError(f"Модель Vosk не найдена: {settings.VOSK_MODEL_PATH}")
_model = vosk.Model(str(settings.VOSK_MODEL_PATH))
return _model
def convert_audio_to_wav(input_file, output_file):
"""WAV 16 кГц моно для Vosk, с шумоподавлением и нормализацией громкости"""
try:
(
ffmpeg
.input(input_file)
.output(output_file, format='wav', acodec='pcm_s16le', ar='16000', ac=1,
af='acompressor,afftdn,dynaudnorm,aresample=16000') # 16kHz для Vosk
.global_args('-loglevel', 'quiet')
.run(cmd=FFMPEG_PATH, overwrite_output=True)
.global_args('-loglevel', 'error')
.run(cmd=settings.FFMPEG_BINARY, overwrite_output=True, capture_stdout=True, capture_stderr=True)
)
print(f"Конвертация завершена: {output_file}")
except ffmpeg.Error as e:
print("Ошибка при конвертации:", e.stderr.decode())
raise RecognitionError("Не удалось прочитать аудиофайл") from e
vosk.SetLogLevel(-1)
def is_vosk_ready_wav(path) -> bool:
try:
with wave.open(path, "rb") as wf:
return wf.getnchannels() == 1 and wf.getsampwidth() == 2 and wf.getframerate() == 16000
except (wave.Error, EOFError):
return False
def recognize_speech(audio_path) -> str:
if not os.path.exists(MODEL_PATH):
print("Ошибка: Модель не найдена!")
return ""
model = get_model()
model = vosk.Model(MODEL_PATH)
# временный файл — свой на каждый запрос (раньше общий audio.wav в текущей папке:
# одновременные запросы перезаписывали друг другу аудио)
with tempfile.TemporaryDirectory() as tmp:
if not (audio_path.lower().endswith(".wav") and is_vosk_ready_wav(audio_path)):
wav_path = os.path.join(tmp, "audio.wav")
convert_audio_to_wav(audio_path, wav_path)
audio_path = wav_path
if audio_path.split('.')[-1] != "wav":
convert.convert_audio_to_wav(audio_path, "audio.wav", FFMPEG_PATH)
audio_path = "audio.wav"
else:
# Vosk отдаёт текст по фразам: раньше бралось только FinalResult(), и в длинной записи
# оставалась лишь последняя фраза
parts = []
with wave.open(audio_path, "rb") as wf:
if wf.getnchannels() != 1 or wf.getsampwidth() != 2 or wf.getframerate() != 16000:
convert.convert_audio_to_wav(audio_path, "audio.wav", FFMPEG_PATH)
audio_path = "audio.wav"
with wave.open(audio_path, "rb") as wf: # использование vosk
recognizer = vosk.KaldiRecognizer(model, wf.getframerate())
while True:
data = wf.readframes(3200)
data = wf.readframes(4000)
if not data:
break
recognizer.AcceptWaveform(data)
if recognizer.AcceptWaveform(data):
parts.append(json.loads(recognizer.Result()).get("text", ""))
parts.append(json.loads(recognizer.FinalResult()).get("text", ""))
if audio_path == "audio.wav":
os.remove(audio_path)
return recognizer.FinalResult().split(": \"")[-1][:-3]
return " ".join(p for p in parts if p)