Jahmori-R commited on
Commit
337effc
·
verified ·
1 Parent(s): 199d41b

Sync from Jahmori-R/PureVoiceAI-HG via hub-sync

Browse files
Files changed (1) hide show
  1. app.py +78 -46
app.py CHANGED
@@ -20,24 +20,40 @@ from pydub import AudioSegment
20
  torch.backends.cuda.matmul.allow_tf32 = False
21
  torch.backends.cudnn.allow_tf32 = False
22
 
23
- # Loading token from .env
24
  load_dotenv()
25
 
26
- # Hugging Face authentication token grab
27
  token = os.getenv("HF_TOKEN")
28
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
29
 
30
  # Path checking for removing source directory every run
31
  def reset_dir():
 
32
  # Getting current source directory
33
- sources = Path(__file__).parent / "sources"
34
- if os.path.exists(sources):
35
- try:
36
- # Remove directorty recursively
37
- shutil.rmtree(sources)
38
- gr.Info("Process Reset")
39
- except:
40
- raise gr.Error("Reset Failed")
 
41
 
42
  # To clear all Gradio Blocks
43
  return None, None, None, None, None
@@ -52,7 +68,8 @@ def chatFormat(transcription_log):
52
  start = i.get("start")
53
  end = i.get("end")
54
 
55
- title_format = f"**{name} - [{start}s - {end}s]**"
 
56
  role = "user" if name == "speaker_2" else "assistant"
57
 
58
  chat.append({"role": role, "content":text, "metadata":{"title":title_format}})
@@ -70,14 +87,6 @@ def process_audio(mixture, progress=gr.Progress()):
70
 
71
  # Gradio processing alert
72
  gr.Info("Processing Has Started", title="Notice")
73
- # GPU processing availability check
74
- device = "cuda" if torch.cuda.is_available() else "cpu"
75
-
76
- # Name of separated file folder
77
- sources = Path(os.getcwd()) / "sources"
78
-
79
- # Creates separated directory
80
- Path.mkdir(sources, exist_ok=True)
81
 
82
  # ---------------------------------------------------------
83
  # 1. INITIALIZE MODELS
@@ -146,13 +155,46 @@ def process_audio(mixture, progress=gr.Progress()):
146
  # Calling ReSepformer model onto audio source
147
  assert separator is not None, "Failed to load the Sepformer model."
148
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
149
  progress(0.50,"Separating Mixtures...")
 
150
  # Running mixed audio onto separation model
151
  print("Separating Audio Mixture")
152
- est_sources = separator.separate_file(mixture)
 
153
  print("Mixture separated")
154
 
155
- print(type(est_sources))
156
  # Saving separated sources to directory
157
  torchaudio.save(
158
  f"{sources}/speaker_1.wav", est_sources[:, :, 0].detach().cpu(), 16000
@@ -161,25 +203,6 @@ def process_audio(mixture, progress=gr.Progress()):
161
  f"{sources}/speaker_2.wav", est_sources[:, :, 1].detach().cpu(), 16000
162
  )
163
 
164
- # Applying noise gate to remaining artefacts in sources
165
- spk1_wave_torch,sr1 = torchaudio.load(f"{sources}/speaker_1.wav")
166
- spk2_wave_torch,sr2 = torchaudio.load(f"{sources}/speaker_2.wav")
167
-
168
- # Convert torch to numpy for noisereduce
169
- spk1_wave_np = spk1_wave_torch.numpy()
170
- spk2_wave_np = spk2_wave_torch.numpy()
171
-
172
- # Apply noise gate onto waveforms
173
- spk1_denoised_np = nr.reduce_noise(y=spk1_wave_np,sr=sr1,n_fft=512)
174
- spk2_denoised_np = nr.reduce_noise(y=spk2_wave_np,sr=sr2,n_fft=512)
175
-
176
- # Convert numpy data back to torch for saving
177
- spk1_denoised = torch.from_numpy(spk1_denoised_np)
178
- spk2_denoised = torch.from_numpy(spk2_denoised_np)
179
-
180
- # Saving new denoised sources
181
- torchaudio.save(f"{sources}/speaker_denoised_1.wav",spk1_denoised,sr1)
182
- torchaudio.save(f"{sources}/speaker_denoised_2.wav",spk2_denoised,sr2)
183
 
184
  # ---------------------------------------------------------
185
  # VAD + TRANSCRIPTION ON SOURCES
@@ -193,7 +216,7 @@ def process_audio(mixture, progress=gr.Progress()):
193
  # Storing both speakers
