exboogiman commited on
Commit
28bcfea
·
verified ·
1 Parent(s): c296eb3

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +124 -78
app.py CHANGED
@@ -1,88 +1,134 @@
 
1
  import gradio as gr
2
- import sherpa_onnx
3
  import numpy as np
4
- import os
5
- import urllib.request
6
  from huggingface_hub import hf_hub_download
7
 
8
- # ── Загрузка моделей ─────────────────────────────────────────
9
- encoder = hf_hub_download("exboogiman/stt", "stt-encoder.int8.onnx")
10
- decoder = hf_hub_download("exboogiman/stt", "stt-decoder.int8.onnx")
11
- tokens = hf_hub_download("exboogiman/stt", "stt-tokens.txt")
12
-
13
- ten_vad_path = "/tmp/ten-vad.onnx"
14
- if not os.path.exists(ten_vad_path):
15
- urllib.request.urlretrieve(
16
- "https://github.com/k2-fsa/sherpa-onnx/releases/download/asr-models/ten-vad.onnx",
17
- ten_vad_path,
18
- )
19
-
20
- recognizer = sherpa_onnx.OfflineRecognizer.from_whisper(
21
- encoder=encoder,
22
- decoder=decoder,
23
- tokens=tokens,
24
- language="uz",
25
- task="transcribe",
26
- debug=False,
 
 
 
27
  )
28
 
 
29
  vad_config = sherpa_onnx.VadModelConfig()
30
- vad_config.ten_vad.model = ten_vad_path
31
- vad_config.ten_vad.threshold = 0.5
32
- vad_config.ten_vad.min_silence_duration = 0.5
33
- vad_config.ten_vad.min_speech_duration = 0.25
34
- vad_config.sample_rate = 16000
35
-
36
- vad = sherpa_onnx.VoiceActivityDetector(vad_config, buffer_size_in_seconds=30)
37
-
38
-
39
- # ── Транскрипция ─────────────────────────────────────────────
40
- def transcribe(stream, new_chunk):
41
- sr, data = new_chunk
42
-
43
- if data.ndim > 1:
44
- data = data.mean(axis=1)
45
-
46
- data = data.astype(np.float32) / 32768.0
47
-
48
- if sr != 16000:
49
- import librosa
50
- data = librosa.resample(data, orig_sr=sr, target_sr=16000)
51
-
52
- # Накапливаем буфер
53
- stream = np.concatenate([stream, data]) if stream is not None else data
54
-
55
- # Скармливаем VAD
56
- vad.accept_waveform(data)
57
-
58
- new_text = ""
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
59
  while not vad.empty():
60
- segment = vad.front.samples
 
61
  vad.pop()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
 
63
- s = recognizer.create_stream()
64
- s.accept_waveform(16000, segment)
65
- recognizer.decode_stream(s)
66
- result = s.result.text.strip()
67
- if result:
68
- new_text += result + " "
69
-
70
- return stream, new_text
71
-
72
-
73
- demo = gr.Interface(
74
- fn=transcribe,
75
- inputs=[
76
- gr.State(),
77
- gr.Audio(sources=["microphone"], streaming=True, type="numpy"),
78
- ],
79
- outputs=[
80
- gr.State(),
81
- gr.Textbox(label="Транскрипция", lines=8, placeholder="Здесь появится текст..."),
82
- ],
83
- live=True,
84
- title="🎙️ Kotib STT",
85
- description="Говори в микрофон — текст появляется автоматически",
86
- )
87
-
88
- demo.launch()
 
1
+ import os
2
  import gradio as gr
 
3
  import numpy as np
4
+ import sherpa_onnx
 
5
  from huggingface_hub import hf_hub_download
6
 
7
+ # ── 1. Загрузка приватных моделей ────────────────────────────
8
+ HF_TOKEN = os.environ.get("HF_TOKEN")
9
+ REPO_ID = "exboogiman/streamingASR"
10
+
11
+ print("Скачивание моделей...")
12
+ encoder_path = hf_hub_download(repo_id=REPO_ID, filename="encoder-uzbek_fc.onnx", token=HF_TOKEN)
13
+ decoder_path = hf_hub_download(repo_id=REPO_ID, filename="decoder-uzbek_fc.onnx", token=HF_TOKEN)
14
+ joiner_path = hf_hub_download(repo_id=REPO_ID, filename="joiner-uzbek_fc.onnx", token=HF_TOKEN)
15
+ tokens_path = hf_hub_download(repo_id=REPO_ID, filename="tokens.txt", token=HF_TOKEN)
16
+ vad_path = hf_hub_download(repo_id=REPO_ID, filename="silero_vad.onnx", token=HF_TOKEN)
17
+
18
+ # ── 2. Инициализация компонентов ────────────────────────────
19
+ recognizer = sherpa_onnx.OfflineRecognizer.from_transducer(
20
+ encoder=encoder_path,
21
+ decoder=decoder_path,
22
+ joiner=joiner_path,
23
+ tokens=tokens_path,
24
+ model_type="nemo_transducer",
25
+ num_threads=2,
26
+ sample_rate=16000,
27
+ feature_dim=80,
28
+ decoding_method="greedy_search",
29
  )
