arshan123 commited on
Commit
9685fee
·
1 Parent(s): f826ca0

multithread for low latency

Browse files
Files changed (1) hide show
  1. detector.py +47 -25
detector.py CHANGED
@@ -4,6 +4,7 @@ import numpy as np
4
  import scipy.stats as stats
5
  import torch.nn.functional as F
6
  from transformers import AutoModelForAudioClassification, AutoFeatureExtractor, WhisperProcessor, WhisperForConditionalGeneration
 
7
  import base64
8
  import io
9
  import tempfile
@@ -154,7 +155,9 @@ class HybridEnsembleDetector:
154
  self.CV_HUMAN_THRESHOLD = 0.32
155
  self.INTENSITY_MIN_STD = 0.05
156
  self.INTENSITY_MAX_STD = 0.15
157
-
 
 
158
  print("✅ Hybrid Detector Ready\n")
159
 
160
  # ==========================================================
@@ -342,30 +345,36 @@ class HybridEnsembleDetector:
342
  y = y[:max_samples]
343
  duration = self.max_duration
344
  was_truncated = True
 
 
 
 
 
 
 
 
 
345
 
346
- print(f" 🔬 Running physics analysis on {duration:.1f}s audio at {sr}Hz")
347
-
348
- # Robust pitch tracking using PYIN
349
  try:
350
- f0, voiced_flag, voiced_probs = librosa.pyin(
351
- y,
352
  fmin=librosa.note_to_hz('C2'), # ~65 Hz
353
  fmax=librosa.note_to_hz('C7'), # ~2093 Hz
354
  sr=sr,
355
- frame_length=2048
356
  )
357
- valid_f0 = f0[~np.isnan(f0)]
358
  except Exception as pitch_error:
359
  print(f" ⚠️ Pitch detection failed: {pitch_error}, using fallback method")
360
- # Fallback: use simpler pitch detection
361
  valid_f0 = np.array([])
362
 
363
  if len(valid_f0) < 10: # Need at least 10 valid pitch points
364
  print(f" ⚠️ Insufficient pitch data ({len(valid_f0)} points), using alternative features")
365
- # Fall back to non-pitch features
366
- rms = librosa.feature.rms(y=y)[0]
367
- centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
368
- zcr = librosa.feature.zero_crossing_rate(y)[0]
369
 
