feat(audio): Music becomes a crossfading playlist of 5 mellow synth tracks

Replace the single ethereal loop with the operator's chosen set — Music for
Manatees, Dewdrop Fantasy, Eastminster, Overheat, Concentration (Kevin MacLeod,
CC BY 4.0, drumless). The Music source now plays one track and every 5 min
crossfades to a random OTHER track (pure HEFScrub.pickNextIndex, never an
immediate repeat), altitude-independent, reusing the two audio elements; both
primed in-gesture for Safari. Intervals injectable via window.__hefMusicRotateMs
/ __hefMusicXfadeMs.

Media loudness-normalized + transcoded under the 25MB LFS limit (build_audio_media
MUSIC), committed via LFS; ethereal loop retired. CC-BY attribution added to
credits.html. Docs updated (music-candidate-pool = final set; audio-candidate-pool
ethereal section superseded).

+2 node unit (pickNextIndex), +2 e2e (music-playlist), audio-source spec updated.
40 unit + 49 e2e green; live probe confirms playback + rotation.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
BenStullsBets
2026-07-02 16:22:28 -07:00
parent 969236b3f7
commit 4f4f21a767
16 changed files with 246 additions and 56 deletions
+20
View File
@@ -26,6 +26,26 @@ test("audioPlan: Music ignores altitude fraction (constant across the dial)", ()
assert.deepEqual(a, b);
});
test("pickNextIndex: never returns the current track; covers the rest", () => {
// r in [0,1) maps onto the (count-1) other indices, skipping `current`.
assert.equal(S.pickNextIndex(0, 5, 0), 1);
assert.equal(S.pickNextIndex(0, 5, 0.999), 4);
assert.equal(S.pickNextIndex(2, 5, 0), 0);
assert.equal(S.pickNextIndex(2, 5, 0.5), 3);
assert.equal(S.pickNextIndex(4, 5, 0.999), 3);
// sweep: for each current, every r lands somewhere valid and never on current
for (let cur = 0; cur < 5; cur++) {
for (let i = 0; i < 20; i++) {
const n = S.pickNextIndex(cur, 5, i / 20);
assert.ok(n >= 0 && n < 5 && n !== cur, `cur=${cur} r=${i / 20} -> ${n}`);
}
}
});
test("pickNextIndex: single-track playlist stays on 0", () => {
assert.equal(S.pickNextIndex(0, 1, 0.7), 0);
});
test("audioPlan: Soundtrack blends lo/hi by fraction, scaled by level", () => {
assert.deepEqual(S.audioPlan("soundtrack", 1, 0), { mode: "blend", lo: 1, hi: 0 });
assert.deepEqual(S.audioPlan("soundtrack", 1, 1), { mode: "blend", lo: 0, hi: 1 });