ai-chatbot/scripts/qa/smart-edit-models.py

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))