Spaces:
Running
Running
Added app.py and requirements.txt
Browse files- app.py +91 -0
- requirements.txt +3 -0
app.py
ADDED
@@ -0,0 +1,91 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
|
2 |
+
import os
|
3 |
+
import wave
|
4 |
+
from pydub import AudioSegment
|
5 |
+
from vosk import Model, KaldiRecognizer
|
6 |
+
import gradio as gr
|
7 |
+
import typing
|
8 |
+
import types
|
9 |
+
import logging
|
10 |
+
|
11 |
+
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(message)s')
|
12 |
+
|
13 |
+
|
14 |
+
def print_file_info(file_path: str) -> None:
|
15 |
+
"""
|
16 |
+
Данная функция печатает информацию об аудиофайле: количество каналов, ширину выборки, частоту и общее количестов фрагментов.
|
17 |
+
|
18 |
+
Args:
|
19 |
+
file_path (Str): Путь к файлу, который проверяем.
|
20 |
+
"""
|
21 |
+
with wave.open(file_path, 'rb') as wf: #открытие .wav файла в бинарном формате для чтения
|
22 |
+
print(f'Channels: {wf.getnchannels()}, Sample Width: {wf.getsampwidth()}, Frame Rates: {wf.getframerate()}, Frames: {wf.getnframes()}') #получение всех данных
|
23 |
+
|
24 |
+
|
25 |
+
def convert_wav_to_text(audio_file_path: str) -> str:
|
26 |
+
"""
|
27 |
+
Данная функция конвертирует любой входящий аудиофайл в .wav формат, после чего распознаёт его с помощью модели Vosk и возвращает либо текст, либо ошибку.
|
28 |
+
|
29 |
+
Args:
|
30 |
+
audio_file_path (Str): Получаемый аудиофайл любого формата.
|
31 |
+
Returns:
|
32 |
+
text_from_audio (Str): Данные файла в текстовом формате.
|
33 |
+
error (Str): Какая-то ошибка.
|
34 |
+
"""
|
35 |
+
file_extension = audio_file_path.split('.')[-1].lower() #получение формата файла
|
36 |
+
temp_wav_path = 'temp.wav' #создание временного файла для обработки
|
37 |
+
try: #блок для отлова возможных ошибок
|
38 |
+
audio = AudioSegment.from_file(audio_file_path, format=file_extension) #получение данных из файла
|
39 |
+
audio = audio.set_channels(1).set_sample_width(2).set_frame_rate(16000) #принудительная установка параметров
|
40 |
+
audio.export(temp_wav_path, format='wav') #конвертация в .wav формат
|
41 |
+
print_file_info(temp_wav_path) #проверяем параметры файла
|
42 |
+
if not os.path.exists('model/vosk-model-ru-0.42'): #если не существует папка с моделью
|
43 |
+
raise FileNotFoundError('Проверьте наличие обученной модели Vosk в папке model!') #то показываем ошибку
|
44 |
+
model = Model('model/vosk-model-ru-0.42') #получаем предобученную модель для распознавания
|
45 |
+
recognizer = KaldiRecognizer(model, 16000) #объявление объекта-распознавателя
|
46 |
+
with wave.open(temp_wav_path, 'rb') as wave_file: #открытие .wav файла в бинарном формате для чтения
|
47 |
+
if wave_file.getnchannels() != 1 or wave_file.getsampwidth() != 2 or wave_file.getframerate() != 16000: #если файл не прошёл проверку параметров
|
48 |
+
raise ValueError('Проверьте файл, он должен быть в формате .wav с 1 каналом, 16-bit и 16000 Hz!') #то показываем ошибку
|
49 |
+
recognizer.AcceptWaveform(wave_file.readframes(wave_file.getnframes())) #распознавание файла
|
50 |
+
result = recognizer.Result() #получение результата распознавания
|
51 |
+
text_from_audio = result.split('"text" : ')[1].strip('} \n"') #форматируем вывод, а то там словарь на выходе
|
52 |
+
return text_from_audio #возвращаем текст сообщения
|
53 |
+
except Exception as e: #если какая-то шелуха
|
54 |
+
return f'Возникла неожиданная ошибка: {e}' #то показываем ошибку
|
55 |
+
finally: #этот блок выполняется в любом случае
|
56 |
+
if os.path.exists(temp_wav_path): #если есть временный файл
|
57 |
+
os.remove(temp_wav_path) #то сносим его нафиг, чтобы место не занимал
|
58 |
+
|
59 |
+
|
60 |
+
def recognize_speech_from_microphone(audio: typing.Union[str, types.NoneType, AudioSegment]) -> str:
|
61 |
+
"""
|
62 |
+
Функция, которая записывает аудио.
|
63 |
+
|
64 |
+
Args:
|
65 |
+
audio (Str): Путь к аудиофайлу (загруженный файл).
|
66 |
+
audio (types.NoneType): Появляется при смене типа входных данных.
|
67 |
+
audio (AudioSegment): При записи звука микрофоном.
|
68 |
+
Return:
|
69 |
+
text_from_audio (Str): Результат выполнения функции convert_wav_to_text().
|
70 |
+
warning_str (Str): Предупреждение при смене типа входных данных.
|
71 |
+
"""
|
72 |
+
if isinstance(audio, str): #если входящие данные - путь к файлу (а не записанный звук с микрофона)
|
73 |
+
audio_file_path = audio #то сразу передаём путь без экспорта
|
74 |
+
elif isinstance(audio, types.NoneType): #если входящие данные без типа (пользователь кликнул по смене типа входящих данных)
|
75 |
+
warning_str = 'Вы изменили источник входных данных. Запишите звук или загрузите файл.' #то создаём сообщение-предупреждение
|
76 |
+
return warning_str #и печатаем его
|
77 |
+
else: #иначе (пользователь записывает звук микрофоном)
|
78 |
+
audio_file_path = 'temp_input.wav' #задаём имя файлу
|
79 |
+
audio.export(audio_file_path, format='wav') #конвертируем в .wav-формат
|
80 |
+
return convert_wav_to_text(audio_file_path) #передаём в функцию-распознаватель
|
81 |
+
|
82 |
+
|
83 |
+
gui = gr.Interface( #создаём пользовательский интерфейс gradio
|
84 |
+
fn=recognize_speech_from_microphone, #функция, которая вызывается при записи аудио
|
85 |
+
inputs=gr.Audio(type='filepath'), #входной компонент для записи аудио с микрофона устройства
|
86 |
+
outputs='text', #выходной компонент для отображения текста
|
87 |
+
live=True #позволяет запускаться и работать в реальном времени
|
88 |
+
)
|
89 |
+
|
90 |
+
if __name__ == '__main__':
|
91 |
+
gui.launch(share=True)
|
requirements.txt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
pydub
|
2 |
+
vosk
|
3 |
+
gradio
|