30
 
31
+ # Настройки VAD
32
  vad_config = sherpa_onnx.VadModelConfig()
33
+ vad_config.silero_vad.model = vad_path
34
+ vad_config.silero_vad.min_silence_duration = 0.3 # пауза 300мс = конец фразы
35
+ vad_config.silero_vad.threshold = 0.5
36
+ vad_config.sample_rate = 16000
37
+
38
+ # ── 3. Логика стриминга ──────────────────────────────────────
39
+ def initialize_state():
40
+ return {
41
+ "vad": sherpa_onnx.VoiceActivityDetector(vad_config, buffer_size_in_seconds=30),
42
+ "buffer": np.array([], dtype=np.float32),
43
+ "transcript": "",
44
+ }
45
+
46
+ def transcribe_stream(audio, state):
47
+ if state is None:
48
+ state = initialize_state()
49
+
50
+ if audio is None:
51
+ return state["transcript"], state
52
+
53
+ sr, y = audio
54
+
55
+ # Приводим к моно
56
+ if y.ndim > 1:
57
+ y = y.mean(axis=-1)
58
+
59
+ # Приводим к float32 и нормализуем
60
+ samples = y.astype(np.float32)
61
+ if np.max(np.abs(samples)) > 1.0:
62
+ samples = samples / 32768.0
63
+
64
+ # Добавляем новые семплы в буфер
65
+ state["buffer"] = np.concatenate([state["buffer"], samples])
66
+
67
+ vad = state["vad"]
68
+ window_size = vad_config.silero_vad.window_size
69
+
70
+ # Передаем в VAD фиксированными окнами
71
+ while len(state["buffer"]) >= window_size:
72
+ vad.accept_waveform(state["buffer"][:window_size])
73
+ state["buffer"] = state["buffer"][window_size:]
74
+
75
+ # Если VAD вырезал законченную фразу — распознаем её офлайн-моделью
76
+ new_text_segments = []
77
  while not vad.empty():
78
+ stream = recognizer.create_stream()
79
+ stream.accept_waveform(16000, vad.front.samples)
80
  vad.pop()
81
+ recognizer.decode_stream(stream)
82
+ text = stream.result.text.strip()
83
+ if text:
84
+ new_text_segments.append(text)
85
+
86
+ # Если есть новые распознанные слова, добавляем их к общей истории
87
+ if new_text_segments:
88
+ added_text = " ".join(new_text_segments)
89
+ if state["transcript"]:
90
+ state["transcript"] += " " + added_text
91
+ else:
92
+ state["transcript"] = added_text
93
+
94
+ return state["transcript"], state
95
+
96
+ # ── 4. Описание интерфейса Gradio Blocks ─────────────────────
97
+ with gr.Blocks(theme=gr.themes.Soft()) as demo:
98
+ gr.Markdown("# STT Streaming Demo (Uzbek)")
99
+
100
+ # Состояние для сохранения VAD буфера между вызовами
101
+ state = gr.State()
102
+
103
+ with gr.Row():
104
+ # Вход звука с микрофона с флагом streaming=True
105
+ input_audio = gr.Audio(
106
+ sources=["microphone"],
107
+ type="numpy",
108
+ streaming=True,
109
+ label="Говорите в микрофон"
110
+ )
111
+ # Поле вывода текста
112
+ output_text = gr.Textbox(
113
+ label="Распознанный текст",
114
+ interactive=False,
115
+ placeholder="Ваша речь отобразится здесь..."
116
+ )
117
+
118
+ # Событие .stream срабатывает каждые ~0.5 сек по мере записи звука
119
+ input_audio.stream(
120
+ fn=transcribe_stream,
121
+ inputs=[input_audio, state],
122
+ outputs=[output_text, state],
123
+ show_progress="hidden"
124
+ )
125
+
126
+ # Сброс состояния при очистке аудио
127
+ input_audio.clear(
128
+ fn=initialize_state,
129
+ inputs=[],
130
+ outputs=[state]
131
+ )
132
 
133
+ if __name__ == "__main__":
134
+ demo.launch()