Simplification

This commit is contained in:
khodowany
2026-10-05 17:24:16 -05:00
parent bcaf2f1403
commit db7966a430
12 changed files with 639 additions and 123 deletions
+26
View File
@@ -327,6 +327,23 @@ def audition_pair(actual,expected,role,assets,output,cache,baseline=None):
'listening_status':'Rendered and signal-checked, not subjectively auditioned.'}
def candidate_clips(candidates,role,tempo,output,cache):
"""Render the already completed source-only alternatives, never reselect."""
from generator import playback
clips=[]
for index,candidate in enumerate(candidates,1):
origin,end=F(candidate['start']),F(candidate['end'])
audio=render_events([(F(a)-origin,F(b)-origin,F(p)) for a,b,p in candidate['events']],end-origin,tempo,output,cache)
peak=.65*max(map(abs,audio),default=0)
if peak>=1:raise ValueError('Candidate excerpt would clip at comparison gain')
name='audio/'+role.replace('/','_')+f'_candidate_{index}.wav'
playback.write_pcm(output/name,(.65*v for v in audio))
selected=candidate['selected_in_final_solo']
clips.append({'file':name,'version':f'Completed opening {index}'+(' — selected in final solo' if selected else ' — alternative'),
'context':'source-only candidate, solo','peak':peak})
return clips
def run(generated_dir,reference_paths,starts,pitch_convention,output,assets=None,baseline_dir=None):
generated_dir,output=Path(generated_dir),Path(output)
manifest=json.loads((generated_dir/'manifest.solo').read_text())
@@ -348,6 +365,9 @@ def run(generated_dir,reference_paths,starts,pitch_convention,output,assets=None
if assets:review['audition']=audition_pair(actual,expected,role,assets,output,tone_cache,baseline)
review['production_trace']=item['trace']
review['production_phrases']=item.get('phrase_report',{})
candidates=review['production_phrases'].get('selection_trace',{}).get('opening_candidates',[])
if assets and candidates:
review['audition']['clips']+=candidate_clips(candidates,role,F(review['audition']['tempo']),output,tone_cache)
# Preserve the real role cutoff; reference material beyond it is not trimmed.
phrases=review['production_phrases'].get('phrases',[])
if phrases:
@@ -384,6 +404,12 @@ def run(generated_dir,reference_paths,starts,pitch_convention,output,assets=None
for purpose,counts in sorted(funnel.items()):
rows.append('<tr><td>'+html.escape(purpose)+'</td>'+''.join('<td>'+str(counts.get(k,0))+'</td>' for k in stages)+'</tr>')
rows.append('</table></details>')
candidates=r['production_phrases'].get('selection_trace',{}).get('opening_candidates',[])
if candidates:
rows.append('<details open><summary>Completed source-only opening candidates</summary><p>All candidates were composed without the references. Selection accounts for later phrases; the final choice need not have the cheapest opening alone.</p><table><tr><th>Candidate</th><th>Final choice</th><th>Purpose</th><th>Score contributions</th></tr>')
for index,c in enumerate(candidates,1):
rows.append('<tr>'+''.join('<td>'+html.escape(str(v))+'</td>' for v in (index,c['selected_in_final_solo'],c['purpose'],c['evaluation']['contributions']))+'</tr>')
rows.append('</table></details>')
for clip in r.get('audition',{}).get('clips',[]):
rows.append('<p>'+html.escape(clip['version']+' / '+clip['context'])+'<br><audio controls preload="none" src="'+html.escape(clip['file'])+'"></audio></p>')
rows.append('<details><summary>Exact note differences</summary><table><tr><th>Beat relative to form</th><th>Generated</th><th>Reference</th></tr>')