Mastering Processors
This page is the registry for the named mastering API in libsonare. It answers "what can I call?", not "how does it work internally?"
The authoritative runtime source is the name-list API: masteringProcessorNames(), masteringPairProcessorNames(), masteringPairAnalysisNames(), masteringStereoAnalysisNames(), and masteringPresetNames(). This page mirrors those lists.
New to mastering? Don't start here
Calling individual processors one by one is the hard way. Start with a preset (masterAudio) or the Mastering Assistant, which profiles your audio and proposes a whole chain. Reach for solo processors only when you need surgical control over one stage.
For behavior, processing boundaries, and real-time notes by DSP family, see DSP Implementation Notes. For standards and paper citations, see Algorithm References. For test coverage, see Implementation Validation.
What You Will Learn
By the end of this page you should be able to:
- distinguish presets, solo processors, pair processors, and JSON-returning analyses;
- start from a goal such as "control dynamics" or "match a reference" rather than scanning IDs alphabetically;
- know when a preset or assistant flow is more appropriate than directly calling a processor;
- find the exact registry name to pass to JavaScript, Python, Node native, or the C ABI.
What the names mean
| Name type | Meaning | Example |
|---|---|---|
| Preset | A named chain configuration for a style or delivery target | streaming, podcast, jpop |
| Solo processor | One processor applied to a mono or stereo signal | dynamics.compressor, eq.tilt |
| Pair processor | A processor that uses a source and a reference signal | match.applyMatchEq |
| Analysis | A measurement that returns JSON instead of audio | match.referenceLoudness, stereo.monoCompatCheck |
Sidechain and loudness processors
The dynamics family includes dynamics.duckingProcessor (sidechain ducking), maximizer.loudnessOptimize (maximizing toward a LUFS target — LUFS is Loudness Units relative to Full Scale, the broadcast loudness scale), and a de-esser bandpass Q control on dynamics.deesser with stereo preservation, alongside dynamics.transientShaper, dynamics.upwardCompressor, dynamics.upwardExpander, dynamics.vocalRider, and dynamics.sidechainRouter.
Presets
Presets are named chain configurations, not separate algorithms. Apply one with masterAudio(samples, sr, preset, overrides?).
pop, edm, acoustic, hipHop, aiMusic, speech, streaming, youtube, broadcast, podcast, audiobook, cinema, jpop, ambient, lofi, classical, drumAndBass, techno, metal, trap, rnb, jazz, kpop, trance, gameOst
See Choosing a Mastering Preset for how to pick one without treating a preset as a finished master.
Which processor for which job
A goal-first index into the registry below. This is a starting point, not a rule — read the linked guides before committing.
| You want to… | Reach for | Learn the concept |
|---|---|---|
| Even out level / control dynamics | dynamics.compressor, dynamics.limiter, multiband.compressor | Dynamics |
| Add punch without squashing | dynamics.transientShaper, dynamics.parallelComp | Dynamics |
| Tame harsh "ess" sounds | dynamics.deesser | Dynamics |
| Duck a music bed under voice | dynamics.duckingProcessor, dynamics.sidechainRouter | Mixing Engine |
| Shape overall tone / brightness | eq.tilt, eq.parametric, spectral.airBand | Tone and Air |
| Add warmth / harmonics | saturation.tape, saturation.tube, saturation.exciter | Tone and Air |
| Widen / narrow / check stereo | stereo.imager, stereo.monoMaker, stereo.monoCompatCheck | Stereo, Limiter, Loudness |
| Hit a loudness target safely | loudness stage, maximizer.loudnessOptimize, maximizer.truePeakLimiter | Delivery Targets |
| Clean up noise / clicks / clipping | repair.denoiseClassical, repair.declick, repair.declip | Repair and Input |
| Match a reference track | match.applyMatchEq, match.referenceLoudness | Reference Match |
What is sidechaining / ducking?
Sidechaining lets one signal control a processor applied to another. The most common use is ducking: a music bed is automatically turned down whenever a voice is present, then comes back up in the gaps — the way background music drops under a narrator.
What is parallel compression?
A normal compressor turns the loud parts down.
Parallel compression mixes the original signal with a heavily compressed copy. The compressed copy lifts quiet detail, while the original keeps the natural peaks.
Use it when you want density and "glue" without flattening transients. It is also called New York compression. dynamics.transientShaper is the related tool for the opposite goal: exaggerating or softening the attack of each hit.
Drag the threshold and ratio to watch the transfer curve bend and hear a dynamics.compressor work on a live signal:
Processor Families In Plain English
The exact IDs matter for code, but users usually choose by role:
| Family | Use it when | Avoid it when |
|---|---|---|
| Dynamics | The level envelope is the problem: peaks jump out, vocals are uneven, transients need shaping, or a bed must duck under speech | The problem is tonal balance; EQ or spectral processors are clearer |
| EQ | The frequency balance is wrong: too dark, too harsh, too boomy, or needs a surgical cut | You are trying to increase loudness; use dynamics/maximizer stages |
| Multiband | Different frequency ranges need different dynamics or width treatment | A broad single-band processor already solves it; multiband can overfit quickly |
| Saturation | You want harmonic density, edge, warmth, or controlled clipping character | You need clean correction; saturation adds coloration by design |
| Spectral | The issue is perceptual tone shaping: air, presence, low-end focus, broad spectral contour | You need exact filter moves; use EQ |
| Stereo | Width, mono compatibility, phase, or left/right balance is the problem | The mix is already phase-sensitive or mono delivery is primary |
| Maximizer / final | You are at the delivery stage: loudness, ceiling, bit depth, or final output polish | You are still fixing balance or arrangement problems |
| Repair | The input has defects: clicks, crackle, hum, clipping, noise, excessive tail | You expect source separation or neural restoration |
Most full chains use only a small subset: repair if needed, one tone stage, one dynamics stage, optional saturation/stereo, then maximizer/loudness. Stacking many processors from the registry is rarely better than starting from a preset and overriding one or two values.
Loudness, oversampling, and metering details
A few capabilities sit underneath the maximizer/final and analysis APIs:
- Integrated LUFS measurement supports surround layouts up to 8 channels, applying the BS.1770 channel weights. BS.1770-4 itself normatively defines layouts only up to 5.1 (6 channels); the 7.1/8-channel weighting (treating the side-surround pair like the rear surrounds, +1.5 dB) is a non-normative extrapolation, not part of the standard.
- The internal oversampler and true-peak stages accept power-of-two oversampling factors from 1 to 16 (1, 2, 4, 8, 16; the live meter accepts the same factors), trading CPU for inter-sample-peak accuracy.
- For UI metering, pass
maxPointstometeringVectorscope(...)andmeteringPhaseScope(...): they thin the point series down to at mostmaxPointspoints, so a busy scope stays cheap to draw. (WithoutmaxPointsthey emit one point per input sample. The oldermeteringVectorscopeDecimated(...)/meteringPhaseScopeDecimated(...)aliases are deprecated and just delegate.)meteringSpectrumFrame(...)reads a single, non-time-averaged spectrum frame for spectrum-analyzer snapshots. - Every
multiband.*solo processor —compressor,dynamicEq,expander,imager,limiter, andsaturation— shares the same crossover mechanism and accepts a custom number of crossover cutoffs, so you can split into the band count your material needs instead of a fixed three. This entry point exposes up to 8cutoffNHzslots (cutoff0Hz…cutoff7Hz), so a singlemultiband.*call can address up to 9 bands.
What is a crossover?
A crossover splits the signal into frequency bands (e.g. lows / mids / highs) so each band can be processed separately. The "crossover cutoffs" are the frequencies where one band ends and the next begins; more cutoffs means more bands and finer control.
Solo processors
| Family | Processor names |
|---|---|
| Dynamics | dynamics.brickwallLimiter, dynamics.compressor, dynamics.deesser, dynamics.expander, dynamics.gate, dynamics.limiter, dynamics.parallelComp, dynamics.sidechainRouter, dynamics.duckingProcessor, dynamics.transientShaper, dynamics.upwardCompressor, dynamics.upwardExpander, dynamics.vocalRider |
| EQ | eq.apiStyle, eq.bandPass, eq.cutFilter, eq.dynamic, eq.equalizer, eq.graphic, eq.linearPhase, eq.midSide, eq.minimumPhase, eq.parametric, eq.pultec, eq.shelving, eq.tilt |
| Final | final.bitDepth, final.dither, final.outputChain |
| Maximizer | maximizer.adaptiveRelease, maximizer.loudnessOptimize, maximizer.maximizer, maximizer.softKneeMax, maximizer.truePeakLimiter |
| Multiband | multiband.compressor, multiband.dynamicEq, multiband.expander, multiband.imager, multiband.limiter, multiband.saturation |
| Repair | repair.declick, repair.declip, repair.decrackle, repair.dehum, repair.denoiseClassical, repair.dereverbClassical, repair.trimSilence |
| Saturation | saturation.ampSim, saturation.bitcrusher, saturation.exciter, saturation.hardClipper, saturation.multibandExciter, saturation.softClipper, saturation.tape, saturation.transformer, saturation.tube, saturation.waveshaper |
| Spectral | spectral.airBand, spectral.lowEndFocus, spectral.presenceEnhancer, spectral.spectralShaper |
| Stereo | stereo.autoPan, stereo.haasEnhancer, stereo.imager, stereo.monoMaker, stereo.phaseAlign, stereo.stereoBalance |
Stereo-family processors use a different entry point
Most processors run through the single-array masteringProcess() (mono, or interleaved). The stereo-family processors (stereo.imager, stereo.monoMaker, stereo.autoPan, stereo.haasEnhancer, stereo.phaseAlign, stereo.stereoBalance) operate on true left/right channels, so call them through the separate stereo entry point masteringProcessStereo() / mastering_process_stereo(), which takes distinct left and right arrays. stereo.monoMaker uses frequencyHz as the crossover below which it collapses the signal toward mono; amount controls how strongly it does so. The same is true of eq.midSide and the multiband.* processors. Passing these to masteringProcess() cannot express independent channels — see How to call them for the exact signatures.
What is dither?
When you reduce bit depth (e.g. 24-bit down to 16-bit for CD/streaming), rounding creates a faint distortion on quiet tails. Dither adds a tiny, carefully shaped noise that masks that distortion so fades sound smooth instead of grainy. Apply it once, last, at the final bit-depth reduction.
Repair is classical DSP, by design
repair.denoiseClassical, repair.dereverbClassical, and related processors use spectral subtraction / MMSE-STSA / LogMMSE with explicit noise estimation.
They are not DNN source separation or neural spectral repair.
- Good for: noise, hum, clicks, clipping, and mild room smear.
- Not for: unmixing finished tracks or rebuilding missing sources.
- Design reason: the repair path stays deterministic and dependency-free.
Registry names and chain keys differ
The named processor registry exposes one-shot repair processors as repair.denoiseClassical and repair.dereverbClassical.
Full-chain configs use shorter stage keys: repair.denoise.* and repair.dereverb.*. Those keys address the repair slots inside MasteringChainConfig.
Both naming styles point to the same classical denoise/dereverb implementations.
What is spectral subtraction (MMSE-STSA / LogMMSE)?
These are classical denoising methods.
- The algorithm estimates a noise profile from quiet passages, such as steady hiss or hum.
- Spectral subtraction subtracts that estimated noise from each short-time spectrum frame.
- MMSE-STSA and LogMMSE are statistical versions that estimate how much of each frequency bin is signal versus noise before subtracting.
This reduces the warbly "musical noise" that naive subtraction can leave. These methods do not separate instruments; they only attenuate noise.
What is saturation.ampSim?
A guitar/bass-amp-style coloration stage in the form preamp drive → tone stack → power amp → cabinet. An oversampled 12AX7 triode drive stage sits behind a single [0, 1] drive knob, with a drive-scaled pre-emphasis shelf so the gain character shifts as you push it. After the drive comes a bass/mid/treble tone stack, then an optional power-amp section and a data-free cab voicing. Construction/param keys: drive (0-1), bassDb, midDb, trebleDb, presenceDb, levelDb, power, sag, transformer, and nfb are automatable through set_parameter on every binding. power adds a class-AB push-pull soft-saturation stage; sag models supply droop and bloom after hard hits; transformer adds low-frequency output-transformer saturation; nfb adds a negative-feedback loop around the active power stage. cab (boolean), cabModel (0 = guitar 4x12, 1 = bass 8x10), and ampModel (0 = classic crunch, 1 = Fender-style clean, 2 = modern high-gain, 3 = tweed, 4 = Vox-style chime, 5 = rectifier) are discrete topology choices, so set them at construction time rather than automating them.
Pair processors and analyses
Pair processors consume a source and a reference. Pair/stereo analyses return measurement JSON and do not render audio by themselves.
| Type | Names |
|---|---|
| Pair processors | match.applyMatchEq, match.alignReferenceToSource, match.abSwitch, match.abCrossfade |
| Pair analyses | match.referenceLoudness, match.tonalBalance, match.tonalBalanceLogBands, match.matchEqCurve, match.estimateReferenceDelaySamples |
| Stereo analyses | stereo.monoCompatCheck, stereo.monoCompatCheckLogBands |
These are registry names you pass to masteringPairAnalyze(...) / masteringStereoAnalyze(...). Separately from the registry, the assistant helpers have their own stereo entry points that take a left/right pair directly — masteringAudioProfileStereo, masteringAssistantSuggestStereo, and masteringStreamingPreviewStereo. Use them instead of profiling a downmix, which under-reports integrated loudness by about 6 dB on decorrelated material; see Stereo sources.
What do "tonal balance" and "mono compatibility" measure?
- Tonal balance (
match.tonalBalance) describes how a track's energy is spread across frequency bands — how much sub, bass, mid, presence, and air it has. Comparing your tonal balance to a reference track shows where you are darker or brighter, which is whatmatch.applyMatchEqthen corrects. - Mono compatibility (
stereo.monoCompatCheck) predicts what happens when your stereo mix is summed to mono (phone speakers, club PAs, some broadcast paths). If the left and right channels are out of phase, parts can cancel out and lose level when folded down. The check flags that risk before it surprises a listener. See Mono Compatibility for a deeper walk-through.
Mixer and engine inserts
The creative-FX insert catalog — reverb, modulation, and delay insert IDs, their parameter tables, the masteringInsertNames() discovery APIs, and SONARE_HAVE_FX / BUILD_ACOUSTIC_SIM build gating — lives on its own page: Effects Inserts.
How to call them
Use capabilityCatalog() / capability_catalog() when a host needs one build-aware picker across solo, pair, and creative-insert processors. It lists each processor's parameter descriptors — name, id, type, unit and realtime-safety — plus the built-in preset lists. Its min / max / default fields are always null, so it can populate a picker but not size a control; take value ranges from the per-processor tables on this page. masteringProcessorCatalog() is the narrower mastering registry classification used for mastering-specific pickers.
const build = capabilityCatalog();
console.log(build.processors.length, build.presets.mastering);
masteringProcessorNames(); // discover solo processor ids at runtime
masteringProcessorCatalog(); // classify processors for picker/filter UIs
masteringInsertParamInfo('eq.parametric'); // realtime automation metadata
const out = masteringProcess('dynamics.compressor', samples, sampleRate, {
thresholdDb: -24,
ratio: 1.5,
});
const stereo = masteringProcessStereo('stereo.imager', left, right, sampleRate, { width: 1.1 });
// Analyses return JSON strings — parse them
const report = JSON.parse(masteringPairAnalyze('match.referenceLoudness', source, reference, sampleRate));
const mono = JSON.parse(masteringStereoAnalyze('stereo.monoCompatCheck', left, right, sampleRate));import {
capabilityCatalog,
masteringInsertParamInfo,
masteringPairAnalyze,
masteringProcess,
masteringProcessStereo,
masteringProcessorCatalog,
masteringProcessorNames,
masteringStereoAnalyze,
} from '@libraz/libsonare-native';
const build = capabilityCatalog();
console.log(build.processors.length, build.presets.mastering);
masteringProcessorNames();
masteringProcessorCatalog();
masteringInsertParamInfo('eq.parametric');
const out = masteringProcess('dynamics.compressor', samples, sampleRate, {
thresholdDb: -24,
ratio: 1.5,
});
const stereo = masteringProcessStereo('stereo.imager', left, right, sampleRate, { width: 1.1 });
const report = JSON.parse(masteringPairAnalyze('match.referenceLoudness', source, reference, sampleRate));
const mono = JSON.parse(masteringStereoAnalyze('stereo.monoCompatCheck', left, right, sampleRate));import json
import libsonare as sonare
build = sonare.capability_catalog()
print(len(build["processors"]), build["presets"]["mastering"])
sonare.mastering_processor_names() # discover solo processor ids at runtime
out = sonare.mastering_process('dynamics.compressor', samples, sample_rate=sr, params={
'thresholdDb': -24,
'ratio': 1.5,
})
stereo = sonare.mastering_process_stereo('stereo.imager', left, right, sample_rate=sr, params={'width': 1.1})
# Analyses return JSON strings — parse them
report = json.loads(sonare.mastering_pair_analyze('match.referenceLoudness', source, reference, sample_rate=sr))
mono = json.loads(sonare.mastering_stereo_analyze('stereo.monoCompatCheck', left, right, sample_rate=sr))# inspect this build and its capability-catalog summary
sonare doctor --json
# discover solo processor ids
sonare mastering-processors
# apply one solo processor (--params are floats: k=v,k=v)
sonare mastering-processor song.wav --processor dynamics.compressor \
--params "thresholdDb=-24,ratio=1.5" -o out.wav
# two-input (pair) analysis prints JSON
sonare mastering-pair-analyze song.wav --reference ref.wav --analysis match.referenceLoudness
# The Python CLI has no dedicated mastering-stereo-analyze subcommand; only
# source-built C++ CLI builds expose the two-channel stereo analyses.
# (The Python `mastering-processor` command does run stereo-only processors,
# but previews them by duplicating the mono input across left/right.)Config style differs between chain entry points
The registry is string-based so C, Python, Node, WASM, and CLI callers share processor identifiers.
When you assemble a chain rather than a single processor, the config style depends on the entry point:
| Entry point | Config style |
|---|---|
WASM masteringChain(...) | Nested config objects; dot-notation leaf keys are also accepted in the same object |
masterAudio(...) and Python/Node equivalents | Flat dot-notation overrides such as 'loudness.targetLufs' |
Mastering Assistant chainConfig.params | Flat form, ready for masterAudio. params["dynamics.multibandComp"] can also carry the nested, arbitrary-band v2 object described below — see The chain-config JSON schema |
MasteringChainConfig accepts both spellings. A dot-notation leaf such as 'loudness.targetLufs': -20 can sit beside — or replace — the nested loudness: { targetLufs: -20 }, and the core validates the key and rejects an unknown one. Dot notation is the form the C ABI carries parameters in, which is what makes it the convenient shape when you are assembling overrides dynamically rather than writing them out.
The nested spelling is the canonical one, so prefer it in hand-written code: TypeScript checks a nested config field by field, while a dotted key is only checked at run time.
Repair chain keys follow the chain slots, not the one-shot registry names: use repair.denoise.* / repair.dereverb.* in flat overrides or the nested repair: { denoise: ..., dereverb: ... } shape in masteringChain(...).
The chain-config JSON schema
The flat chainConfig.params map (the shape chainConfig.params uses in the table above, and the shape masterAudio overrides accept) has a JSON-document serialization used by the CLI and the Mastering Assistant: sonare mastering --config <file> reads it, and masteringAssistantSuggest's chainConfig is expressed in it. That serialization auto-selects one of two schema versions.
Version 1 vs. version 2
Version 1 — the flat, fixed 3-band low/mid/high multiband compressor shape:
dynamics.multibandComp.lowCutoffHz,.highCutoffHz, and per-bandlowThresholdDb/midThresholdDb/highThresholdDband their ratio/attack/release siblings. This is what every flat-override entry point sends, and what the Mastering Assistant always emits in practice — it never customizes the multiband compressor beyond the default 3-band shape, so itschainConfigstays version 1.Version 2 — selected automatically once the multiband compressor configuration can no longer be represented as the fixed 3-band shape (a different cutoff count, non-default crossover slope/mode, or a non-default FIR kernel size). In that case
params["dynamics.multibandComp"]becomes a structured object instead of the flatlow/mid/highkeys:crossover.cutoffsHz[],crossover.slope,crossover.mode,crossover.firKernelSizebands[]— up to 64 bands, each withthresholdDb,ratio,attackMs,releaseMs,kneeDb,makeupGainDb,autoMakeup,detector,sidechainHpfEnabled,sidechainHpfHz,pdrTimeMs,pdrReleaseScale
Field validation is strict: unknown keys anywhere in the version-2
dynamics.multibandCompobject are rejected, and the band count must equal the cutoff count plus one.
Reachability: JSON-document feature, not a JS-object feature
The version-2 structured form is reachable through the JSON document — the CLI's sonare mastering --config <file>, or any code that reads/writes chain config as JSON. The WASM masteringChain() TypeScript MasteringChainConfig.dynamics.multibandComp interface still only exposes the fixed low/mid/high shorthand, so the arbitrary-band form cannot be reached by building a MasteringChainConfig object directly in JavaScript — only by writing the JSON document yourself or generating one with the wider crossover count (for example through the named processor multiband.compressor and its up-to-9-band cutoffNHz slots) and passing it through the JSON path.
Related
- Mastering Assistant — profile/suggest/preview JSON and the suggestion→render path
- Mastering Implementation — the chain that renders in the browser demo
- DSP Implementation Notes — how each family behaves
- Mixing Engine — load these processors as channel-strip/bus inserts