AliJabbarpour commited on
Commit
639f86b
·
verified ·
1 Parent(s): fb1f529

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +88 -11
app.py CHANGED
@@ -1,7 +1,7 @@
1
  import os
2
  import tempfile
3
- import numpy as np
4
  import torch
 
5
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from faster_whisper import WhisperModel
@@ -25,7 +25,72 @@ print("Loading DeepFilterNet model...", flush=True)
25
  df_model, df_state, _ = init_df()
26
  print("DeepFilterNet model ready!")
27
 
28
- USE_DEEPFILTERNET = False # برای تست بدون DeepFilterNet، این رو موقتاً False کن
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  @app.get("/health")
31
  async def health():
@@ -49,9 +114,6 @@ async def transcribe(websocket: WebSocket):
49
  tmp_path = tmp.name
50
  print(f"Step 3: saved to {tmp_path}")
51
 
52
- await websocket.send_json({"status": "cleaning"})
53
- print("Step 3.5: cleaning audio with DeepFilterNet")
54
-
55
  audio_to_transcribe = tmp_path
56
 
57
  if USE_DEEPFILTERNET:
@@ -76,32 +138,47 @@ async def transcribe(websocket: WebSocket):
76
  word_timestamps=True,
77
  vad_filter=True,
78
  vad_parameters=dict(
79
- min_silence_duration_ms=500,
80
  speech_pad_ms=200,
81
- threshold=0.2,
82
  ),
83
  )
84
 
85
  print("Step 5: transcription done")
86
 
87
- await websocket.send_json({"status": "processing"})
 
 
 
 
 
 
 
88
 
89
  segments = []
90
  for seg in segments_raw:
91
  words = list(seg.words)
92
  if not words:
93
  continue
 
 
 
 
 
 
94
  segments.append({
95
  "text": seg.text.strip(),
96
- "start": words[0].start,
97
- "end": words[-1].end,
98
- "words": [{"word": w.word, "start": w.start, "end": w.end} for w in words],
99
  })
100
 
101
  print(f"Step 6: got {len(segments)} segments")
102
  for i, seg in enumerate(segments[:5]):
103
  print(f" seg[{i}]: '{seg['text']}' | words={len(seg['words'])} | dur={seg['end']-seg['start']:.2f}s")
104
 
 
 
105
  srt_content = process(segments)
106
  print(f"Step 7: SRT built")
107
 
 
1
  import os
2
  import tempfile
 
3
  import torch
4
+ import torchaudio
5
  from fastapi import FastAPI, WebSocket, WebSocketDisconnect
6
  from fastapi.middleware.cors import CORSMiddleware
7
  from faster_whisper import WhisperModel
 
25
  df_model, df_state, _ = init_df()
26
  print("DeepFilterNet model ready!")
27
 
