// Longest musically-homogeneous stretches in the longplay, from the cached chroma. // The middle filler only has to look like a battle - the game's own audio is CUT // under the ums (DUCK=0) - so a long stretch of ANY single battle theme (gym, // rival, Elite Four) would need one cut in and one out instead of three. import { readFileSync } from "node:fs"; const b = readFileSync("pk-chroma.f32"); const chroma = new Float32Array(b.buffer, b.byteOffset, b.length / 4); const nFrames = chroma.length / 12; const WIN_S = 6, WF = WIN_S * 4; function profile(f) { const p = new Float64Array(12); for (let i = 0; i < WF; i += 1) for (let k = 0; k < 12; k += 1) p[k] += chroma[(f + i) * 12 + k]; let m = 0; for (const x of p) m += x; m /= 12; for (let k = 0; k < 12; k += 1) p[k] -= m; let e = 0; for (const x of p) e += x * x; return { p, e: Math.sqrt(e) || 1 }; } const cos = (a, b2) => { let d = 0; for (let k = 0; k < 12; k += 1) d += a.p[k] * b2.p[k]; return d / (a.e * b2.e); }; // step 1s; grow a segment while each window still matches the segment's anchor const STEP = 4, THR = 0.80; const segs = []; let f = 0; while (f + WF < nFrames) { const anchor = profile(f); let g = f + STEP, miss = 0; while (g + WF < nFrames) { if (cos(anchor, profile(g)) >= THR) miss = 0; else if (++miss >= 3) break; // tolerate 3s of dropout g += STEP; } const len = (g - f) / 4; if (len >= 45) segs.push({ start: f / 4, len }); f = g > f + STEP ? g : f + STEP; } const fmt = (s) => `${Math.floor(s / 60)}:${(s % 60).toFixed(0).padStart(2, "0")}`; segs.sort((a, b2) => b2.len - a.len); console.log(`${segs.length} homogeneous segments >=45s. Longest 20:`); for (const s of segs.slice(0, 20)) console.log(` ${s.start.toFixed(1)}s (${fmt(s.start)}) len ${s.len.toFixed(1)}s`);