from pathlib import Path
import hashlib,json,subprocess
import numpy as np
import soundfile as sf
root=Path(__file__).resolve().parent
original=root.parent/'original-song/output-0.m4a'
revision=root.parent/'infill-request/output-0.m4a'
rate=48000;start=18.8;end=23.3;phrase_start=9.1;phrase_end=13.54
for name,src in [('original-decoded',original),('revision-decoded',revision)]:
 subprocess.run(['ffmpeg','-v','error','-y','-i',str(src),'-ar',str(rate),'-ac','2','-codec:a','pcm_f32le',str(root/(name+'.wav'))],check=True)
ratio=(phrase_end-phrase_start)/(end-start)
subprocess.run(['ffmpeg','-v','error','-y','-ss',str(phrase_start),'-t',str(phrase_end-phrase_start),'-i',str(root/'revision-decoded.wav'),'-af',f'atempo={ratio},apad','-t',str(end-start),'-codec:a','pcm_f32le',str(root/'replacement-phrase.wav')],check=True)
a,s=sf.read(root/'original-decoded.wav',dtype='float32',always_2d=True);b,_=sf.read(root/'replacement-phrase.wav',dtype='float32',always_2d=True)
i=round(start*s);j=round(end*s);n=j-i;out=a.copy();w=np.ones((n,1),dtype='float32');fade=round(.08*s);w[:fade,0]=np.linspace(0,1,fade,dtype='float32');w[-fade:,0]=np.linspace(1,0,fade,dtype='float32')
out[i:j]=a[i:j]*(1-w)+b[:n]*w
sf.write(root/'region-replaced.wav',out,s,subtype='FLOAT');read,_=sf.read(root/'region-replaced.wav',dtype='float32',always_2d=True)
report={'method':'Actual QuickRouter Suno original + revised full song; local bounded crossfade replacement','nativeInfillVerified':False,'reason':'task=infill returned a new 34-second whole-song version versus original39.48s; 200 did not prove region preservation','regionSeconds':[start,end],'revisedPhraseSeconds':[phrase_start,phrase_end],'sampleRate':s,'originalFrames':len(a),'resultFrames':len(read),'beforeRegionIdentical':bool(np.array_equal(a[:i],read[:i])),'afterRegionIdentical':bool(np.array_equal(a[j:],read[j:])),'replacementChanged':bool(not np.array_equal(a[i:j],read[i:j])),'outputSha256':hashlib.sha256((root/'region-replaced.wav').read_bytes()).hexdigest(),'qualityLimit':'Phrase is from a different generated rendition. Timbre, harmony and joins need listening; not seamless AI re-singing or native Suno Replace Section.'}
assert report['beforeRegionIdentical']and report['afterRegionIdentical']and report['replacementChanged'];(root/'validation.json').write_text(json.dumps(report,indent=2));print(report)
