From ee7190bb4a83dc98f0b0aa804277c311a09c8caf Mon Sep 17 00:00:00 2001 From: BenStullsBets Date: Wed, 1 Jul 2026 21:55:23 -0700 Subject: [PATCH] feat(audio): Audio source dropdown (None/Soundtrack/Music) + Volume rename MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Split the single 0–10 'Audio' slider into two controls: - Audio — a dropdown selecting the source: None / Soundtrack / Music - Volume — the renamed 0–10 slider (master gain for the active source) Music is one ethereal, relaxing ambient loop played the same at every altitude (no per-altitude crossfade), per the innerflo.me brief. Sourced CC0: deadrobotmusic 'Ambient F Sharp Minor Ethereal Choir Pad' (freesound #808032); added to build_audio_media.py, the candidate-pool doc, and the public credits colophon. Media stays gitignored; run build_audio_media.py. Routing lives in a pure, node-tested HEFScrub.audioPlan(source, level, frac); app.js's restAudio/blendAudio consult it. None greys out Volume. The welcome screen's mirror level slider is relabeled Volume too. i18n for all 4 langs. Spec: docs/superpowers/specs/2026-07-01-audio-music-option-design.md Tests: +5 node unit (audioPlan), +4 e2e (audio-source.spec.ts); 32 related e2e green, 38 node unit green. Co-Authored-By: Claude Opus 4.8 --- docs/audio-candidate-pool.md | 27 ++++++- simulator/build_audio_media.py | 9 ++- simulator/e2e/tests/audio-source.spec.ts | 73 +++++++++++++++++++ simulator/static/app.js | 93 +++++++++++++++++------- simulator/static/credits.html | 1 + simulator/static/i18n.js | 4 + simulator/static/index.html | 11 ++- simulator/static/scrub.js | 17 ++++- simulator/unit/audio.test.js | 36 +++++++++ 9 files changed, 239 insertions(+), 32 deletions(-) create mode 100644 simulator/e2e/tests/audio-source.spec.ts create mode 100644 simulator/unit/audio.test.js diff --git a/docs/audio-candidate-pool.md b/docs/audio-candidate-pool.md index 77cd033..140c3b4 100644 --- a/docs/audio-candidate-pool.md +++ b/docs/audio-candidate-pool.md @@ -9,8 +9,10 @@ model — random pick per landing, or pairable per-clip). The "audio + video" setting layers **two** kinds of audio per altitude: 1. **Ambience / audio track** — the soundscape (waves, reef crackle, chorus, etc.). **This is what we're sourcing now.** -2. **Music track** — a separate composed musical layer. **Deferred** until the - ambience tracks are settled across all altitudes. +2. **Music track** — a separate composed musical layer. **Shipped** (2026-07-01) + as the Audio-dropdown **"Music"** option: unlike the earlier per-altitude idea, + it is now **one single ethereal loop played the same at every altitude** (see + § Music below). Pools below are the **ambience** layer unless marked `music`. **Distinction (operator):** the ambience/soundtrack layer is an *environmental, real-world soundscape* (waves, reef, @@ -48,6 +50,22 @@ recordings (NASA/NOAA) keep the "machines reshaping perception" thesis literal. | 🏖 coast | `coast/waves` | Sea waves + tern calls | BigSoundBank #0267 — https://bigsoundbank.com/sea-waves-and-seagulls-s0267.html (`/UPLOAD/mp3/0267.mp3`) | 57s | CC0 | | 🕳 abyss | `abyss/whale` | NE Pacific blue-whale "AB call" | NOAA PMEL — https://www.pmel.noaa.gov/acoustics/multimedia/NETS_bluWhale.wav | 13s | PD (NOAA) | +## Music (the Audio-dropdown "Music" option, shipped 2026-07-01) + +**One** ethereal ambient loop, played the same at every altitude (altitude-independent — +NOT the per-altitude idea the "cosmos/music" / "Frozen Star" rows above reserved; those +stay as historical candidates). The operator's brief: *ethereal, relaxing — like +innerflo.me.* CC0 (operator's sourcing choice), so attribution is courtesy, not required. + +| id | clip | source | dur | license | +|---|---|---|---|---| +| `music/ethereal` | **✓ KEEP** — "Ambient F Sharp Minor Ethereal Choir Pad 1": blurry, paulstretched layered choir — "peaceful, magical, soothing" | deadrobotmusic — https://freesound.org/people/deadrobotmusic/sounds/808032/ (hq preview: `cdn.freesound.org/previews/808/808032_*-hq.mp3`) | 3:38 | CC0 | + +Production: `simulator/build_audio_media.py` `SOURCES["music"]` → seamless loudness-normalized +loop at `audio/music/ethereal.loop.mp3`, served `/media/audio/music/ethereal.loop.mp3` +(client fallback: `app.js` `MUSIC_FALLBACK`). Download the sound page's WAV/MP3 to +`simulator/sample_media/audio/music/ethereal.mp3`, then `python simulator/build_audio_media.py`. + ## Sourcing rule (learned 2026-06-26) **Source only from a browsable page the operator can open and whose license is stated.** @@ -67,7 +85,10 @@ hubs: NASA SVS / Chandra, NOAA PMEL/SanctSound (PD), BigSoundBank (CC0), Pixabay - **reef** — ✅ round-4 6-layer bake (`reef/soundscape`): NOAA PD biological layers + a CC0 submerged underwater "you're under" bed (DCSFX freesound #366159, hq preview). License clean. SanctSound clips: `files/SanctSound_*.mp4` at `https://sanctsound.ioos.us/files/` (extract audio). -- **music layer** — separate composed music track per altitude, deferred (cosmos slot reserved). +- **music layer** — ✅ SHIPPED 2026-07-01 as the Audio-dropdown "Music" option, but as + ONE altitude-independent ethereal loop (`music/ethereal`, CC0), not the per-altitude + track once envisioned. See § Music above. (The `cosmos/music` / "Frozen Star" rows are + now historical per-altitude candidates, not planned work.) - **Production (after picks):** loop seams, length normalization, cross-altitude crossfade (mirror video); cosmos/abyss clips are short → loop. diff --git a/simulator/build_audio_media.py b/simulator/build_audio_media.py index 4a1a3e0..fafc287 100644 --- a/simulator/build_audio_media.py +++ b/simulator/build_audio_media.py @@ -1,4 +1,5 @@ -"""Production pass for the 5 per-altitude soundtracks (audio spec §5.3). The audio +"""Production pass for the 5 per-altitude soundtracks + the single Music loop +(audio spec §5.3; Music layer per docs/audio-candidate-pool.md). The audio analogue of build_pool_manifest.py --media: read the sourced ambience clips (re-downloadable per docs/audio-candidate-pool.md), and make each a seamless loudness-normalized loop. @@ -22,12 +23,18 @@ AUDIO = Path(__file__).parent / "sample_media" / "audio" # scale -> (sourced raw file, production output file), both under audio//. # Sources per docs/audio-candidate-pool.md (the "✓ KEEP" picks). +# +# The `music` entry is the Audio-dropdown "Music" layer (docs/audio-candidate-pool.md +# § Music): ONE ethereal ambient loop played the same at every altitude — not a +# per-altitude soundtrack. It lands at audio/music/ethereal.loop.mp3, served at +# /media/audio/music/ethereal.loop.mp3 (client fallback: app.js MUSIC_FALLBACK). SOURCES: dict[str, tuple[str, str]] = { "cosmos": ("pillars.mp3", "pillars.loop.mp3"), "orbit": ("spaceamb.mp3", "spaceamb.loop.mp3"), "coast": ("waves.mp3", "waves.loop.mp3"), "reef": ("soundscape.mp3", "soundscape.loop.mp3"), "abyss": ("whale.wav", "whale.loop.mp3"), + "music": ("ethereal.mp3", "ethereal.loop.mp3"), } diff --git a/simulator/e2e/tests/audio-source.spec.ts b/simulator/e2e/tests/audio-source.spec.ts new file mode 100644 index 0000000..9df942b --- /dev/null +++ b/simulator/e2e/tests/audio-source.spec.ts @@ -0,0 +1,73 @@ +import { test, expect, Page } from "@playwright/test"; + +// The Audio-source feature (docs/superpowers/specs/2026-07-01-audio-music-option-design.md): +// the old 0–10 "Audio" slider becomes "Volume", and a new Audio 2/10 @@ -82,7 +82,14 @@ Video -