31 lines
2.1 KiB
Python
31 lines
2.1 KiB
Python
"""Real local ASR + separation regression. Pass an actual Chinese speech WAV fixture.
|
|
Usage: SMART_EDIT_*_MODEL=... python scripts/qa/smart-edit-models.py speech.wav output-dir
|
|
No provider calls; the input and all derived files stay local.
|
|
"""
|
|
import json,sys,subprocess,os
|
|
from pathlib import Path
|
|
import numpy as np
|
|
import soundfile as sf
|
|
sys.path.insert(0,str(Path('services/smart-edit').resolve()))
|
|
from analyze import analyze
|
|
from separate import separate
|
|
source=Path(sys.argv[1]).resolve();root=Path(sys.argv[2]).resolve();root.mkdir(parents=True,exist_ok=True)
|
|
subprocess.run(['ffmpeg','-v','error','-y','-i',str(source),'-ac','1','-ar','16000',str(root/'speech.wav')],check=True)
|
|
result=analyze({'audio':str(root/'speech.wav'),'transcribe':True})
|
|
text=''.join(s['text'] for s in result['subtitles']);assert '智能剪辑' in text and '人声分离' in text,text
|
|
assert len(result['subtitles'])>=2,'Sentence timestamps must be enabled'
|
|
assert all(0<=s['start']<s['end'] for s in result['subtitles'])
|
|
subprocess.run(['ffmpeg','-v','error','-y','-i',str(source),'-ac','2','-ar','44100',str(root/'clean.wav')],check=True)
|
|
clean,sr=sf.read(root/'clean.wav',dtype='float32',always_2d=True)
|
|
t=np.arange(len(clean))/sr
|
|
music=(.06*np.sin(2*np.pi*220*t)+.04*np.sin(2*np.pi*330*t))*(.6+.4*np.sin(2*np.pi*2*t)**2)
|
|
mix=clean+music[:,None];sf.write(root/'mixed.wav',mix,sr,subtype='FLOAT')
|
|
separate(str(root/'mixed.wav'),str(root))
|
|
v,_=sf.read(root/'vocals.wav',dtype='float32',always_2d=True);b,_=sf.read(root/'background.wav',dtype='float32',always_2d=True)
|
|
assert v.shape==b.shape==mix.shape
|
|
error=float(np.max(np.abs(v+b-mix)));assert error<1e-6,error
|
|
assert float(np.sqrt(np.mean(v*v)))>.005 and float(np.sqrt(np.mean(b*b)))>.005
|
|
assert not np.allclose(v,mix),'Separation must not return the original track'
|
|
report={'subtitles':result['subtitles'],'duration':len(clean)/sr,'stemReconstructionMaxError':error,'vocalRms':float(np.sqrt(np.mean(v*v))),'backgroundRms':float(np.sqrt(np.mean(b*b)))}
|
|
(root/'report.json').write_text(json.dumps(report,ensure_ascii=False,indent=2));print(json.dumps(report,ensure_ascii=False))
|