don0726 commited on
Commit
0ab11bd
·
verified ·
1 Parent(s): 5919ea7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +29 -92
app.py CHANGED
@@ -1,117 +1,54 @@
1
  import gradio as gr
2
- import torch
3
- import numpy as np
4
- import librosa
5
 
6
- from speechbrain.pretrained import EncoderClassifier
7
- from sklearn.cluster import AgglomerativeClustering
8
- from sklearn.metrics.pairwise import cosine_similarity
9
-
10
- # 🔥 Strong embedding model (better than resemblyzer)
11
- classifier = EncoderClassifier.from_hparams(
12
- source="speechbrain/spkrec-ecapa-voxceleb",
13
- run_opts={"device": "cpu"}
14
  )
15
 
16
- def diarize(audio_path):
17
- wav, sr = librosa.load(audio_path, sr=16000)
18
-
19
- # 🔥 VAD (simple energy-based)
20
- frame_length = int(0.025 * sr)
21
- hop_length = int(0.01 * sr)
 
22
 
23
- energy = np.array([
24
- np.sum(np.abs(wav[i:i+frame_length]**2))
25
- for i in range(0, len(wav), hop_length)
26
- ])
27
 
28
- threshold = np.mean(energy) * 0.5
29
- speech_frames = energy > threshold
30
 
31
- # Convert frames to segments
32
  segments = []
33
- start = None
34
-
35
- for i, val in enumerate(speech_frames):
36
- if val and start is None:
37
- start = i * hop_length / sr
38
- elif not val and start is not None:
39
- end = i * hop_length / sr
40
- segments.append((start, end))
41
- start = None
42
-
43
- if start is not None:
44
- segments.append((start, len(wav)/sr))
45
-
46
- # 🔥 Extract embeddings
47
- embeddings = []
48
- valid_segments = []
49
-
50
- for (s, e) in segments:
51
- if e - s < 0.5:
52
- continue
53
-
54
- audio_chunk = wav[int(s*sr):int(e*sr)]
55
- emb = classifier.encode_batch(torch.tensor(audio_chunk).unsqueeze(0))
56
- emb = emb.squeeze().detach().numpy()
57
-
58
- embeddings.append(emb)
59
- valid_segments.append((s, e))
60
-
61
- if len(embeddings) == 0:
62
- return {"segments": []}
63
-
64
- embeddings = np.array(embeddings)
65
-
66
- # 🔥 Clustering
67
- sim_matrix = cosine_similarity(embeddings)
68
-
69
- clustering = AgglomerativeClustering(
70
- n_clusters=None,
71
- distance_threshold=0.7
72
- )
73
-
74
- labels = clustering.fit_predict(embeddings)
75
-
76
- # 🔥 Merge same speaker segments
77
- merged = []
78
- prev_label = labels[0]
79
- start = valid_segments[0][0]
80
- end = valid_segments[0][1]
81
 
82
- for i in range(1, len(labels)):
83
- if labels[i] == prev_label:
84
- end = valid_segments[i][1]
85
- else:
86
- merged.append({
87
- "speaker": f"Speaker {prev_label+1}",
88
- "start": round(start, 2),
89
- "end": round(end, 2)
90
- })
91
- prev_label = labels[i]
92
- start = valid_segments[i][0]
93
- end = valid_segments[i][1]
94
 
95
- merged.append({
96
- "speaker": f"Speaker {prev_label+1}",
97
- "start": round(start, 2),
98
- "end": round(end, 2)
99
- })
100
 
101
- return {"segments": merged}
102
 
103
 
104
  def process(audio):
105
  if audio is None:
106
- return {"error": "No audio"}
107
  return diarize(audio)
108
 
109
 
 
110
  demo = gr.Interface(
111
  fn=process,
112
  inputs=gr.Audio(sources=["upload"], type="filepath"),
113
  outputs=gr.JSON(),
114
- title="High Accuracy Speaker Diarization (No Token)"
 
115
  )
116
 
117
  if __name__ == "__main__":
 
1
  import gradio as gr
2
+ import os
3
+ from pyannote.audio import Pipeline
 
4
 
5
+ # Load pipeline once (important for speed)
6
+ pipeline = Pipeline.from_pretrained(
7
+ "pyannote/speaker-diarization",
8
+ use_auth_token=os.getenv("HF_TOKEN")
 
 
 
 
9
  )
10
 
11
+ # Optional: use GPU if available
12
+ try:
13
+ import torch
14
+ if torch.cuda.is_available():
15
+ pipeline.to(torch.device("cuda"))
16
+ except:
17
+ pass
18
 
 
 
 
 
19
 
20
+ def diarize(audio_path):
21
+ diarization = pipeline(audio_path)
22
 
 
23
  segments = []
24
+ speaker_map = {}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ for turn, _, speaker in diarization.itertracks(yield_label=True):
27
+ if speaker not in speaker_map:
28
+ speaker_map[speaker] = f"Speaker {len(speaker_map) + 1}"
 
 
 
 
 
 
 
 
 
29
 
30
+ segments.append({
31
+ "speaker": speaker_map[speaker],
32
+ "start": round(turn.start, 2),
33
+ "end": round(turn.end, 2)
34
+ })
35
 
36
+ return {"segments": segments}
37
 
38
 
39
  def process(audio):
40
  if audio is None:
41
+ return {"error": "No audio provided"}
42
  return diarize(audio)
43
 
44
 
45
+ # Gradio Interface
46
  demo = gr.Interface(
47
  fn=process,
48
  inputs=gr.Audio(sources=["upload"], type="filepath"),
49
  outputs=gr.JSON(),
50
+ title="Speaker Diarization API (pyannote)",
51
+ description="High accuracy speaker labeling with timestamps"
52
  )
53
 
54
  if __name__ == "__main__":