From 25e080151cd5d73ee522b13f09f4b546d003146a Mon Sep 17 00:00:00 2001 From: "prompt.ac/@jeffrey" Date: Thu, 24 Sep 2026 16:47:00 -0700 Subject: [PATCH] Fleet: syllable timings on every lyric line; the display beat at 5 Hz with the sung syllable and each face's line Co-Authored-By: Claude Fable 5.1 --- .../macneopolitan/bin/run-full-trio.py | 11 +++++++++-- .../macneopolitan/bin/trio-fleet-plan.mjs | 6 ++++-- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/grants/culturehub-la-2026/macneopolitan/bin/run-full-trio.py b/grants/culturehub-la-2026/macneopolitan/bin/run-full-trio.py index 5edda09daf..fbb71d20ee 100644 --- a/grants/culturehub-la-2026/macneopolitan/bin/run-full-trio.py +++ b/grants/culturehub-la-2026/macneopolitan/bin/run-full-trio.py @@ -23,7 +23,11 @@ def lyric_at(t): def visuals(playing,elapsed): cur,nxt=lyric_at(elapsed) if playing else (None,None) body={'playing':playing,'elapsed':max(-1,min(plan['duration']+1,elapsed)),'title':plan.get('title'),'dance':'trio-round-v1','bpm':plan['bpm'],'duration':plan['duration'], - 'lyric':cur and {'text':cur['text'],'member':cur['member'],'rgb':cur['rgb'],'t':cur['t'],'dur':cur['dur']},'next':nxt and {'text':nxt['text'],'member':nxt['member'],'rgb':nxt['rgb'],'in':round(nxt['t']-elapsed,2)}} + 'lyric':cur and {'text':cur['text'],'member':cur['member'],'rgb':cur['rgb'],'t':cur['t'],'dur':cur['dur'],'syllables':cur.get('syllables',[]), + 'syllable':next((i for i in range(len(cur.get('syllables',[]))-1,-1,-1) if cur['syllables'][i]['t']<=elapsed),-1)}, + 'next':nxt and {'text':nxt['text'],'member':nxt['member'],'rgb':nxt['rgb'],'in':round(nxt['t']-elapsed,2)}, + 'faces':{m:(l and {'text':l['text'],'rgb':l['rgb'],'t':l['t'],'dur':l['dur']}) for m,l in ((m,next((l for l in plan.get('lyrics',[]) if l['member']==m and elapsed>=l['t']-.3 and elapsed{ const lines=v.lyrics.split(' / ').map(l=>l.trim().split(/\s+/).filter(t=>t!=='/').map(t=>t.replace(/-/g,'')).join(' ')); // words, not syllables, for the notation lines.forEach((text,k)=>{const ns=parts[i].filter(n=>n.line===k);if(!ns.length)return; - phrases.push({member:members[i],memberIndex:i,phrase:k,text,t:ns[0].t,dur:Math.max(...ns.map(n=>n.t+n.dur))-ns[0].t,notes:ns.map(n=>n.note)});}); + const toks=(v.lyrics.split(' / ')[k]||'').trim().split(/\s+/).filter(t=>t!=='/').flatMap(t=>t.split('-')); // one syllable per note + phrases.push({member:members[i],memberIndex:i,phrase:k,text,t:ns[0].t,dur:Math.max(...ns.map(n=>n.t+n.dur))-ns[0].t,notes:ns.map(n=>n.note), + syllables:ns.map((n,j)=>({t:+n.t.toFixed(3),dur:+n.dur.toFixed(3),text:toks[j]||'',note:n.note}))});}); }); phrases.sort((a,b)=>a.t-b.t||a.memberIndex-b.memberIndex); const beat=60/score.bpm; @@ -138,7 +140,7 @@ export function buildPlan(score,profiles,fleet,levels={}) { const plan={schema:'trio-fleet-plan-v1',title:score.title,bpm:score.bpm,duration:dur,levels,payloads,nodes,events, requiredReceivers:[...members.map(m=>`singer-${m}`),...nodes.map(n=>n.id),'sub','dmx'], layers:src,routes,colors:Object.fromEntries(members.map((m,i)=>[m,colors[i]])), - lyrics:phrases.map(p=>({t:p.t,dur:p.dur,text:p.text,member:p.member,rgb:colors[p.memberIndex]})), + lyrics:phrases.map(p=>({t:p.t,dur:p.dur,text:p.text,member:p.member,rgb:colors[p.memberIndex],syllables:p.syllables})), sections:(score.arrangement?.sections??[]).map(s=>({name:s.name,beat:s.beat})),arrangement:{total:score.arrangement?.total??null,meter:score.arrangement?.meter??null}, dmx:{host:'192.168.1.235',port:8790,activeAddresses:addresses,inactiveAddresses:[41,511]}, sub:{host:'192.168.1.67',port:8788,transport:'rustdesk-local-bridge'}, -- 2.51.2