370
  feats = {
371
  'pitch_cv': 0.25, # Neutral value
@@ -403,9 +412,9 @@ class HybridEnsembleDetector:
403
  print(f" 🔬 Physics score (no pitch): {final_score:.3f}")
404
  return round(final_score, 3), "Physics Analysis (Limited)", feats
405
 
406
- # Full analysis with pitch
407
- rms = librosa.feature.rms(y=y)[0]
408
- centroid = librosa.feature.spectral_centroid(y=y, sr=sr)[0]
409
 
410
  mean_pitch = np.mean(valid_f0)
411
  std_pitch = np.std(valid_f0)
@@ -611,16 +620,29 @@ class HybridEnsembleDetector:
611
 
612
  print(f"🎵 Analyzing: {os.path.basename(audio_path)}")
613
 
614
-
 
 
615
 
616
- # 2. Run Physics Analysis
617
- phys_score, phys_method, phys_feats = self.get_physics_score(audio_path)
618
- # phys_score = 0
619
- # phys_feats = {'pitch_cv': 1, 'intensity_std': 1}
620
-
621
- # 3. Run Deep Learning Analysis
622
- dl_score, dl_label = self.get_dl_score(audio_path)
623
- print(f"DL Score is: ${dl_score}")
 
 
 
 
 
 
 
 
 
 
 
624
 
625
  # 4. Calculate weighted ensemble score
626
  final_score = (
 
4
  import scipy.stats as stats
5
  import torch.nn.functional as F
6
  from transformers import AutoModelForAudioClassification, AutoFeatureExtractor, WhisperProcessor, WhisperForConditionalGeneration
7
+ from concurrent.futures import ThreadPoolExecutor, as_completed
8
  import base64
9
  import io
10
  import tempfile
 
155
  self.CV_HUMAN_THRESHOLD = 0.32
156
  self.INTENSITY_MIN_STD = 0.05
157
  self.INTENSITY_MAX_STD = 0.15
158
+ # Analyze only middle N seconds for physics (reduces latency ~60% vs full 30s)
159
+ self.physics_analysis_duration = 8 # seconds; middle segment where voice is most stable
160
+
161
  print("✅ Hybrid Detector Ready\n")
162
 
163
  # ==========================================================
 
345
  y = y[:max_samples]
346
  duration = self.max_duration
347
  was_truncated = True
348
+
349
+ # Use middle segment only for physics (faster; voice most stable in middle)
350
+ seg_dur = min(self.physics_analysis_duration, duration)
351
+ half = (duration - seg_dur) / 2.0
352
+ start_samp = int(half * sr)
353
+ end_samp = int((half + seg_dur) * sr)
354
+ y_physics = y[start_samp:end_samp]
355
+
356
+ print(f" 🔬 Running physics analysis on middle {seg_dur:.1f}s of {duration:.1f}s audio at {sr}Hz")
357
 
358
+ # Fast pitch tracking using YIN (faster than PYIN, suitable for CPU/HF Spaces)
 
 
359
  try:
360
+ f0 = librosa.yin(
361
+ y_physics,
362
  fmin=librosa.note_to_hz('C2'), # ~65 Hz
363
  fmax=librosa.note_to_hz('C7'), # ~2093 Hz
364
  sr=sr,
365
+ frame_length=2048,
366
  )
367
+ valid_f0 = f0[~np.isnan(f0)] if f0 is not None else np.array([])
368
  except Exception as pitch_error:
369
  print(f" ⚠️ Pitch detection failed: {pitch_error}, using fallback method")
 
370
  valid_f0 = np.array([])
371
 
372
  if len(valid_f0) < 10: # Need at least 10 valid pitch points
373
  print(f" ⚠️ Insufficient pitch data ({len(valid_f0)} points), using alternative features")
374
+ # Fall back to non-pitch features (on segment)
375
+ rms = librosa.feature.rms(y=y_physics)[0]
376
+ centroid = librosa.feature.spectral_centroid(y=y_physics, sr=sr)[0]
377
+ zcr = librosa.feature.zero_crossing_rate(y_physics)[0]
378
 
379
  feats = {
380
  'pitch_cv': 0.25, # Neutral value
 
412
  print(f" 🔬 Physics score (no pitch): {final_score:.3f}")
413
  return round(final_score, 3), "Physics Analysis (Limited)", feats
414
 
415
+ # Full analysis with pitch (on segment)
416
+ rms = librosa.feature.rms(y=y_physics)[0]
417
+ centroid = librosa.feature.spectral_centroid(y=y_physics, sr=sr)[0]
418
 
419
  mean_pitch = np.mean(valid_f0)
420
  std_pitch = np.std(valid_f0)
 
620
 
621
  print(f"🎵 Analyzing: {os.path.basename(audio_path)}")
622
 
623
+ # 2 & 3. Run Physics and DL analysis in parallel (latency ≈ max of the two, not sum)
624
+ phys_score, phys_method, phys_feats = 0.0, "", {}
625
+ dl_score, dl_label = 0.0, ""
626
 
627
+ with ThreadPoolExecutor(max_workers=2) as executor:
628
+ future_phys = executor.submit(self.get_physics_score, audio_path)
629
+ future_dl = executor.submit(self.get_dl_score, audio_path)
630
+ for future in as_completed([future_phys, future_dl]):
631
+ try:
632
+ result = future.result()
633
+ # Physics returns (score, method, feats); DL returns (score, label)
634
+ if len(result) == 3:
635
+ phys_score, phys_method, phys_feats = result
636
+ else:
637
+ dl_score, dl_label = result
638
+ print(f"DL Score is: ${dl_score}")
639
+ except Exception as e:
640
+ print(f" ⚠️ Parallel task error: {e}")
641
+ # Cannot know which failed; set both fallbacks if needed
642
+ if future is future_phys:
643
+ phys_score, phys_method, phys_feats = 0.0, f"Error: {e}", {'duration': 0, 'was_truncated': False}
644
+ else:
645
+ dl_score, dl_label = 0.0, f"DL Error: {e}"
646
 
647
  # 4. Calculate weighted ensemble score
648
  final_score = (