// master_gain.h — PURE dB<->linear<->knob-taper math for the FB1 post-mixer master gain. // NO VST3, NO REAPER, NO SWELL/LICE types. The mirror of trigger_seam: one tiny module owns // the ONE formula both sides of a seam share — here the editor's Gain knob (normalized 0..1) // and the processor's stored/applied linear gain — so the drawn needle, the persisted value, // and the audio-thread multiply can never drift. // // THE CONTROL (Daniel, FB1). A post-mixer master gain, range -inf .. +24 dB, dB-scaled taper // with -inf at the BOTTOM of the knob: normalized 0 maps to TRUE ZERO linear gain (silence, // not a tiny epsilon), and the remaining travel maps linearly in dB from kMasterGainMinDb // (the finite taper floor) up to kMasterGainMaxDb. Unity (0 dB) sits at norm // kMasterGainMinDb/(kMasterGainMinDb - kMasterGainMaxDb) ~= 0.714 — most of the throw is // usable trim, the last stretch is boost. The PERSISTED value is the LINEAR gain (a plain // finite double, 0 = silence — no -inf on the wire); the taper is a UI-side view of it. // // RT DISCIPLINE: the processor applies the linear gain as one multiply over the summed // output — these functions run on the UI/state threads only. #pragma once #include namespace reasampler::vst { // The dB taper endpoints. norm 0 is -inf (true zero); norm just above 0 starts at the // finite floor kMasterGainMinDb and sweeps linearly in dB to kMasterGainMaxDb at norm 1. inline constexpr double kMasterGainMinDb = -60.0; inline constexpr double kMasterGainMaxDb = 24.0; // The largest linear gain the control can produce (kMasterGainMaxDb as a ratio, ~15.849). double masterGainMaxLinear(); // Knob taper: normalized [0,1] -> dB. norm <= 0 -> -infinity; else the linear-in-dB sweep // [kMasterGainMinDb, kMasterGainMaxDb]. norm is clamped to [0,1]. Pure. double masterGainDbFromNorm(double norm); // Inverse taper: dB -> normalized [0,1]. -infinity (or any dB at or below kMasterGainMinDb, // including below-floor values like -80 dB) maps to norm 0 (the -inf bottom detent) — the // finite sweep only covers the range above kMasterGainMinDb; everything at or below it collapses // to the same true-zero bottom. +24 -> 1. Pure. double masterGainNormFromDb(double db); // Knob taper composed with dB->ratio: normalized [0,1] -> LINEAR gain. norm 0 -> exactly // 0.0 (true silence); norm 1 -> masterGainMaxLinear(). Pure. double masterGainLinearFromNorm(double norm); // Inverse: LINEAR gain -> normalized [0,1]. linear <= 0 -> 0 (the -inf bottom); a linear at // or below the kMasterGainMinDb floor (e.g. 0.001 = -60 dB, or anything below) also maps to 0 // — the floor IS the -inf detent; values between true-zero and the floor cannot be represented // on the knob and collapse to the bottom. unity -> ~0.714; masterGainMaxLinear() -> 1. // Out-of-range/non-finite input clamps. Pure. double masterGainNormFromLinear(double linear); // The knob's hover/drag value label for a normalized value: "-inf" at the bottom, else a // signed one-decimal dB string ("-12.0dB", "+0.0dB", "+2.4dB"). Writes at most `len` bytes // including the terminator. Pure. void formatMasterGainLabel(double norm, char* buf, std::size_t len); } // namespace reasampler::vst