Aryan Gosaliya commited on
Commit
c3c40f4
·
1 Parent(s): 7046d04

added pyannote

Browse files
Files changed (2) hide show
  1. app/services/asr.py +72 -34
  2. requirements.txt +3 -0
app/services/asr.py CHANGED
@@ -1,51 +1,89 @@
1
  from typing import List, Dict
2
  from faster_whisper import WhisperModel
3
  import os
 
 
4
 
5
  # Load environment variables
6
  WHISPER_MODEL_SIZE = os.getenv("WHISPER_MODEL_SIZE", "base")
7
- WHISPER_DEVICE = os.getenv("WHISPER_DEVICE", "cpu")
8
- WHISPER_COMPUTE_TYPE = os.getenv("WHISPER_COMPUTE_TYPE", "int8")
9
 
 
10
  try:
11
- _whisper = WhisperModel(
12
- WHISPER_MODEL_SIZE,
13
- device="cpu", # Force CPU to avoid CUDA issues
14
- compute_type="int8"
15
- )
16
  except Exception as e:
17
  print(f"Error loading Whisper model: {e}")
18
- # Fallback to base model with explicit CPU settings
19
- _whisper = WhisperModel(
20
- "base",
21
- device="cpu",
22
- compute_type="int8"
23
- )
 
 
 
 
 
 
 
 
 
 
 
 
24
 
25
  def transcribe(audio_path: str) -> List[Dict]:
26
  """
27
- Transcribe an audio file using faster-whisper and return our standard
28
- list of segments: [{start, end, speaker, text}].
29
  """
30
- # beam_size=1 is fastest; raise for a bit more accuracy.
31
- segments, info = _whisper.transcribe(
32
- audio_path,
33
- language="en",
34
- vad_filter=True,
35
- beam_size=1
36
  )
37
 
38
- out = []
39
  for seg in segments:
40
- out.append({
41
- "start": float(seg.start),
42
- "end": float(seg.end),
43
- "speaker": "A", # no diarization here; we can add later
44
- "text": seg.text.strip()
45
- })
46
-
47
- # If there were no segments (edge case), return a single empty segment
48
- if not out:
49
- out = [{"start": 0.0, "end": 0.0, "speaker": "A", "text": ""}]
50
-
51
- return out
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  from typing import List, Dict
2
  from faster_whisper import WhisperModel
3
  import os
4
+ import torch
5
+ from pyannote.audio import Pipeline
6
 
7
  # Load environment variables
8
  WHISPER_MODEL_SIZE = os.getenv("WHISPER_MODEL_SIZE", "base")
9
+ HF_TOKEN = os.getenv("HF_TOKEN") # For pyannote
 
10
 
11
+ # Initialize Whisper model
12
  try:
13
+ _whisper = WhisperModel(WHISPER_MODEL_SIZE, device="cpu", compute_type="int8")
 
 
 
 
14
  except Exception as e:
15
  print(f"Error loading Whisper model: {e}")
16
+ _whisper = WhisperModel("base", device="cpu", compute_type="int8")
17
+
18
+ # Initialize pyannote diarization pipeline
19
+ if HF_TOKEN:
20
+ try:
21
+ diarization_pipeline = Pipeline.from_pretrained(
22
+ "pyannote/speaker-diarization-3.1", use_auth_token=HF_TOKEN
23
+ )
24
+ # Move pipeline to CPU if no GPU is available
25
+ if not torch.cuda.is_available():
26
+ diarization_pipeline = diarization_pipeline.to(torch.device("cpu"))
27
+ except Exception as e:
28
+ print(f"Error loading pyannote pipeline: {e}")
29
+ diarization_pipeline = None
30
+ else:
31
+ print("HUGGING_FACE_TOKEN not set, skipping diarization.")
32
+ diarization_pipeline = None
33
+
34
 
35
  def transcribe(audio_path: str) -> List[Dict]:
36
  """
37
+ Transcribe an audio file using faster-whisper and combine with
38
+ pyannote.audio for speaker diarization.
39
  """
40
+ # 1. Transcribe with Whisper
41
+ segments, _ = _whisper.transcribe(
42
+ audio_path, language="en", vad_filter=True, beam_size=1
 
 
 
43
  )
44
 
45
+ whisper_segments = []
46
  for seg in segments:
47
+ whisper_segments.append(
48
+ {"start": float(seg.start), "end": float(seg.end), "text": seg.text.strip()}
49
+ )
50
+
51
+ if not diarization_pipeline:
52
+ # If diarization is not available, return with a single speaker
53
+ for seg in whisper_segments:
54
+ seg["speaker"] = "A"
55
+ return whisper_segments
56
+
57
+ # 2. Perform Diarization
58
+ try:
59
+ diarization = diarization_pipeline(audio_path)
60
+ except Exception as e:
61
+ print(f"Error during diarization: {e}")
62
+ for seg in whisper_segments:
63
+ seg["speaker"] = "A"
64
+ return whisper_segments
65
+
66
+ # 3. Assign Speaker to Segments
67
+ out_segments = []
68
+ for seg in whisper_segments:
69
+ # Find the speaker for the segment's midpoint
70
+ midpoint = seg["start"] + (seg["end"] - seg["start"]) / 2
71
+ speaker = "UNKNOWN"
72
+ for turn, _, speaker_label in diarization.itertracks(yield_label=True):
73
+ if turn.start <= midpoint <= turn.end:
74
+ speaker = speaker_label
75
+ break
76
+
77
+ out_segments.append(
78
+ {
79
+ "start": seg["start"],
80
+ "end": seg["end"],
81
+ "speaker": speaker,
82
+ "text": seg["text"],
83
+ }
84
+ )
85
+
86
+ if not out_segments:
87
+ return [{"start": 0.0, "end": 0.0, "speaker": "A", "text": ""}]
88
+
89
+ return out_segments
requirements.txt CHANGED
@@ -62,3 +62,6 @@ uvicorn==0.35.0
62
  uvloop==0.21.0
63
  watchfiles==1.1.0
64
  websockets==15.0.1
 
 
 
 
62
  uvloop==0.21.0
63
  watchfiles==1.1.0
64
  websockets==15.0.1
65
+
66
+ # Added for Speaker Diarization
67
+ pyannote.audio>=3.3.2