# Listening room

From the project root, with MoonBit and Node.js available:

```sh
node playground/build.mjs
node playground/serve.mjs
```

Open http://127.0.0.1:8080. Select or drop a native FLAC / Ogg FLAC file, inspect its sample format and integrity, play/pause/seek, and export integer WAV.

`browser/bridge.mbt` calls the pure MoonBit decoder. Select JavaScript or WebAssembly GC to run the corresponding compiled artifact in a dedicated Worker. Changing the selection re-decodes the same file. Both backends share the MoonBit implementation; the wasm host imports only transfer bytes and numbers. Web Audio receives Float32 PCM and never receives the compressed input. Timings include input/PCM boundary transfer and separately report backend loading/compilation. A browser without WebAssembly GC receives an explicit runtime error; there is no silent backend fallback. Audio device playback may resample and Float32 cannot preserve all 32-bit integer precision; exported WAV retains integer precision.

This interactive whole-file preview caps compressed input at 64 MiB and output at 32 million channel samples. It retains PCM for playback and export. Selecting another file terminates the previous worker, including an ongoing decode. Use the library streaming API/CLI for recordings beyond this preview's limits. Browser AudioBuffer sample-rate support varies; export still works if playback is unsupported.

No npm dependencies, remote services, or asset requests are needed.

Same-format chained Ogg streams with the same effective speaker mask are joined in order. An absent mask and an explicit FLAC-default mask are compatible. Streams whose sample rate, bit depth, channel count, or effective speaker layout changes are rejected by this single-buffer preview with an explicit diagnostic; the library frame API can consume each link. Metadata shown is from the first link. WAV serialization uses the library's `wav_header` and `frame_pcm`, preserving an explicit `WAVEFORMATEXTENSIBLE_CHANNEL_MASK` including zero, with extensible headers for nonstandard precision/multichannel PCM. Export re-decodes in the worker on demand to avoid retaining a second integer PCM copy.

Playback follows the [W3C Web Audio speaker model](https://www.w3.org/TR/webaudio/#channel-up-mixing-and-down-mixing), sections 4.1–4.5: mono, stereo, quad with back left/right, and 5.1 with front left/right/center, LFE, back left/right. Named subsets are placed into those canonical positions; any unnamed remaining PCM channels are silent during playback and retained in export. The UI states this before playback. An explicit zero mask has no known speaker positions and disables playback. Side/height/back-center layouts (including side 5.1 `0x060f` and default FLAC 7/8-channel layouts) cannot be represented by these canonical positions and also disable playback, while WAV export remains available. Side speakers are never relabeled as back speakers. The audio device can downmix the canonical buffer; Float32 playback is a preview, not integer verification.

Optional real-browser smoke verification (requires Playwright and Chromium already installed):

```sh
node playground/smoke.mjs "validation/cache/flac-test-files/subset/60 - mono audio.flac"
node playground/smoke.mjs "validation/cache/flac-test-files/subset/60 - mono audio.flac" wasm-gc
node playground/responsiveness.mjs
node playground/responsiveness.mjs "validation/cache/flac-test-files/subset/34 - samplerate 192kHz, using only 32nd order predictors.flac" wasm-gc
node playground/test-layout.mjs
```

The smoke script exercises file input, actual AudioContext playback/pause, seek/reset, WAV download, backend switching with byte-identical WAV assertion, drag/drop, malformed input, and mobile layout. It checks that the selected backend's actual artifact was requested, prohibits `decodeAudioData`, and writes screenshots plus reports with separate `js`/`wasm-gc` filenames under `validation/results/browser/`. Decoder timings are measured locally and are not cross-device performance guarantees.

The layout regression uses the independent libFLAC fixtures generated by `python cmd/test_cli.py` under `validation/cache/review-layout/`. It runs both artifacts, verifies WAV masks and PCM, checks playback availability/partial-channel notices, compatible/incompatible Ogg chains, and renders isolated impulses through `OfflineAudioContext` to assert actual channel routing and silence for unnamed channels. Evidence is written to `validation/results/review-browser/`. libFLAC 1.5 itself replaces explicit zero with its default mask when exporting WAV, so that case checks the original explicit-zero tag contract while comparing its PCM against libFLAC.
