-
feat(cosmicfest): two tools for a gig with no stems — separation as a lens, and a bus-only master · b8059f81
Both exist because CosmicFest is one stage-mic MP3, and the house pipeline assumes twelve Ardour orbit-stems. demucs_sections.py — per-track stems plus a global pass, PLN's ask: "to see how both are interpreted locally and globally". One measured fact decides what that comparison can show. htdemucs' receptive field is 7.8 s (model.segment = 39/5) and it always splits internally, so a 63-minute file gives the model no more musical context than a 4-minute one. The local/global difference is therefore NOT context — it is input normalisation, since demucs normalises by the mean/std of whatever you hand it. On this gig that is a real lever rather than a nitpick: the set sits at -25.2 LUFS with 11.1 LU of range, so quiet sections get far less gain under whole-set statistics than under their own. So global mode computes the normalisation over the entire recording and then applies the model in bounded chunks with those statistics. That isolates the one variable that can actually differ, in about 1 GB instead of the ~12 GB a single 63-minute tensor needs (four sources x 1.33 GB of output, plus apply_model's overlap accumulator, against 12 GB free). Chunks overlap and are crossfaded with an equal-power ramp — the seam is two estimates of the same audio summed, so a linear fade would dip 3 dB in the middle of it. Written against demucs' Python API rather than its CLI, because the CLI cannot save here at all: torchaudio 2.10 routes writes through TorchCodec, which is not installed, so `python -m demucs` separates for 87 seconds and then dies on ImportError. Going through apply_model and soundfile also avoids installing anything into PLN's venv to work around it. Validated before launching a six-hour job: 30 s slice, four stems written, all populated, and sum-minus-original sits 27.4 dB below the signal. Vocals came back at -50.6 dBFS RMS on an instrumental passage — correctly near-empty, which is the kind of agreement that makes later disagreements worth trusting. Runs as a systemd --user unit per reference_durable_background_jobs, niced with idle IO so it can never be why the rig stutters. master_stemless.py — MASTERING.md's chain with the per-stem half removed and one stage added, because the measurement demanded it. Full-file analysis first: L/R correlation 0.833 (genuinely stereo, so bass-mono is safe), sub-30 Hz is 0.07% of total energy (the HPF is free headroom), and then the finding that reshapes the job — 300-1000 Hz carries 34.75% while 4-8 kHz has 1.75% and 8-16 kHz has 0.12%. That is a microphone in a room, not a desk feed: the PA's treble never reaches the mic, the room absorbs what does, mid-bass piles up. The tape is dark and boxy, not merely quiet, so the dominant move is a broadband tilt rather than gain. Restraint where the numbers cannot justify enthusiasm: the air shelf is +5, not the +12 the deficit suggests, because a 320 k MP3 puts codec residue in that band alongside cymbals and a big boost lifts hiss into the master. The demucs stems are the lens that can tell those apart; until they answer, the shelf stays modest. Bass-mono runs in mid/side, not by splitting and re-summing bands: a lowpass(120) summed with a highpass(120) leaves a phase notch at the crossover, whereas high-passing the side channel alone never filters the mid path. Two failures worth keeping. First, the club target with the limiter placed before any gain: the signal reaching it still peaked at -9.5 dBTP, so a -1 dBFS ceiling never engaged, and loudnorm's linear=true — correctly refusing to breach TP — capped the master at -11.2 LUFS against a -9 target. The gain has to come first so the limiter has something to catch. Second, that fix alone still undershot, because every dB the limiter absorbs is a dB the final loudnorm cannot add; the shortfall IS the missing gain, so feeding it back converges. Bounded at three attempts and guarded by an LRA floor, since a loudness target reached by flattening the music is not reached — presence_is_a_precondition, applied to dynamics. On the 90 s trial the streaming target lands at -14.0 LUFS, peak -1.0, with 1.8 LU of range lost. The club target stops at -10.5 with the floor tripped, and reports the miss instead of clipping its way to a number. Whether -9 is reachable on the full set, whose LRA is 11.1 rather than the slice's 4.6, is a question the full render answers.
PLN (Algolia) authoredb8059f81
| Name |
Last commit
|
Last update |
|---|---|---|
| .. | ||
| api | Loading commit data... | |
| escales | Loading commit data... | |
| manifeste | Loading commit data... | |
| semaphore | Loading commit data... | |
| tasks | Loading commit data... | |
| tide-table | Loading commit data... | |
| ui | Loading commit data... | |
| .gitignore | Loading commit data... | |
| DESIGN.md | Loading commit data... | |
| PRODUCT.md | Loading commit data... | |
| README.md | Loading commit data... | |
| ardour_faders.json | Loading commit data... | |
| serve.py | Loading commit data... | |
| setlist_opal2026.txt | Loading commit data... |