diff --git a/pop/imab/bin/aesthetivox-alignment-video.py b/pop/imab/bin/aesthetivox-alignment-video.py index c634ecd51f..9dd4f6b2f9 100644 --- a/pop/imab/bin/aesthetivox-alignment-video.py +++ b/pop/imab/bin/aesthetivox-alignment-video.py @@ -28,7 +28,7 @@ SPB = 60.0 / BPM PXB = 96 PXS = PXB / SPB PLAYHEAD_X = 560 -PLACE = 2 * 4 * SPB # vocal enters after a 2-bar count-in +PLACE = 1 * 4 * SPB # one-bar count-in — short feedback loop SYNC_MS = float(os.environ.get("SYNC_MS", "50")) # measured on synccal: 50 ms locks on this display THEME = os.environ.get("SCORE_THEME") or ("light" if 7 <= datetime.datetime.now().hour < 19 else "dark") diff --git a/pop/imab/bin/boundfix.py b/pop/imab/bin/boundfix.py index a1cb172a0b..32d30a9597 100644 --- a/pop/imab/bin/boundfix.py +++ b/pop/imab/bin/boundfix.py @@ -9,7 +9,7 @@ is a windows file holyvox.mjs merges over the whisper timings. pop/.venv/bin/python pop/imab/bin/boundfix.py """ -import json, sys, re +import json, os, sys, re import numpy as np import librosa @@ -59,6 +59,15 @@ for i in idx: else: if idx.size: last["toMs"] = int(times[idx[-1]] * 1000) +# hand overrides (tracked, singer-confirmed) win over everything +ov_path = os.path.join(os.path.dirname(os.path.dirname(os.path.abspath(__file__))), + f"boundaries-{wav.split('whistlegraph-')[-1].split('/')[0]}.json") +if os.path.exists(ov_path): + ov = json.load(open(ov_path))["overrides"] + for w in seq: + o = ov.get(str(w["ti"])) + if o: w.update(o) + print(f" (+{len(ov)} hand overrides from {os.path.basename(ov_path)})") json.dump({"source": wav.split("/")[-1], "words": seq}, open(outp, "w"), indent=1) for w in seq: print(f" {w['text']:<12}{w['fromMs']/1000:6.2f} – {w['toMs']/1000:6.2f}") diff --git a/pop/imab/bin/holyvox.mjs b/pop/imab/bin/holyvox.mjs index 44e91b49eb..28945b4798 100644 --- a/pop/imab/bin/holyvox.mjs +++ b/pop/imab/bin/holyvox.mjs @@ -35,6 +35,7 @@ if (!existsSync(SEP)) { } // boundaries: energy-valley refined (boundfix.py), REQUIRED const BOUNDS = JSON.parse(readFileSync(`${WORK}/bounds-${TAKE}.json`, "utf8")).words; +const doc = JSON.parse(readFileSync(`${WORK}/stem-${TAKE}.syllnote.json`, "utf8")); const NSYL = { butterfly: 3, flapping: 2, costume: 2 }; // the melody is fully @jeffrey's — absolute pitches, no detection involved // notepat truth (@jeffrey): c g c c c · c h c c c · g f e d e e d c c c diff --git a/pop/imab/bin/lyricline.mjs b/pop/imab/bin/lyricline.mjs index 2fa03bb0ff..c5d017e726 100644 --- a/pop/imab/bin/lyricline.mjs +++ b/pop/imab/bin/lyricline.mjs @@ -30,7 +30,7 @@ const readF32 = (wav) => { return new Float32Array(b.buffer, b.byteOffset, Math.floor(b.length / 4)); }; const vox = readF32(VOX); -const PLACE = 2 * BAR; +const PLACE = 1 * BAR; const total = PLACE + vox.length / SR + 2 * BAR; const NT = Math.ceil(total * SR); const mix = new Float32Array(NT); diff --git a/pop/imab/boundaries-7311159624588070175.json b/pop/imab/boundaries-7311159624588070175.json new file mode 100644 index 0000000000..6628ef5e1c --- /dev/null +++ b/pop/imab/boundaries-7311159624588070175.json @@ -0,0 +1,12 @@ +{ + "note": "hand overrides on the energy-valley pass — by template word index; the flapping/for/you region from voiced-run analysis (PING vowel = 6.03-6.77 C#4 hold), whisper swallowed 'for you' into flapping", + "overrides": { + "0": { "fromMs": 100, "toMs": 950 }, + "1": { "fromMs": 950, "toMs": 1320 }, + "2": { "fromMs": 1320, "toMs": 4550 }, + "3": { "fromMs": 4820, "toMs": 6850 }, + "4": { "fromMs": 6850, "toMs": 7350 }, + "5": { "fromMs": 7350, "toMs": 7900 }, + "6": { "fromMs": 7900, "toMs": 9500 } + } +}