28
+ USE_DEEPFILTERNET = False # غیرفعال - شواهد نشون داد کمکی نمی‌کنه و بعضی صداها رو خراب می‌کنه
29
+
30
+ print("Loading word-alignment model...", flush=True)
31
+ align_bundle = torchaudio.pipelines.MMS_FA
32
+ align_model = align_bundle.get_model().to("cpu")
33
+ align_labels = align_bundle.get_labels()
34
+ align_dictionary = {c: i for i, c in enumerate(align_labels)}
35
+ print("Word-alignment model ready!")
36
+
37
+
38
+ def realign_word_timestamps(waveform_16k, words, whisper_start, whisper_end):
39
+ """
40
+ برای یه جمله، به‌جای زمان‌بندی حدسی خودِ Whisper، با یه مدل تخصصی جدا
41
+ (wav2vec2 از طریق torchaudio) دقیقاً چک می‌کنه هر کلمه از کجای صدا گفته شده.
42
+ اگه به هر دلیلی هم‌ترازی جواب نداد، همون زمان‌بندی قبلی Whisper دست‌نخورده می‌مونه.
43
+ """
44
+ try:
45
+ margin = 0.5
46
+ start_sample = max(0, int((whisper_start - margin) * 16000))
47
+ end_sample = int((whisper_end + margin) * 16000)
48
+ clip = waveform_16k[:, start_sample:end_sample]
49
+
50
+ clean_words = []
51
+ for w in words:
52
+ token = "".join(ch for ch in w["word"].upper() if ch in align_dictionary)
53
+ if token:
54
+ clean_words.append((w, token))
55
+
56
+ if not clean_words:
57
+ return words
58
+
59
+ transcript = "".join(tok for _, tok in clean_words)
60
+ tokens = [align_dictionary[c] for c in transcript]
61
+
62
+ with torch.inference_mode():
63
+ emission, _ = align_model(clip)
64
+
65
+ targets = torch.tensor([tokens], dtype=torch.int32)
66
+ input_lengths = torch.tensor([emission.shape[1]])
67
+ target_lengths = torch.tensor([len(tokens)])
68
+
69
+ aligned_tokens, scores = torchaudio.functional.forced_align(
70
+ emission, targets, input_lengths, target_lengths, blank=0
71
+ )
72
+ spans = torchaudio.functional.merge_tokens(aligned_tokens[0], scores[0])
73
+
74
+ ratio = clip.shape[1] / emission.shape[1] / 16000
75
+ char_idx = 0
76
+ new_words = []
77
+ span_idx = 0
78
+ for w, tok in clean_words:
79
+ n_chars = len(tok)
80
+ matched = [s for s in spans[span_idx:] if s.token != 0][:n_chars]
81
+ if len(matched) < n_chars:
82
+ new_words.append(w)
83
+ continue
84
+ w_start = matched[0].start * ratio + (whisper_start - margin)
85
+ w_end = matched[-1].end * ratio + (whisper_start - margin)
86
+ new_words.append({"word": w["word"], "start": round(max(0, w_start), 3), "end": round(max(0, w_end), 3)})
87
+ span_idx += n_chars
88
+
89
+ return new_words if len(new_words) == len(words) else words
90
+ except Exception as e:
91
+ print(f" [align] skipped due to: {type(e).__name__}: {e}")
92
+ return words
93
+
94
 
95
  @app.get("/health")
96
  async def health():
 
114
  tmp_path = tmp.name
115
  print(f"Step 3: saved to {tmp_path}")
116
 
 
 
 
117
  audio_to_transcribe = tmp_path
118
 
119
  if USE_DEEPFILTERNET:
 
138
  word_timestamps=True,
139
  vad_filter=True,
140
  vad_parameters=dict(
141
+ min_silence_duration_ms=300,
142
  speech_pad_ms=200,
143
+ threshold=0.3,
144
  ),
145
  )
146
 
147
  print("Step 5: transcription done")
148
 
149
+ await websocket.send_json({"status": "aligning"})
150
+ print("Step 5.5: loading audio for word-alignment")
151
+
152
+ align_waveform, sr = torchaudio.load(audio_to_transcribe)
153
+ if sr != 16000:
154
+ align_waveform = torchaudio.functional.resample(align_waveform, sr, 16000)
155
+ if align_waveform.shape[0] > 1:
156
+ align_waveform = align_waveform.mean(dim=0, keepdim=True)
157
 
158
  segments = []
159
  for seg in segments_raw:
160
  words = list(seg.words)
161
  if not words:
162
  continue
163
+ word_dicts = [{"word": w.word, "start": w.start, "end": w.end} for w in words]
164
+
165
+ realigned = realign_word_timestamps(
166
+ align_waveform, word_dicts, word_dicts[0]["start"], word_dicts[-1]["end"]
167
+ )
168
+
169
  segments.append({
170
  "text": seg.text.strip(),
171
+ "start": realigned[0]["start"],
172
+ "end": realigned[-1]["end"],
173
+ "words": realigned,
174
  })
175
 
176
  print(f"Step 6: got {len(segments)} segments")
177
  for i, seg in enumerate(segments[:5]):
178
  print(f" seg[{i}]: '{seg['text']}' | words={len(seg['words'])} | dur={seg['end']-seg['start']:.2f}s")
179
 
180
+ await websocket.send_json({"status": "processing"})
181
+
182
  srt_content = process(segments)
183
  print(f"Step 7: SRT built")
184