194
  file_paths = sorted(
195
  # Glob finds all similar files
196
- glob.glob(f"{sources}/speaker_denoised_*.wav")
197
  )
198
 
199
  spk_1 = file_paths[0]
@@ -209,8 +232,16 @@ def process_audio(mixture, progress=gr.Progress()):
209
  print(f"Diarizing {speaker_key}")
210
  diarization = diarize(path, num_speakers=1)
211
 
 
 
 
212
  # Storing speaker voice activity timestamps to dictionary
213
- for turn, _ in diarization.speaker_diarization:
 
 
 
 
 
214
  unfiltered_log[i].append(
215
  {
216
  "start": float(round(turn.start, 2)),
@@ -257,6 +288,7 @@ def process_audio(mixture, progress=gr.Progress()):
257
 
258
  end_time = perf_counter()
259
 
 
260
  inference_time = f"{end_time - start_time:.2f} seconds"
261
  return (spk_1, spk_2, chat, inference_time)
262
 
@@ -278,7 +310,7 @@ with gr.Blocks() as demo:
278
  gr.Markdown("PureVoiceAI")
279
  with gr.Row(variant="panel"):
280
  audio_input = gr.Audio(
281
- type="filepath", label="Input Audio Mixture | 2 Speakers"
282
  )
283
  with gr.Row(elem_id="centered-row"):
284
  process_button = gr.Button(
@@ -286,7 +318,7 @@ with gr.Blocks() as demo:
286
  variant="primary",
287
  scale=0,
288
  )
289
- reset_button = gr.Button("Reset", variant="stop", interactive=False, scale=0)
290
  with gr.Row(variant="panel", visible=False) as source_row:
291
  with gr.Column():
292
  speaker_1_output = gr.Audio(label="Speaker 1", interactive=False)
@@ -295,7 +327,7 @@ with gr.Blocks() as demo:
295
  inference_time_log = gr.Textbox(label="Inference Time", interactive=False)
296
 
297
  with gr.Row(variant="panel") as log_row:
298
- logs = gr.Chatbot(label="Conversation Log",layout="panel")
299
 
300
  # Process Button Functionality
301
  process_button.click(
@@ -333,4 +365,4 @@ with gr.Blocks() as demo:
333
  )
334
 
335
  if __name__ == "__main__":
336
- demo.launch(theme=gr.themes.Soft(), css=css)
 
20
  torch.backends.cuda.matmul.allow_tf32 = False
21
  torch.backends.cudnn.allow_tf32 = False
22
 
23
+ # Passing .env file as environment variable on machine
24
  load_dotenv()
25
 
26
+ # Hugging Face authentication token grab from .env
27
  token = os.getenv("HF_TOKEN")
28
 
29
+ # GPU processing availability check
30
+ device = "cuda" if torch.cuda.is_available() else "cpu"
31
+
32
+ # Name of mixture folder
33
+ mixtures = Path(os.getcwd())/"mixtures"
34
+
35
+ # Name of separated file folder
36
+ sources = Path(os.getcwd()) / "sources"
37
+
38
+ # Creates mixture directory
39
+ Path.mkdir(mixtures, exist_ok=True)
40
+
41
+ # Creates separated directory
42
+ Path.mkdir(sources, exist_ok=True)
43
 
44
  # Path checking for removing source directory every run
45
  def reset_dir():
46
+ remove_dir = [mixtures,sources]
47
  # Getting current source directory
48
+ for dir in remove_dir:
49
+ if os.path.exists(dir):
50
+ try:
51
+ # Remove directorty recursively
52
+ shutil.rmtree(sources)
53
+ except:
54
+ raise gr.Error("Reset Failed")
55
+
56
+ gr.Info("System Reset",title="Notice")
57
 
58
  # To clear all Gradio Blocks
59
  return None, None, None, None, None
 
68
  start = i.get("start")
69
  end = i.get("end")
70
 
71
+ # Title format for gradio chatbot block
72
+ title_format = f"**({name}) [{start}s - {end}s]**"
73
  role = "user" if name == "speaker_2" else "assistant"
74
 
75
  chat.append({"role": role, "content":text, "metadata":{"title":title_format}})
 
87
 
88
  # Gradio processing alert
89
  gr.Info("Processing Has Started", title="Notice")
 
 
 
 
 
 
 
 
90
 
91
  # ---------------------------------------------------------
92
  # 1. INITIALIZE MODELS
 
155
  # Calling ReSepformer model onto audio source
156
  assert separator is not None, "Failed to load the Sepformer model."
157
 
158
+ progress(0.40,"Denoising Mixture...")
159
+
160
+ #Denoising mixture
161
+ sr, mixture_np = mixture
162
+
163
+ if len(mixture_np.shape) > 1:
164
+ # Swaps channels location with sample rate in numpy array
165
+ if mixture_np.shape[0] > mixture_np.shape[1]:
166
+ mixture_np = mixture_np.T
167
+
168
+ # If it has more than 2 channels take the first one
169
+ if len(mixture_np.shape) > 1 and mixture_np.shape[0] > 2:
170
+ mixture_np = mixture_np[0]
171
+
172
+ # Converting numpy to float32 for torchaudio
173
+ if mixture_np.dtype != np.float32:
174
+
175
+ # Converting integer value to float between -1.0 and 1.0
176
+ if np.issubdtype(mixture_np.dtype, np.integer):
177
+ mixture_np = mixture_np.astype(np.float32) / np.iinfo(mixture_np.dtype).max
178
+ else:
179
+ mixture_np = mixture_np.astype(np.float32)
180
+
181
+ # Apply noise gate onto waveform
182
+ denoised_mixture_np = nr.reduce_noise(y=mixture_np,sr=sr, prop_decrease=0.5,n_fft=512)
183
+
184
+ # Convert numpy data back to torch for saving
185
+ denoised_mixture = torch.from_numpy(denoised_mixture_np)
186
+
187
+ # Saving new denoised mixture
188
+ torchaudio.save(f"{mixtures}/mixture.wav",denoised_mixture,sr)
189
+
190
  progress(0.50,"Separating Mixtures...")
191
+
192
  # Running mixed audio onto separation model
193
  print("Separating Audio Mixture")
194
+
195
+ est_sources = separator.separate_file(f"{mixtures}/mixture.wav")
196
  print("Mixture separated")
197
 
 
198
  # Saving separated sources to directory
199
  torchaudio.save(
200
  f"{sources}/speaker_1.wav", est_sources[:, :, 0].detach().cpu(), 16000
 
203
  f"{sources}/speaker_2.wav", est_sources[:, :, 1].detach().cpu(), 16000
204
  )
205
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
206
 
207
  # ---------------------------------------------------------
208
  # VAD + TRANSCRIPTION ON SOURCES
 
216
  # Storing both speakers
217
  file_paths = sorted(
218
  # Glob finds all similar files
219
+ glob.glob(f"{sources}/speaker_*.wav")
220
  )
221
 
222
  spk_1 = file_paths[0]
 
232
  print(f"Diarizing {speaker_key}")
233
  diarization = diarize(path, num_speakers=1)
234
 
235
+ # Minimum timestamp duration in seconds
236
+ minimum_duration = 0.8
237
+
238
  # Storing speaker voice activity timestamps to dictionary
239
+ for turn, _ in diarization.exclusive_speaker_diarization:
240
+ duration = turn.end - turn.start
241
+ if duration < minimum_duration:
242
+ continue
243
+
244
+ # Adding timestamp to list
245
  unfiltered_log[i].append(
246
  {
247
  "start": float(round(turn.start, 2)),
 
288
 
289
  end_time = perf_counter()
290
 
291
+ # Inference time calculation
292
  inference_time = f"{end_time - start_time:.2f} seconds"
293
  return (spk_1, spk_2, chat, inference_time)
294
 
 
310
  gr.Markdown("PureVoiceAI")
311
  with gr.Row(variant="panel"):
312
  audio_input = gr.Audio(
313
+ type="numpy", label="Input Audio Mixture | 2 Speakers"
314
  )
315
  with gr.Row(elem_id="centered-row"):
316
  process_button = gr.Button(
 
318
  variant="primary",
319
  scale=0,
320
  )
321
+ reset_button = gr.Button("Restart", variant="stop", interactive=False, scale=0)
322
  with gr.Row(variant="panel", visible=False) as source_row:
323
  with gr.Column():
324
  speaker_1_output = gr.Audio(label="Speaker 1", interactive=False)
 
327
  inference_time_log = gr.Textbox(label="Inference Time", interactive=False)
328
 
329
  with gr.Row(variant="panel") as log_row:
330
+ logs = gr.Chatbot(label="Conversation Log",layout="bubble")
331
 
332
  # Process Button Functionality
333
  process_button.click(
 
365
  )
366
 
367
  if __name__ == "__main__":
368
+ demo.launch(theme=gr.Theme.from_hub("pseudolab/huggingface-korea-theme"), css=css)