Meter each track and the song, and limit the output at -1 dBFS (#359)
game-maker / gate (push) Successful in 3m43s
game-maker / suite (push) Failing after 25m11s

A song could clip at the output without anyone noticing: a track and a
sound item at +12 dB each add up to +24 dB. The Song tab now shows how
loud each track and the whole song are, and a fixed limiter behind the
master keeps every sample under -1 dBFS in the editor and in the game.

- Each track head has a stereo meter up its right edge. The head is
  190 px wide instead of 176.
- Out sits in the corner above the heads, with the loudest peak since
  Play in dB. In the tab header it made the row too wide for a 1280 px
  window, and the inspector lost its fields.
- The meters read AnalyserNodes beside the buses, one per side after a
  channel splitter, so a peak panned hard is not halved. They are tapped
  only while the song plays and its tab is shown.
- A clip lamp lights past 0 dBFS and stays through Stop. The next Play
  or a click on the meter puts it out.
- The meter of a track that mute or solo keeps from being heard is grey.
- Music always plays through a bus of its own, with or without a sound
  FX. That bus is what Out measures.
- The limiter is the one from #326 and needs the FX worklet. Until that
  is loaded the master goes straight out.
- It is 5 ms late. latency() counts that in, so the playhead and the
  alignment of a recorded take follow it.
- Unit tests build SoundLibrary without the limiter, as their wiring
  checks follow every node of a voice.
- Out shows "-inf" while nothing was loud. The UI font has no infinity
  sign, which the font test found.

Tests:
- Unit meter-view 8 new, sound-library 112 (2 new), 3 counter-tests
  each red
- e2e 65 (melektaus-atx): ALL PASSED, 55 checks (3 new). Out wrote
  +16.6 dB, behind the limiter the loudest sample was 0.8913
- Gate (melektaus-atx): 272 files, 4209 tests, one red in the font
  test. Fixed after it, then the font and meter tests ran green.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
adminandClaude Opus 5.5 committed 2026-10-08 22:08:39 +02:00
1 parent dc666016db
commit 490fcba88f
12 files changed
+810 -137

No files matched your search

+14
View File
@@ -44,6 +44,20 @@ export function songHooks({ state, editor }: HookContext) {
clock: () => soundLibrary.clock(),
stats: () => soundLibrary.stats(),
buses: () => soundLibrary.busLevels(),
/** The level meters (#359): each track's by its id and Out, in dB, and how many buses are tapped. */
meters: () => {
const asset = findAssetOf(state.project, songEditor.currentAssetId, 'song');
return {
tracks: Object.fromEntries((asset?.tracks ?? []).map((t) => [t.id, timeline.meterOf(t.id)])),
out: timeline.outShown(),
metered: soundLibrary.metered(),
};
},
/** The loudest sample on each side behind the limiter at the output, null without it. */
guard: () => soundLibrary.guardPeaks(),
latency: () => soundLibrary.latency(),
meterAt: (track: number) => timeline.meterPoint(track),
outAt: () => timeline.outPoint(),
/** Timeline or Routing (#350), and where their switches are. */
shown: () => songEditor.view,
viewAt: (view: 'timeline' | 'routing') =>