BWF Analyser: browser page and macOS app
Reads and edits BWF metadata for production sound. One source tree builds a single self-contained page and a native Tauri app with a Rust audio engine and WAV writer. Around 370 checks across seven test suites. First commit of the existing state, so that from here every change can be seen and undone.
This commit is contained in:
@@ -0,0 +1,120 @@
|
||||
# BWF Analyser
|
||||
|
||||
Reads and edits BWF (Broadcast Wave) metadata for production sound: scene,
|
||||
take, timecode, track names, plus export, split, combine and a sound report.
|
||||
Two things get built from one source tree:
|
||||
|
||||
- `index.html` at the root: a single self-contained page, no server, no
|
||||
network, opens in a browser.
|
||||
- `mac-app/`: a native macOS app (Tauri v2 + Rust) that wraps the same page
|
||||
and adds real file access, a Rust audio engine and a Rust WAV writer.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
python3 build/build.py # writes ./index.html
|
||||
python3 build/build.py --tauri # writes ./mac-app/dist/index.html
|
||||
```
|
||||
|
||||
Both, every time: a change to shared code affects both pages, and a feature
|
||||
wired into only one looks exactly like a broken feature (a visible button that
|
||||
does nothing). There is a test for that specific trap.
|
||||
|
||||
The Mac app is built by double-clicking `mac-app/Build BWF Analyser.command`.
|
||||
That script regenerates the page, touches `main.rs` so the page is re-embedded
|
||||
(cargo does not reliably notice a changed `dist/`), compiles, assembles and
|
||||
signs the bundle.
|
||||
|
||||
## Test
|
||||
|
||||
```bash
|
||||
node build/test.js # the browser page, in jsdom
|
||||
node build/test-tauri.js # the Mac page against a stubbed engine
|
||||
node build/test-export.js # every export/split/combine/format combination
|
||||
node build/test-play.js # playback maths, peaks, spectrogram, meters
|
||||
node build/test-restore.js build/test-framerate.js build/test-pdf.js
|
||||
BWF_TINY_CHUNKS=1 node build/test-tauri.js # tiny-read paths
|
||||
```
|
||||
|
||||
All of them should pass before anything ships. Roughly 370 checks.
|
||||
|
||||
## How the build works, and its traps
|
||||
|
||||
`build/build.py` inlines CSS and JS into one HTML file. The app's JavaScript
|
||||
(`build/src/bwf-analyser-app.js`) is vendored from a WordPress plugin and is
|
||||
kept pristine: changes to it are made as **exact-string patches** in lists in
|
||||
`build.py` (`SHARED_APP_JS_PATCHES`, `APP_JS_PATCHES`, `PLAYER_PATCHES`,
|
||||
`SPECTRO_PATCHES`, `MIXER_PATCHES`). Each patch asserts its anchor is found
|
||||
exactly once, so a stale anchor fails the build loudly rather than silently
|
||||
doing nothing.
|
||||
|
||||
- Patch strings are Python **non-raw** triple-quoted strings, so tabs in the
|
||||
JavaScript are written `\t`. Get this wrong and the anchor never matches.
|
||||
- `SHARED_*` patches apply to both builds; the others only to `--tauri`.
|
||||
- When inserting into the page, use the **last** `</body>`, not the first: the
|
||||
page contains inlined JavaScript that writes HTML, and the first match is
|
||||
inside a string literal.
|
||||
|
||||
## The Rust side
|
||||
|
||||
`mac-app/src-tauri/src/`:
|
||||
|
||||
- `main.rs` — the commands the page calls (`bwf_scan`, `bwf_export`,
|
||||
`bwf_combine`, `bwf_peaks`, `bwf_spectrogram`, `bwf_play`, `bwf_gains`, …).
|
||||
- `convert.rs` — RIFF/BWF parsing and writing, peaks, spectrogram, FFT.
|
||||
- `play.rs` — the audio engine. cpal, one engine thread owning the stream
|
||||
(`cpal::Stream` is `!Send` on macOS), a reader thread streaming blocks over
|
||||
bounded channels, per-channel gains and meters as atomics read in the
|
||||
callback. Playback deliberately does **not** use Web Audio: the webview's
|
||||
audio would die after a while and no JavaScript could recover it.
|
||||
|
||||
`build/wav-convert.js` is a line-for-line **Node mirror** of the Rust
|
||||
converter, and the tests drive both. Anything added to the Rust side that has
|
||||
maths in it should be mirrored and tested, including the rounding: Rust's
|
||||
`f64::round` and JavaScript's `Math.round` disagree on negative ties.
|
||||
|
||||
## Conventions worth keeping
|
||||
|
||||
**Mutation-test every new test.** Break the thing on purpose, confirm the test
|
||||
fails with a message that names the problem, put it back. Several tests in
|
||||
this suite passed for the wrong reason until this caught them.
|
||||
|
||||
**jsdom has no layout engine.** Nothing here can see that a button has no hit
|
||||
area, that a grid is scrambled, or that an element collapsed to zero width.
|
||||
Every one of those has shipped at least once. Layout changes need a screenshot
|
||||
from the user; keep them small and ask.
|
||||
|
||||
**Two pages, one behaviour.** If a feature only makes sense on the Mac build,
|
||||
it still needs to not look broken on the other one.
|
||||
|
||||
**The page stamps itself.** `window.BWFA_BUILD` holds the build time, also on
|
||||
`data-bwfa-build` on the app root. Use it to settle "is the app running the
|
||||
page I just built" rather than guessing.
|
||||
|
||||
**There is an in-app log.** Cmd-Shift-L opens it (`build/diagnostics.js`): it
|
||||
captures console output and errors from before the app starts, traces clicks
|
||||
with what was actually hit, and measures the controls' boxes against
|
||||
`elementFromPoint`. This is the fastest route to a diagnosis when a control
|
||||
looks fine and does nothing. Devtools are compiled in too (right-click →
|
||||
Inspect Element).
|
||||
|
||||
**Text lives in `build/l10n.json`**, including the hover hints, keyed by data
|
||||
attribute.
|
||||
|
||||
## Where things are
|
||||
|
||||
```
|
||||
build/
|
||||
build.py assembles both pages, holds all the patches
|
||||
body.html markup
|
||||
overrides.css the app's own styling on top of the plugin's
|
||||
l10n.json every string, plus hints
|
||||
diagnostics.js the in-app log
|
||||
src/ the vendored plugin (JS + CSS), kept pristine
|
||||
wav-convert.js Node mirror of the Rust converter, for tests
|
||||
test*.js the suites
|
||||
mac-app/
|
||||
src-tauri/src/ the Rust
|
||||
dist/index.html generated
|
||||
Build BWF Analyser.command
|
||||
```
|
||||
Reference in New Issue
Block a user