Normalize Module — Post-Render Peak/RMS Normalisation

A shared builtin module that applies peak or RMS normalisation to rendered audio during WAV export. Runs post-render in the renderToBuffer() Promise callback, operating directly on the final Float32Array of samples.

Used by all six synth engines.

Exports

Export Signature Purpose
paramDefs {} 5 parameter definitions (normalize, normTarget, normMode, normCeiling, normDC)
createNormalizeModule () → {id, type, params, state, apply} Creates a module object with shared apply logic and a state object for the meter
startNormMeter (state) → undefined Finds the normalize card, builds pre/after/gain meter UI, starts a RAF loop reading from state

Params

Param Type/Range Default Description
normalize checkbox true Enable normalisation
normTarget –12–0 dB −1 Target peak or RMS level
normMode peak/rms rms Peak: loudest sample hits target. RMS: matches perceived loudness.
normCeiling checkbox false Hard-clip any sample exceeding target after normalisation
normDC checkbox false Remove DC offset before normalising

Apply Logic

The apply function is called by engine.js's renderToBufferInternal for every builtin module after the OfflineAudioContext render completes:

engine.modules.forEach(mod => {
  if (mod.type === 'builtin' && mod.apply) {
    mod.apply(samples, params);
  }
});

Processing order: 1. Set target from params.normTarget (fallback –1 dB) 2. If normalize is off, set state.disabled = true and return 3. Optional DC removal: subtract the sample mean 4. RMS or peak mode: compute pre-level, apply gain, record gain value 5. Optional ceiling: hard-clamp to ±targetLin

Meter

startNormMeter locates the normalize section card (by [data-key="normTarget"]), appends a meter row showing pre-level, target level, and applied gain in dB. It reads from the shared state object written by apply:

Pre:  ████████░░  –18.3 dB
After:████████████ –1.0 dB
Gain: +17.3 dB

The meter shows "Off:" with dashed values when normalisation is disabled, and "..." before the first render completes.

Integration

import { startNormMeter } from './modules/normalize.js';
startNormMeter(SynthX.modules.find(m => m.id === 'normalize').state);

Files

File Purpose
modules/normalize.js Module definition
All 6 engines/*.js Each replaces its inline normalise with createNormalizeModule()
All 6 init-*.js Each calls startNormMeter() after initSynth