Spaces:
Running
Running
S190: WPE runs in a timeout-guarded subprocess instead of unbounded in-process
Browse files- engine_safaa_v4.py +56 -25
engine_safaa_v4.py
CHANGED
|
@@ -41,7 +41,7 @@ from __future__ import annotations
|
|
| 41 |
|
| 42 |
__version__ = 'v4'
|
| 43 |
|
| 44 |
-
import os, shutil, subprocess, tempfile, warnings
|
| 45 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 46 |
from dataclasses import dataclass, field
|
| 47 |
from pathlib import Path
|
|
@@ -397,23 +397,13 @@ def _lra_overlapping(s, frame_s=0.4, hop_s=0.2, sr=SR):
|
|
| 397 |
for i in range(0, len(s) - fn, hn)]
|
| 398 |
return float(np.percentile(db, 95) - np.percentile(db, 10)) if len(db) >= 4 else 0.0
|
| 399 |
|
| 400 |
-
|
| 401 |
-
if not WPE_OK:
|
| 402 |
-
_L(st, ' [S3-WPE] nara_wpe not installed β pip install nara_wpe soundfile')
|
| 403 |
-
return wav
|
| 404 |
-
if rt60 < RT60_WPE_MIN:
|
| 405 |
-
_L(st, f' [S3-WPE] RT60={rt60:.2f}s < {RT60_WPE_MIN}s β skip (Β§109.6)')
|
| 406 |
-
return wav
|
| 407 |
|
| 408 |
-
|
| 409 |
-
|
|
|
|
|
|
|
| 410 |
try:
|
| 411 |
-
mi = os.path.join(d, 'in.wav')
|
| 412 |
-
rc, _, _ = _run(['ffmpeg', '-y', '-i', wav,
|
| 413 |
-
'-acodec', 'pcm_f32le', '-ar', str(SR), '-ac', '1',
|
| 414 |
-
'-loglevel', 'error', mi])
|
| 415 |
-
if rc or not os.path.exists(mi):
|
| 416 |
-
return wav
|
| 417 |
if SF_OK:
|
| 418 |
y, _ = SF.read(mi, dtype='float32', always_2d=False)
|
| 419 |
else:
|
|
@@ -421,10 +411,10 @@ def _wpe(wav, rt60, st):
|
|
| 421 |
with _w.open(mi, 'rb') as f: raw = f.readframes(f.getnframes())
|
| 422 |
y = np.frombuffer(raw, dtype=np.float32).copy()
|
| 423 |
|
| 424 |
-
if
|
| 425 |
-
elif rt60 > 4.0:
|
| 426 |
-
elif rt60 > 2.0:
|
| 427 |
-
else:
|
| 428 |
delay = 3
|
| 429 |
|
| 430 |
Y = _wpe_stft(y, size=512, shift=128)
|
|
@@ -434,17 +424,15 @@ def _wpe(wav, rt60, st):
|
|
| 434 |
|
| 435 |
ld = abs(_lra_overlapping(y) - _lra_overlapping(z)) # [I5]
|
| 436 |
if ld > _LRA_MAX_DELTA:
|
| 437 |
-
_L(st, f' [S3-WPE] LRA Ξ={ld:.2f}LU β retry iters-1')
|
| 438 |
Z2 = wpe_v8(Y[..., np.newaxis], taps=taps, delay=delay, iterations=max(1, iters - 1))
|
| 439 |
z2 = _wpe_istft(Z2[..., 0], size=512, shift=128)
|
| 440 |
z2 = z2[:len(y)] if len(z2) > len(y) else np.pad(z2, (0, len(y) - len(z2)))
|
| 441 |
ld2 = abs(_lra_overlapping(y) - _lra_overlapping(z2))
|
| 442 |
if ld2 > _LRA_MAX_DELTA:
|
| 443 |
-
|
| 444 |
-
|
| 445 |
z = z2; ld = ld2
|
| 446 |
|
| 447 |
-
mo = os.path.join(d, 'out.wav')
|
| 448 |
if SF_OK:
|
| 449 |
SF.write(mo, z.astype(np.float32), SR, subtype='FLOAT')
|
| 450 |
else:
|
|
@@ -453,11 +441,54 @@ def _wpe(wav, rt60, st):
|
|
| 453 |
with _w.open(mo, 'wb') as f:
|
| 454 |
f.setnchannels(1); f.setsampwidth(2); f.setframerate(SR)
|
| 455 |
f.writeframes(b16.tobytes())
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 456 |
out = _tmp('s3', st)
|
| 457 |
if not _enc_mono(mo, out):
|
| 458 |
return wav
|
| 459 |
st.wpe = True
|
| 460 |
-
_L(st, f
|
|
|
|
| 461 |
return out
|
| 462 |
except Exception as e:
|
| 463 |
_L(st, f' [S3-WPE] exception: {e}'); return wav
|
|
|
|
| 41 |
|
| 42 |
__version__ = 'v4'
|
| 43 |
|
| 44 |
+
import os, shutil, subprocess, tempfile, warnings, multiprocessing as mp
|
| 45 |
from concurrent.futures import ThreadPoolExecutor, as_completed
|
| 46 |
from dataclasses import dataclass, field
|
| 47 |
from pathlib import Path
|
|
|
|
| 397 |
for i in range(0, len(s) - fn, hn)]
|
| 398 |
return float(np.percentile(db, 95) - np.percentile(db, 10)) if len(db) >= 4 else 0.0
|
| 399 |
|
| 400 |
+
WPE_TIMEOUT_S = 240 # S190: hard cap so a slow/atypical file can't eat the whole 600s job budget
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 401 |
|
| 402 |
+
def _wpe_compute_worker(mi, mo, rt60, mujawwad_conf, q):
|
| 403 |
+
"""Runs in a separate forked process so a slow/hung wpe_v8 call can be
|
| 404 |
+
killed without taking down the whole engine. Writes result wav to `mo`
|
| 405 |
+
on success; only a small dict goes back through the queue."""
|
| 406 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 407 |
if SF_OK:
|
| 408 |
y, _ = SF.read(mi, dtype='float32', always_2d=False)
|
| 409 |
else:
|
|
|
|
| 411 |
with _w.open(mi, 'rb') as f: raw = f.readframes(f.getnframes())
|
| 412 |
y = np.frombuffer(raw, dtype=np.float32).copy()
|
| 413 |
|
| 414 |
+
if mujawwad_conf > 0.6: taps, iters = 5, 2
|
| 415 |
+
elif rt60 > 4.0: taps, iters = 12, 3
|
| 416 |
+
elif rt60 > 2.0: taps, iters = 10, 3
|
| 417 |
+
else: taps, iters = 8, 3
|
| 418 |
delay = 3
|
| 419 |
|
| 420 |
Y = _wpe_stft(y, size=512, shift=128)
|
|
|
|
| 424 |
|
| 425 |
ld = abs(_lra_overlapping(y) - _lra_overlapping(z)) # [I5]
|
| 426 |
if ld > _LRA_MAX_DELTA:
|
|
|
|
| 427 |
Z2 = wpe_v8(Y[..., np.newaxis], taps=taps, delay=delay, iterations=max(1, iters - 1))
|
| 428 |
z2 = _wpe_istft(Z2[..., 0], size=512, shift=128)
|
| 429 |
z2 = z2[:len(y)] if len(z2) > len(y) else np.pad(z2, (0, len(y) - len(z2)))
|
| 430 |
ld2 = abs(_lra_overlapping(y) - _lra_overlapping(z2))
|
| 431 |
if ld2 > _LRA_MAX_DELTA:
|
| 432 |
+
q.put({'ok': False, 'reason': f'LRA {ld2:.2f}LU after retry β REVERT'})
|
| 433 |
+
return
|
| 434 |
z = z2; ld = ld2
|
| 435 |
|
|
|
|
| 436 |
if SF_OK:
|
| 437 |
SF.write(mo, z.astype(np.float32), SR, subtype='FLOAT')
|
| 438 |
else:
|
|
|
|
| 441 |
with _w.open(mo, 'wb') as f:
|
| 442 |
f.setnchannels(1); f.setsampwidth(2); f.setframerate(SR)
|
| 443 |
f.writeframes(b16.tobytes())
|
| 444 |
+
q.put({'ok': True, 'taps': taps, 'delay': delay, 'iters': iters, 'ld': float(ld)})
|
| 445 |
+
except Exception as e:
|
| 446 |
+
q.put({'ok': False, 'reason': f'exception: {e}'})
|
| 447 |
+
|
| 448 |
+
def _wpe(wav, rt60, st):
|
| 449 |
+
if not WPE_OK:
|
| 450 |
+
_L(st, ' [S3-WPE] nara_wpe not installed β pip install nara_wpe soundfile')
|
| 451 |
+
return wav
|
| 452 |
+
if rt60 < RT60_WPE_MIN:
|
| 453 |
+
_L(st, f' [S3-WPE] RT60={rt60:.2f}s < {RT60_WPE_MIN}s β skip (Β§109.6)')
|
| 454 |
+
return wav
|
| 455 |
+
|
| 456 |
+
_L(st, f' [S3-WPE] RT60={rt60:.2f}s β running WPE (cap {WPE_TIMEOUT_S}s)')
|
| 457 |
+
d = tempfile.mkdtemp(prefix='safaa3_wpe_')
|
| 458 |
+
try:
|
| 459 |
+
mi = os.path.join(d, 'in.wav')
|
| 460 |
+
rc, _, _ = _run(['ffmpeg', '-y', '-i', wav,
|
| 461 |
+
'-acodec', 'pcm_f32le', '-ar', str(SR), '-ac', '1',
|
| 462 |
+
'-loglevel', 'error', mi])
|
| 463 |
+
if rc or not os.path.exists(mi):
|
| 464 |
+
return wav
|
| 465 |
+
|
| 466 |
+
mo = os.path.join(d, 'out.wav')
|
| 467 |
+
ctx = mp.get_context('fork')
|
| 468 |
+
q = ctx.Queue()
|
| 469 |
+
proc = ctx.Process(target=_wpe_compute_worker, args=(mi, mo, rt60, st.mujawwad_conf, q))
|
| 470 |
+
proc.start()
|
| 471 |
+
proc.join(WPE_TIMEOUT_S)
|
| 472 |
+
if proc.is_alive():
|
| 473 |
+
proc.terminate(); proc.join(5)
|
| 474 |
+
if proc.is_alive():
|
| 475 |
+
proc.kill(); proc.join(5)
|
| 476 |
+
st.guard_reverts += 1
|
| 477 |
+
_L(st, f' [S3-WPE] TIMED OUT after {WPE_TIMEOUT_S}s β using source for this stage only')
|
| 478 |
+
return wav
|
| 479 |
+
|
| 480 |
+
result = q.get() if not q.empty() else {'ok': False, 'reason': 'worker died with no result'}
|
| 481 |
+
if not result.get('ok'):
|
| 482 |
+
st.guard_reverts += 1
|
| 483 |
+
_L(st, f" [S3-WPE] {result.get('reason', 'failed')} β REVERT")
|
| 484 |
+
return wav
|
| 485 |
+
|
| 486 |
out = _tmp('s3', st)
|
| 487 |
if not _enc_mono(mo, out):
|
| 488 |
return wav
|
| 489 |
st.wpe = True
|
| 490 |
+
_L(st, f" [S3-WPE] β taps={result['taps']} delay={result['delay']} "
|
| 491 |
+
f"iters={result['iters']} LRA Ξ={result['ld']:.2f}LU")
|
| 492 |
return out
|
| 493 |
except Exception as e:
|
| 494 |
_L(st, f' [S3-WPE] exception: {e}'); return wav
|