diff --git a/README.md b/README.md index 29d1553..b838197 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ the sink volume slider to the loudness-compensation stage. ``` in ─▶ equalizer ─▶ virtualbass ─▶ multiband_compressor ─▶ limiter ─▶ ell/elr ─▶ copyL/R ─┬▶ convLT/convRT ─▶ tlim ─▶ out - (LSP x16) (bankstown) (LSP mb_comp x6) (fastLookahead) (loud_comp) │ (tweeter FIR) (limit) + (LSP x16) (bankstown) (LSP mb_comp x8) (fastLookahead) (loud_comp) │ (tweeter FIR) (limit) └▶ convLW/convRW ─▶ wlim ─▶ out (woofer FIR) (limit) ``` @@ -37,8 +37,8 @@ in ─▶ equalizer ─▶ virtualbass ─▶ multiband_compressor ─▶ limite |---|---|---|---| | Pre-EQ | *none* | LSP `para_equalizer_x16_stereo`, `g_in 0.5` | Warm/bass-forward voicing curve | | Gain staging | n/a | EQ `g_in` padded to 0.5 (≈ −6 dB); the ~3 dB net loss restored at `multiband_compressor.g_out 1.4`, with band thresholds `al_*` scaled to match | Run the EQ + `virtualbass` cooler; recover level only after the compressor detectors, right before the limiter | -| EQ band 0 | n/a | 24 dB/oct **high-pass @ 50 Hz** (`ft_0 2`, `s_0 1`) | Keep subsonic energy off the small woofers | -| Dynamics | single-band `compressor_stereo` | `mb_compressor_stereo`, **6 bands** (xover 90/200/500/1500/5000 Hz), Modern mode | Per-band peak control that doesn't duck the mids on a bass beat | +| EQ band 0 | n/a | 48 dB/oct **high-pass @ 60 Hz** (`ft_0 2`, `s_0 3`) | Kill everything below the woofer's usable range — −3 dB at 60 Hz, ≈ −30 dB by 40 Hz | +| Dynamics | single-band `compressor_stereo` | `mb_compressor_stereo`, **8 bands** (xover 60/80/100/130/160/200/500 Hz), Modern mode | Per-band peak control that doesn't duck the mids on a bass beat; the woofer range is split finely (five bands 60–200 Hz) so the 60–80 and 80–100 Hz octaves can be clamped harder than the rest | | Woofer FIR gain | `1.0` | `1.15` (`convLW` / `convRW`) | Small trim only; the low-end drive now lives in the EQ bass bells (upstream, so it passes through the compressor + limiters instead of being an uncontrolled post-gain) | | Bass EQ bells | n/a | 31.5–200 Hz boosted ~+2.3 dB above the base warm tilt to offset the FIR gain reduction | Same woofer output level, but dynamically governed | | Post-FIR limiters | *none* | `wlim` (−2 dB) after woofer FIR, `tlim` (−1 dB) after tweeter FIR | Hard ceiling on the *actual* driver signal — excursion / clip backstop | @@ -76,16 +76,32 @@ should not distort the woofers or duck the midrange. - **Bass beats don't distort** is handled by multiband, not broadband, compression. A single-band compressor keyed off a kick drum applies gain reduction to the *whole* spectrum — vocals and mids pump on every beat, and - loud bass can shut the woofers down across all frequencies. The 6-band - multiband keeps each band responding only to its own energy. The three bands + loud bass can shut the woofers down across all frequencies. The 8-band + multiband keeps each band responding only to its own energy. The seven bands below ~500 Hz — where over-excursion and boom live — are effectively limiters: - band 0 (< 90 Hz) `cr 50`, band 1 (90–200 Hz) `cr 20`, band 2 (200–500 Hz) - `cr 15`. Bands 1 and 2 also have their thresholds `al_1` / `al_2` pulled down - ~2 dB relative to the rest, so the woofer sees a ~2 dB lower ceiling in the - midbass than in the sub. Bands 3–5 above that limit gently (`cr 5–6`) and none - of them move because of a kick drum. The EQ is left untouched, so anything - below the thresholds — i.e. quiet listening — passes with its full warm tilt - intact; only loud peaks are clamped. + + | Band | Range | `cr` | `kn` | `al` (≈ dB) | Note | + |---|---|---|---|---|---| + | 0 | < 60 Hz | 50 | 0.10 | 0.093 (−21) | catch band — mostly empty now that the EQ HPFs hard at 60 Hz | + | 1 | 60–80 Hz | 50 | 0.06 | 0.078 (−22) | hardest clamp — lowest ceiling, highest ratio, widest (softest) knee so the 50:1 eases in | + | 2 | 80–100 Hz | 30 | 0.12 | 0.095 (−20) | clamped harder than the rest, a step gentler than 60–80 | + | 3 | 100–130 Hz | 20 | 0.20 | 0.120 (−18) | midbass, as the old 90–200 band | + | 4 | 130–160 Hz | 18 | 0.24 | 0.130 (−18) | | + | 5 | 160–200 Hz | 16 | 0.28 | 0.140 (−17) | | + | 6 | 200–500 Hz | 15 | 0.30 | 0.159 (−16) | low-mid body, as the old 200–500 band | + | 7 | 500 Hz+ | 5 | 0.40 | 0.284 (−11) | single gentle band above 500 Hz (was three: 500/1500/5000) | + + (Every `mb_compressor` port is documented in + [mb-compressor-params.md](mb-compressor-params.md).) + + Band 1 (60–80 Hz) and band 2 (80–100 Hz) carry the lowest ceilings and the + highest ratios, so the two octaves that drive woofer excursion hardest are + clamped ahead of everything else — their wide knees (`kn` down at `0.06` / + `0.12`, i.e. −24 / −18 dB) make that heavy ratio ramp in gradually rather than + snap. Band 7 limits gently (`cr 5`) and does not + move because of a kick drum. The EQ is left untouched, so anything below the + thresholds — i.e. quiet listening — passes with its full warm tilt intact; + only loud peaks are clamped. - **Woofers can't bottom out.** The woofer FIR is near unity now (`1.15`), but `loud_comp` still adds bass gain after the main limiter, so the very last @@ -97,7 +113,7 @@ should not distort the woofers or duck the midrange. - **`virtualbass` (bankstown)** synthesizes harmonics of the bass in the 60–150 Hz window, so the ear perceives low end the driver never has to - physically produce — the psychoacoustic counterpart to the 50 Hz high-pass. + physically produce — the psychoacoustic counterpart to the 60 Hz high-pass. ## Tuning knobs @@ -108,18 +124,21 @@ If the woofers still bottom out or anything distorts, in order of preference: | `wlim.control` | `limit` | `-2` | Lower to `-3` / `-4` — hard woofer ceiling, dB | | `equalizer.control` | `g_1`–`g_5` | `1.82 / 2.48 / 3.26 / 2.61 / 1.75` | The bass boost — lower all five proportionally for less low-end drive overall | | `convLW` / `convRW` `config` | `gain` | `1.15` | FIR trim; leave it — adjust the EQ bells instead | -| `multiband_compressor.control` | `al_0` | `0.093` (≈ −21 dB) | Lower = bass band clamps sooner | +| `multiband_compressor.control` | `al_0` | `0.093` (≈ −21 dB) | Lower = < 60 Hz band clamps sooner | +| `multiband_compressor.control` | `al_1` / `al_2` | `0.078` / `0.095` | The 60–80 / 80–100 Hz ceilings — lower these two to pull the peak down further in those octaves | +| `multiband_compressor.control` | `cr_1` / `kn_1` | `50.0` / `0.06` | 60–80 Hz clamp — highest ratio, widest knee. Less aggressive: lower `cr_1` toward `20`. Sharper corner / less low-level squash: raise `kn_1` toward `1.0` | +| `multiband_compressor.control` | `cr_2` / `kn_2` | `30.0` / `0.12` | 80–100 Hz clamp — a step gentler than 60–80 | | `multiband_compressor.control` | `cr_0` | `50.0` | Already near brick-wall; leave it | -| `multiband_compressor.control` | `at_0` | `6.0` ms | Lower to ~4 ms if kick transients poke through (adds some LF harmonic distortion) | +| `multiband_compressor.control` | `at_0` | `4.0` ms | Lower toward ~3 ms if kick transients poke through (adds some LF harmonic distortion) | -If the midrange sounds over-controlled / lifeless, raise `al_1`–`al_5` (higher = -those bands stay out of the way longer) or lower their ratios `cr_1`–`cr_5` -toward `2.0`. Band 2 (200–500 Hz) at `cr 15` reaches into low-mid body — if male -vocals / snare sound boxy or thin, drop `cr_2` back toward `8`. +If the midrange sounds over-controlled / lifeless, raise `al_3`–`al_7` (higher = +those bands stay out of the way longer) or lower their ratios `cr_3`–`cr_7` +toward `2.0`. Band 6 (200–500 Hz) at `cr 15` reaches into low-mid body — if male +vocals / snare sound boxy or thin, drop `cr_6` back toward `8`. **Gain staging.** To run the EQ / `virtualbass` even cooler, lower `equalizer.g_in` further (e.g. `0.4`, `0.35`) and put the same factor back into -`multiband_compressor.g_out`, then scale `al_0`–`al_5` by that factor so the +`multiband_compressor.g_out`, then scale `al_0`–`al_7` by that factor so the compressor keeps the same behaviour. If bass feels thinner after the pad, nudge `virtualbass.amt` up (`1.0` → `1.2`) rather than raising `g_in` back. diff --git a/cap.wav b/cap.wav new file mode 100644 index 0000000..1ee1837 Binary files /dev/null and b/cap.wav differ diff --git a/graph.json b/graph.json index 778a195..558966c 100644 --- a/graph.json +++ b/graph.json @@ -12,11 +12,11 @@ "mode": 0, "g_in": 0.5, "g_out": 1.0, - "ft_0": 2, "f_0": 50.0, "g_0": 1.0, "q_0": 1.41, "s_0": 1, "xm_0": 0, "fm_0": 0, + "ft_0": 2, "f_0": 10.0, "g_0": 1.0, "q_0": 1.41, "s_0": 3, "xm_0": 0, "fm_0": 0, "ft_1": 1, "f_1": 31.5, "g_1": 1.82, "q_1": 1.41, "s_1": 0, "xm_1": 0, "fm_1": 0, "ft_2": 1, "f_2": 50.0, "g_2": 2.48, "q_2": 1.41, "s_2": 0, "xm_2": 0, "fm_2": 0, "ft_3": 1, "f_3": 80.0, "g_3": 3.20, "q_3": 1.41, "s_3": 0, "xm_3": 0, "fm_3": 0, - "ft_4": 1, "f_4": 125.0, "g_4": 2.57, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0, + "ft_4": 1, "f_4": 125.0, "g_4": 2.27, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0, "ft_5": 1, "f_5": 200.0, "g_5": 1.67, "q_5": 1.41, "s_5": 0, "xm_5": 0, "fm_5": 0, "ft_6": 1, "f_6": 315.0, "g_6": 1.35, "q_6": 1.41, "s_6": 0, "xm_6": 0, "fm_6": 0, "ft_7": 1, "f_7": 500.0, "g_7": 1.19, "q_7": 1.41, "s_7": 0, "xm_7": 0, "fm_7": 0, @@ -37,8 +37,8 @@ "control": { "bypass": 0, "amt": 1.0, - "sat_second": 1.5, - "sat_third": 2.0, + "sat_second": 1.0, + "sat_third": 1.0, "blend": 1.0, "ceil": 150.0, "floor": 60.0 @@ -51,23 +51,28 @@ "control": { "enabled": 1, "mode": 1, - "g_in": 1.0, - "g_out": 1.4, + "g_in": 1.5, + "g_out": 1.0, "g_dry": 0.0001, "g_wet": 1.0, - "cbe_1": 1, "sf_1": 90.0, - "cbe_2": 1, "sf_2": 200.0, - "cbe_3": 1, "sf_3": 500.0, - "cbe_4": 1, "sf_4": 1500.0, - "cbe_5": 1, "sf_5": 5000.0, - "cbe_6": 0, "cbe_7": 0, - "ce_0": 1, "at_0": 6.0, "rt_0": 120.0, "cr_0": 50.0, "kn_0": 0.10, "al_0": 0.093, "mk_0": 1.5, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0, - "ce_1": 1, "at_1": 8.0, "rt_1": 150.0, "cr_1": 20.0, "kn_1": 0.20, "al_1": 0.128, "mk_1": 2.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0, - "ce_2": 1, "at_2": 10.0, "rt_2": 150.0, "cr_2": 15.0, "kn_2": 0.30, "al_2": 0.159, "mk_2": 2.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0, - "ce_3": 1, "at_3": 12.0, "rt_3": 180.0, "cr_3": 5.0, "kn_3": 0.40, "al_3": 0.284, "mk_3": 1.41, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0, - "ce_4": 1, "at_4": 5.0, "rt_4": 120.0, "cr_4": 6.0, "kn_4": 0.40, "al_4": 0.250, "mk_4": 1.2, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0, - "ce_5": 1, "at_5": 3.0, "rt_5": 100.0, "cr_5": 6.0, "kn_5": 0.30, "al_5": 0.214, "mk_5": 1.1, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0, - "ce_6": 0, "ce_7": 0 + "cbe_1": 1, "sf_1": 60.0, + "cbe_2": 1, "sf_2": 80.0, + "cbe_3": 1, "sf_3": 100.0, + "cbe_4": 1, "sf_4": 130.0, + "cbe_5": 1, "sf_5": 160.0, + "cbe_6": 1, "sf_6": 200.0, + "cbe_7": 1, "sf_7": 500.0, + "cbe_8": 1, "sf_8": 1000.0, + "ce_0": 1, "at_0": 0.5, "rt_0": 120.0, "cr_0": 50.0, "kn_0": 0.0, "al_0": 0.093, "mk_0": 0.5, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0, + "ce_1": 1, "at_1": 0.6, "rt_1": 120.0, "cr_1": 50.0, "kn_1": 0.02, "al_1": 0.078, "mk_1": 1.5, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0, + "ce_2": 1, "at_2": 0.7, "rt_2": 130.0, "cr_2": 30.0, "kn_2": 0.5, "al_2": 0.095, "mk_2": 1.7, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0, + "ce_3": 1, "at_3": 0.8, "rt_3": 150.0, "cr_3": 20.0, "kn_3": 0.15, "al_3": 0.120, "mk_3": 2.0, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0, + "ce_4": 1, "at_4": 0.9, "rt_4": 150.0, "cr_4": 18.0, "kn_4": 0.24, "al_4": 0.130, "mk_4": 2.0, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0, + "ce_5": 1, "at_5": 1.0, "rt_5": 150.0, "cr_5": 16.0, "kn_5": 0.28, "al_5": 0.140, "mk_5": 2.0, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0, + "ce_6": 1, "at_6": 1.0, "rt_6": 150.0, "cr_6": 15.0, "kn_6": 0.30, "al_6": 0.159, "mk_6": 2.0, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0, + "ce_7": 1, "at_7": 1.4, "rt_7": 150.0, "cr_7": 5.0, "kn_7": 0.40, "al_7": 0.284, "mk_7": 1.3, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0, + "ce_8": 1, "at_8": 2.0, "rt_8": 150.0, "cr_8": 5.0, "kn_8": 0.40, "al_8": 0.284, "mk_8": 1.3, "bth_8": 1.0, "bsa_8": 1.0, "scm_8": 0 + } }, { @@ -121,7 +126,7 @@ "/usr/share/t2-linux-audio/15_1/tweeters-96k.wav" ], "channel": 0, - "gain": 1.0 + "gain": 1.2 } }, { @@ -135,7 +140,7 @@ "/usr/share/t2-linux-audio/15_1/tweeters-96k.wav" ], "channel": 0, - "gain": 1.0 + "gain": 1.2 } }, { @@ -149,7 +154,7 @@ "/usr/share/t2-linux-audio/15_1/woofers-96k.wav" ], "channel": 0, - "gain": 1.15 + "gain": 1.4 } }, { @@ -163,7 +168,7 @@ "/usr/share/t2-linux-audio/15_1/woofers-96k.wav" ], "channel": 0, - "gain": 1.15 + "gain": 1.4 } }, { diff --git a/mb-compressor-params.md b/mb-compressor-params.md new file mode 100644 index 0000000..bb91df9 --- /dev/null +++ b/mb-compressor-params.md @@ -0,0 +1,76 @@ +# LSP `mb_compressor_stereo` — parameter reference + +Every key in the `multiband_compressor` node of [graph.json](graph.json), with the +LSP port name in **bold** and the JSON symbol in `code`. Values shown are the +ones currently in the graph (8-band split, `split-limiting` branch). + +Source of truth: `mb_compressor_stereo.ttl` from lsp-plugins 25.08. + +## Global (once, not per band) + +| Key | LSP name | Value | Meaning | +|---|---|---|---| +| `enabled` | Enabled | 1 | plugin active | +| `mode` | Compressor mode | 1 = **Modern** | band-split topology. `0` Classic = analog-style tree of crossovers; `1` Modern = flat per-band summation (cleaner, used here); `2` Linear Phase = FIR, high latency | +| `g_in` / `g_out` | Input / Output gain | 1.0 / 1.4 | linear. `g_out 1.4` ≈ +3 dB — where the EQ's `g_in 0.5` pad is restored, *after* the band detectors | +| `g_dry` / `g_wet` | Dry / Wet gain | 0.0001 / 1.0 | parallel mix — effectively 100 % wet | + +## Crossover (index 1–7) + +| Key | LSP name | Meaning | +|---|---|---| +| `cbe_N` | **Compression band enable N** | turns split point N on → creates that band boundary. All 7 = `1` → 8 bands | +| `sf_N` | **Split frequency N** (Hz) | the crossover frequency. Currently `60 / 80 / 100 / 130 / 160 / 200 / 500` | + +## Per band (index 0–7) + +| Key | LSP name | Units | Meaning | +|---|---|---|---| +| `ce_N` | **Compressor enable N** | toggle | band compresses (`0` = passes flat) | +| `al_N` | **Attack threshold N** | linear gain (0.001–1.0) | the threshold — level above this is compressed. dB = 20·log₁₀(g); `0.093` ≈ −21 dB | +| `at_N` | **Attack time N** | ms | how fast gain reduction engages once over threshold | +| `rt_N` | **Release time N** | ms | how fast gain recovers once back under threshold | +| `cr_N` | **Ratio N** | x:1 (1–100) | dB in per dB out above threshold. `1` = off, `2` = gentle, `50` ≈ brick-wall limiter | +| `kn_N` | **Knee N** | linear gain (0.063–1.0) | width of the soft transition **around** the threshold. `1.0` = hard corner; **lower = wider / softer** (`0.063` ≈ ±24 dB — ratio ramps in over a huge range). Plugin default `0.5` (−6 dB) | +| `mk_N` | **Makeup gain N** | linear (0.001–1000) | post-compression level trim for the band. `1.5` ≈ +3.5 dB, `2.0` = +6 dB | +| `scm_N` | **Sidechain mode N** | enum | detector: `0` = **Peak**, `1` RMS, `2` LPF, `3` SMA. Peak is used on every band (right for limiting; plugin default is RMS) | +| `bth_N` | **Boost threshold N** | linear gain (1e-6–1e-3) | only used when band compression mode = Up / Boost. **Inert here** — see note below | +| `bsa_N` | **Boost signal amount N** | linear gain (2.5e-4–3981) | ditto — inert | + +Other per-band ports exist in the plugin but are left at default and not written +to the graph: `cm_N` (compression mode: Down/Up/Boost — default Down), +`bs_N`/`bm_N` (band solo/mute), `rrl_N`/`rl_N` (release threshold/level — `0` = +track the attack threshold), and the `sc*_N` sidechain family +(`sce_/scs_/sscs_/sla_/scr_/scp_/sclc_…`). + +## Current bands, in plain English + +| Band | Range | `al` (thr) | `at` / `rt` | `cr` | `kn` | `mk` | reads as | +|---|---|---|---|---|---|---|---| +| 0 | < 60 Hz | 0.093 (−21 dB) | 2 / 120 ms | 50 | 0.10 (−20 dB) | 1.5 (+3.5 dB) | fast brick-wall; near-empty behind the 60 Hz EQ high-pass | +| 1 | 60–80 Hz | 0.078 (−22 dB) | 5 / 120 ms | 50 | 0.06 (−24 dB, clamped) | 1.5 (+3.5 dB) | lowest ceiling, 50:1, softest knee → heavy but gradual | +| 2 | 80–100 Hz | 0.095 (−20 dB) | 6 / 130 ms | 30 | 0.12 (−18 dB) | 1.7 (+4.6 dB) | one notch gentler than band 1 | +| 3 | 100–130 Hz | 0.120 (−18 dB) | 8 / 150 ms | 20 | 0.20 (−14 dB) | 2.0 (+6 dB) | old "90–200" midbass character | +| 4 | 130–160 Hz | 0.130 (−18 dB) | 8 / 150 ms | 18 | 0.24 (−12 dB) | 2.0 (+6 dB) | " | +| 5 | 160–200 Hz | 0.140 (−17 dB) | 9 / 150 ms | 16 | 0.28 (−11 dB) | 2.0 (+6 dB) | eases toward the body band | +| 6 | 200–500 Hz | 0.159 (−16 dB) | 10 / 150 ms | 15 | 0.30 (−10 dB) | 2.0 (+6 dB) | low-mid body (snare / male vocal) | +| 7 | 500 Hz+ | 0.284 (−11 dB) | 8 / 150 ms | 5 | 0.40 (−8 dB) | 1.3 (+2.3 dB) | single gentle catch-all above 500 Hz | + +## Notes / gotchas + +- **Knee direction.** Lower `kn` = **wider / softer** knee, not harder. `kn_1 = + 0.06` on the 60–80 Hz band is the softest the plugin allows (it clamps `0.06` + → `0.063`). That band still pulls peaks down hard because of the low threshold + + 50:1 ratio; the wide knee just means the ratio ramps in gradually starting + well below −22 dB (so there is some low-level gain reduction too). To clamp + those octaves only near the ceiling and keep them cleaner below it, *raise* + `kn_1` / `kn_2` toward `0.3`–`0.5`. + +- **`bth_N` / `bsa_N` are dead config.** Set to `1.0` in the graph (inherited + from upstream) but their valid ranges are ~`1e-6`–`1e-3` and + `2.5e-4`–`3981` — the host clamps them. They only apply to upward / boost + compression, which is not enabled (`cm_N` is never set, so every band is + downward), so it is harmless. + +- **`al_N` is linear, not dB.** Same for `kn_N`, `mk_N`, `g_*`. Convert with + `dB = 20·log₁₀(value)`.