loudness-tracked-tilt #1
BIN
15_1/tweeters-44k.wav
Normal file
BIN
15_1/tweeters-44k.wav
Normal file
Binary file not shown.
BIN
15_1/tweeters-48k.wav
Normal file
BIN
15_1/tweeters-48k.wav
Normal file
Binary file not shown.
BIN
15_1/tweeters-96k.wav
Normal file
BIN
15_1/tweeters-96k.wav
Normal file
Binary file not shown.
BIN
15_1/woofers-44k.wav
Normal file
BIN
15_1/woofers-44k.wav
Normal file
Binary file not shown.
BIN
15_1/woofers-48k.wav
Normal file
BIN
15_1/woofers-48k.wav
Normal file
Binary file not shown.
BIN
15_1/woofers-96k.wav
Normal file
BIN
15_1/woofers-96k.wav
Normal file
Binary file not shown.
215
INSTALL.md
Normal file
215
INSTALL.md
Normal file
@@ -0,0 +1,215 @@
|
||||
# Install
|
||||
|
||||
How to get this DSP graph running on a **MacBook Pro 15,1** under Linux (T2 /
|
||||
`t2linux`, or Asahi on the Intel-T2 stack where applicable).
|
||||
|
||||
For what the graph *does*, see [README.md](README.md). This file is only the
|
||||
mechanics of getting the pieces in place.
|
||||
|
||||
---
|
||||
|
||||
## 1. What has to be true first
|
||||
|
||||
This repo is **just a `graph.json`** (plus `apply.sh`). It is not a driver and
|
||||
not self-contained. Three things must already exist on the machine:
|
||||
|
||||
| Requirement | Provided by | Why |
|
||||
|---|---|---|
|
||||
| Raw speaker PCM exposed by the kernel/ALSA | the `t2linux` kernel + ALSA stack | there is nothing to process otherwise |
|
||||
| A hidden sink `alsa_output.platform-sound.RawSpeakers` with a filter-chain spliced in front of it | the **t2 speaker-DSP package** (`t2-linux-audio` / `t2-apple-audio-dsp`), specifically its WirePlumber drop-in `51-t2-dsp.conf` | this graph *attaches to* that spliced filter-chain; no package → no sink → nothing to apply |
|
||||
| The FIR correction files at `/usr/share/t2-linux-audio/15_1/` | the same package | `graph.json` references them by absolute path (see §4) |
|
||||
| The LV2 plugins the graph loads | your distro + a source build for one of them | see §3 |
|
||||
|
||||
If `wpctl status` shows no **"MacBook Pro 15,1 DSP Speakers"** sink and no
|
||||
`alsa_output.platform-sound.RawSpeakers`, stop here and install the t2 speaker-DSP
|
||||
package for your distro first — see <https://wiki.t2linux.org/guides/audio-config/>.
|
||||
|
||||
---
|
||||
|
||||
## 2. Get the repo
|
||||
|
||||
```sh
|
||||
git clone <this-repo> mbp15-1-audio-dsp
|
||||
cd mbp15-1-audio-dsp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. LV2 plugin dependencies
|
||||
|
||||
The graph loads four LV2 plugins from three bundles. (`copy` and `convolver` are
|
||||
PipeWire builtins — nothing to install.)
|
||||
|
||||
| Plugin URI in `graph.json` | Bundle | Package (varies by distro) |
|
||||
|---|---|---|
|
||||
| `http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo` | LSP Plugins | `lsp-plugins` / `lsp-plugins-lv2` |
|
||||
| `http://lsp-plug.in/plugins/lv2/mb_compressor_stereo` | LSP Plugins | ″ |
|
||||
| `http://lsp-plug.in/plugins/lv2/loud_comp_mono` | LSP Plugins | ″ |
|
||||
| `http://plugin.org.uk/swh-plugins/fastLookaheadLimiter` | SWH Plugins | `swh-plugins` / `lv2-swh-plugins` |
|
||||
| `https://chadmed.au/bankstown` | Bankstown | **not packaged on most distros — build from source** |
|
||||
|
||||
### 3a. LSP + SWH (from your package manager)
|
||||
|
||||
```sh
|
||||
# Fedora / Fedora Asahi Remix
|
||||
sudo dnf install lsp-plugins swh-plugins
|
||||
|
||||
# Arch / t2linux
|
||||
sudo pacman -S lsp-plugins swh-plugins # or AUR: lsp-plugins-lv2
|
||||
|
||||
# Debian / Ubuntu
|
||||
sudo apt install lsp-plugins swh-plugins
|
||||
```
|
||||
|
||||
Package names drift — if the above miss, search: `dnf search lsp`,
|
||||
`pacman -Ss lsp-plugins`, `apt-cache search swh`. The authority is whether the
|
||||
URIs resolve (§3c), not the package name.
|
||||
|
||||
### 3b. Bankstown (source build)
|
||||
|
||||
Bankstown (`virtualbass` in the graph) is chadmed's psychoacoustic bass plugin.
|
||||
On Fedora Asahi Remix it ships in the `asahi-audio` stack; everywhere else,
|
||||
build it:
|
||||
|
||||
```sh
|
||||
# needs: rust/cargo, clang, lv2 headers, git
|
||||
git clone https://github.com/chadmed/bankstown
|
||||
cd bankstown
|
||||
make # runs: cargo build --release
|
||||
|
||||
# install the bundle. LIBDIR defaults to /usr/lib64 — override on distros
|
||||
# that use /usr/lib (Arch, Debian/Ubuntu):
|
||||
sudo make install # Fedora
|
||||
sudo make install LIBDIR=/usr/lib # Arch, Debian, Ubuntu
|
||||
# → installs to $LIBDIR/lv2/bankstown.lv2/
|
||||
|
||||
cd ..
|
||||
```
|
||||
|
||||
Per-user install (no sudo) also works — copy the built
|
||||
`target/release/libbankstown.so` → `~/.lv2/bankstown.lv2/bankstown.so` alongside
|
||||
`bankstown.ttl` and `manifest.ttl` from the repo.
|
||||
|
||||
### 3c. Verify all four URIs resolve
|
||||
|
||||
```sh
|
||||
for uri in \
|
||||
http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo \
|
||||
http://lsp-plug.in/plugins/lv2/mb_compressor_stereo \
|
||||
http://lsp-plug.in/plugins/lv2/loud_comp_mono \
|
||||
http://plugin.org.uk/swh-plugins/fastLookaheadLimiter \
|
||||
https://chadmed.au/bankstown ; do
|
||||
lv2ls | grep -qxF "$uri" && echo "ok $uri" || echo "MISSING $uri"
|
||||
done
|
||||
```
|
||||
|
||||
Every line must say `ok`. `lv2ls` is from `lilv` (`lilv-utils` / `lilv`).
|
||||
|
||||
---
|
||||
|
||||
## 4. FIR correction files
|
||||
|
||||
`graph.json` references them by **absolute path**:
|
||||
|
||||
```
|
||||
/usr/share/t2-linux-audio/15_1/tweeters-44k.wav tweeters-48k.wav tweeters-96k.wav
|
||||
/usr/share/t2-linux-audio/15_1/woofers-44k.wav woofers-48k.wav woofers-96k.wav
|
||||
```
|
||||
|
||||
```sh
|
||||
ls -l /usr/share/t2-linux-audio/15_1/*.wav
|
||||
```
|
||||
|
||||
These are **not vendored in this repo** and the paths are **deliberately not
|
||||
relative**:
|
||||
|
||||
- The t2 speaker-DSP package installs them to this fixed FHS path on every
|
||||
distro, and you already need that package for the sink to exist at all (§1),
|
||||
so the absolute path is stable wherever the prerequisite is met.
|
||||
- PipeWire's convolver resolves a non-absolute `filename` against the process
|
||||
working directory, which for a WirePlumber-spawned service is unpredictable
|
||||
(`/` or `$HOME`). Relative paths would be *less* portable, not more.
|
||||
|
||||
If you have your own recalibrated FIRs, drop them at that path (or edit the six
|
||||
`filename` entries in `graph.json` to point at yours) before §5.
|
||||
|
||||
---
|
||||
|
||||
## 5. Apply
|
||||
|
||||
```sh
|
||||
./apply.sh
|
||||
```
|
||||
|
||||
This does:
|
||||
|
||||
1. `sudo cp graph.json /usr/share/t2-linux-audio/15_1/graph.json`
|
||||
2. `systemctl --user restart wireplumber`
|
||||
|
||||
Optional sanity check first (the tool is `python3-json` / stdlib):
|
||||
|
||||
```sh
|
||||
python3 -m json.tool graph.json > /dev/null && echo "graph.json OK"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. Confirm it loaded
|
||||
|
||||
```sh
|
||||
wpctl status | grep -i "DSP Speakers"
|
||||
pw-cli ls Node | grep -i t2-151-speakers
|
||||
```
|
||||
|
||||
Select **"MacBook Pro 15,1 DSP Speakers"** as the output (`wpctl set-default
|
||||
<id>`, or your DE's sound settings), then play something.
|
||||
|
||||
To watch the plugins actually run / spot xruns:
|
||||
|
||||
```sh
|
||||
pw-top # look for the filter-chain node, check for XRUN
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. Pick a sound profile
|
||||
|
||||
`user_eq` is the first node in the graph and the only block meant for
|
||||
hand-editing — an 8-band tone control that defaults flat (= the reference
|
||||
voicing). Paste one of the preset rows from
|
||||
[README.md § User preference EQ](README.md#user-preference-eq) into its `control`
|
||||
block and re-run `./apply.sh`.
|
||||
|
||||
---
|
||||
|
||||
## 8. After a system update
|
||||
|
||||
A `t2-linux-audio` package update **overwrites**
|
||||
`/usr/share/t2-linux-audio/15_1/graph.json` and silently reverts to the stock
|
||||
graph. Re-run `./apply.sh` afterward. (Plugin packages updating is fine — the
|
||||
graph only cares that the URIs still resolve.)
|
||||
|
||||
---
|
||||
|
||||
## 9. Revert to stock
|
||||
|
||||
```sh
|
||||
sudo cp /path/to/t2-apple-audio-dsp/configs/15_1/graph.json \
|
||||
/usr/share/t2-linux-audio/15_1/graph.json
|
||||
systemctl --user restart wireplumber
|
||||
```
|
||||
|
||||
Or reinstall the t2 speaker-DSP package.
|
||||
|
||||
---
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely cause | Fix |
|
||||
|---|---|---|
|
||||
| No "DSP Speakers" sink after apply | t2 speaker-DSP package / `51-t2-dsp.conf` not installed | §1 |
|
||||
| Sink present, but silent / falls back to another device | a plugin URI failed to load, so the whole filter-chain fails | §3c — find the `MISSING` line |
|
||||
| `wireplumber` won't start after apply | malformed `graph.json` | `python3 -m json.tool graph.json`; `journalctl --user -u wireplumber -b` |
|
||||
| Works, but no bass enhancement | `bankstown` (`virtualbass`) not loaded | §3b, then §3c |
|
||||
| Distortion when loud | drive too high for your unit — see [README.md § Tuning knobs](README.md#tuning-knobs) | lower `wlim.limit`, or the `user_eq` bass bands |
|
||||
| Reverted itself after an update | expected — see §8 | re-run `./apply.sh` |
|
||||
237
README.md
237
README.md
@@ -22,21 +22,65 @@ All structural elements are unchanged: node names, FIR `.wav` paths
|
||||
output, allowed rates 48000/44100, and the `capture.volumes` mapping that ties
|
||||
the sink volume slider to the loudness-compensation stage.
|
||||
|
||||
## Prior art — why this fork
|
||||
|
||||
Two recurring complaints about the upstream chains, from their own issue
|
||||
trackers and docs, motivated the changes here:
|
||||
|
||||
**It sounds thin / not warm enough.** `asahi-audio` deliberately targets *"a
|
||||
mostly flat response ... without adding an excessive amount of colour"* and
|
||||
explicitly rejects Apple's *"exaggerated Harman curve."* Flat magnitude is
|
||||
equal-energy-per-*Hz*, which reads as bass-light against pink-balanced program
|
||||
material (see [Design rationale](#why-the-voicing-curve-exists--and-why-upstream-sounds-thin)).
|
||||
Users keep asking for the warmth back:
|
||||
|
||||
- [asahi-audio #31](https://github.com/AsahiLinux/asahi-audio/issues/31) — request for a bass/treble ("smiley") curve + a guide to change it system-wide; closed with no documented answer.
|
||||
- [asahi-audio #93](https://github.com/AsahiLinux/asahi-audio/issues/93) — *"Lacks proper bass and high frequencies, sounding flat, thin, and muddy";* closed as not planned.
|
||||
- [t2-apple-audio-dsp #21](https://github.com/lemmyg/t2-apple-audio-dsp/issues/21) (direct upstream) — *"Quality is good but volume is quite low even at maximum volume."*
|
||||
- [Asahi audio docs](https://asahilinux.org/docs/sw/audio-userspace/) known issues: the 13″ MBA EQ *"might be a bit harsh on the treble."*
|
||||
|
||||
**It distorts when loud, because there is no output limiter.** The Asahi docs
|
||||
state plainly: *"There is no final limiter/compressor in the current DSP
|
||||
chains"* (only an input compressor), so *"content in high-gain regions of the EQ
|
||||
curve might cause distortion or clipping."* They also flag *"the 200 Hz region"*
|
||||
for distortion risk and note `bankstown` is *"prone to saturation artifacts at
|
||||
high volumes."*
|
||||
|
||||
- [asahi-audio #22](https://github.com/AsahiLinux/asahi-audio/issues/22) — j313 distortion at 100 %; traced to convolver gain being too hot, worked around by dropping it to ~0.6.
|
||||
- [asahi-audio #42](https://github.com/AsahiLinux/asahi-audio/issues/42) — J474 distortion above 72 % volume.
|
||||
- [asahi-audio #91](https://github.com/AsahiLinux/asahi-audio/issues/91) — j313 *"severe speaker distortion"* at 55 %; `speakersafetyd` logs nothing, i.e. it's in the signal processing, not the protection model.
|
||||
|
||||
This fork's answers, point by point:
|
||||
|
||||
| Upstream complaint | Change here |
|
||||
|---|---|
|
||||
| flat / thin / "not warm enough" | EQ bass tilt (≈ +3 dB/octave per-Hz) ahead of the dynamics |
|
||||
| no final limiter → clipping in high-gain EQ regions | `limiter` + post-FIR `wlim` / `tlim` on the real driver signal |
|
||||
| 200 Hz distortion risk | fine multiband split through the low-mids |
|
||||
| `bankstown` saturates at volume | EQ `g_in 0.5` pad + `sat_second` / `sat_third` reduced |
|
||||
| convolver gain too hot at 100 % (#22) | FIR kept near unity; drive lives in the dynamically-governed EQ |
|
||||
| "volume too low at max" (#21) | `g_out` makeup + loudness maximisation into the limiters |
|
||||
|
||||
## Signal chain
|
||||
|
||||
```
|
||||
in ─▶ equalizer ─▶ virtualbass ─▶ multiband_compressor ─▶ limiter ─▶ ell/elr ─▶ copyL/R ─┬▶ convLT/convRT (tweeter FIR) ─▶ out
|
||||
(LSP x16) (bankstown) (LSP mb_comp x4) (fastLookahead) (loud_comp) └▶ convLW/convRW (woofer FIR) ─▶ out
|
||||
in ─▶ user_eq ─▶ equalizer ─▶ virtualbass ─▶ multiband_compressor ─▶ limiter ─▶ ell/elr ─▶ copyL/R ─┬▶ convLT/convRT ─▶ tlim ─▶ out
|
||||
(LSP x16) (LSP x16) (bankstown) (LSP mb_comp x8) (fastLookahead) (loud_comp) │ (tweeter FIR) (limit)
|
||||
user prefs voicing └▶ convLW/convRW ─▶ wlim ─▶ out
|
||||
(woofer FIR) (limit)
|
||||
```
|
||||
|
||||
## Changes vs. upstream `15_1/graph.json`
|
||||
|
||||
| Stage | Upstream | This fork | Purpose |
|
||||
|---|---|---|---|
|
||||
| Pre-EQ | *none* | LSP `para_equalizer_x16_stereo`, `g_in 0.7` | Warm/bass-forward voicing curve |
|
||||
| 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`, 4 bands (xover 120/400/1000 Hz) | See rationale below |
|
||||
| Woofer FIR gain | `1.0` | `1.5` (`convLW` / `convRW`) | More low-end output (overdrive) |
|
||||
| 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 | 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 |
|
||||
|
||||
Everything else is byte-identical to upstream.
|
||||
|
||||
@@ -45,53 +89,185 @@ Everything else is byte-identical to upstream.
|
||||
**Goal:** mild-volume music should sound warm and full; bass-heavy material
|
||||
should not distort the woofers or duck the midrange.
|
||||
|
||||
### Why the voicing curve exists — and why upstream sounds thin
|
||||
|
||||
Music is mastered for systems with a roughly **equal-energy-per-octave** (pink)
|
||||
balance and the headroom to reproduce it. The forked FIR filters (`asahi-audio` /
|
||||
`t2-apple-audio-dsp`) correct the drivers to **flat magnitude = equal energy per
|
||||
_Hz_** — measurement-correct, but each octave down then carries the same per-Hz
|
||||
energy across half the bandwidth, so it lands **bass-light** ("not warm enough").
|
||||
Matching the per-octave balance needs ≈ **+3 dB/octave** of per-Hz lift toward the
|
||||
lows — that is what the EQ bass bells are for.
|
||||
|
||||
That tilt can't be static. The two things it can break are different above and
|
||||
below **~150 Hz**:
|
||||
|
||||
| Region | Failure mode | Scaling | Guarded by |
|
||||
|---|---|---|---|
|
||||
| **< ~150 Hz** | woofer **over-excursion** — cone bottoms out | displacement ∝ 1/f² ≈ **+12 dB/octave** for constant SPL | fine multiband split (one limiter per bass octave, held release) + the 20 Hz subsonic HPF; `virtualbass` supplies deep sub as harmonics so the cone never has to move for it |
|
||||
| **> ~150 Hz** | **over-voltage** — demanded level exceeds the amp's max swing to the cone | ≈ flat (voltage/thermal, not displacement) | multiband peak control per band, then `limiter` / `wlim` / `tlim` as backstop |
|
||||
|
||||
The `fastLookaheadLimiter` stages are a *second* line of defence only, because
|
||||
they are **broadband**: when one triggers it ducks every frequency at once, so a
|
||||
loud trombone transient pulls the violins down with it. The multiband compressor
|
||||
is the *first* line precisely because its gain reduction stays inside the
|
||||
offending band — the more work it does, the less the broadband limiters engage
|
||||
and the cleaner the result. Net: full warmth at low level, graceful flattening
|
||||
toward the FIR's flat-per-Hz curve as it gets loud, with cheap pitch-reinforcing
|
||||
harmonic distortion traded for ugly (and mechanically risky) excursion
|
||||
distortion.
|
||||
|
||||
### Warm at low volume
|
||||
|
||||
- **Warm at low volume** is handled two ways:
|
||||
- `ell` / `elr` (`loud_comp_mono`) is a true ISO-226 equal-loudness
|
||||
compensator. The sink volume slider feeds `ell:volume` / `elr:volume`
|
||||
(cubic, −65→0 dB), so bass/treble lift automatically increases as you turn
|
||||
the volume down and recedes as you turn it up.
|
||||
- The static EQ bells (31.5–125 Hz, +1.4 to +2.5 dB) add a fixed warmth tilt.
|
||||
- The static EQ bells (31.5–125 Hz) add a fixed warmth tilt. Note LSP's `g_*`
|
||||
ports are **linear amplitude, not dB** — `g_3 = 3.26` is ≈ +10 dB, offset by
|
||||
`g_in 0.5` (≈ −6 dB). This is a hot bass shelf on purpose; the dynamics
|
||||
stages below exist to keep it safe when loud. The bass boost lives here
|
||||
rather than in the woofer FIR gain (kept near unity at 1.15) so it passes
|
||||
through the compressor and limiters and is dynamically controlled, instead
|
||||
of being a fixed post-everything gain that only `wlim` can catch.
|
||||
|
||||
- **Gain staging.** `g_in` on the EQ is padded to 0.5 so the boosted bands and
|
||||
`virtualbass`'s saturation stages run with headroom rather than near/over
|
||||
0 dBFS. The signal path is 32-bit float end-to-end (real clipping only happens
|
||||
at the ALSA sink), but a cooler operating point keeps `virtualbass` from being
|
||||
over-driven and keeps every plugin's internal detectors honest. The ~3 dB net
|
||||
level loss is put back at `multiband_compressor.g_out` (1.0 → 1.4) — *after*
|
||||
the band detectors, immediately before the main limiter — and the band
|
||||
thresholds `al_*` were scaled by the same factor so the compressor behaves
|
||||
exactly as before, just at a lower internal level.
|
||||
|
||||
- **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 multiband
|
||||
confines the gain reduction to band 0 (< 120 Hz): ~10:1 above ≈ −17 dB with a
|
||||
10 ms attack, so the sub band is clamped to a near-fixed ceiling while the
|
||||
midrange stays open.
|
||||
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 | 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
|
||||
stage is unguarded. `wlim` / `tlim` are `fastLookaheadLimiter` instances placed
|
||||
*after* the convolvers, so they clamp the real signal the drivers see
|
||||
regardless of upstream gain. `wlim` at −2 dB is the mechanical-excursion
|
||||
backstop; `tlim` at −1 dB protects the tweeters and keeps the two paths
|
||||
time-aligned (equal lookahead latency — no comb filtering at the crossover).
|
||||
|
||||
- **`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.
|
||||
|
||||
## User preference EQ
|
||||
|
||||
`user_eq` is the **first node in the graph** and the only block meant for
|
||||
hand-editing — a plain 8-band tone control for matching the sound to content
|
||||
type. It defaults **flat** (every `g_*` = `1.0`), which *is* the reference
|
||||
voicing; editing it never touches the calibrated `equalizer` / dynamics below.
|
||||
Because it sits ahead of the compressor and limiters, even an aggressive preset
|
||||
is dynamically governed — it can't clip or over-excurse, it just gets
|
||||
compressed if pushed hard.
|
||||
|
||||
| Band | `f` | Type | Region |
|
||||
|---|---|---|---|
|
||||
| 0 | 70 Hz | low shelf | sub weight / rumble |
|
||||
| 1 | 110 Hz | bell | bass punch |
|
||||
| 2 | 220 Hz | bell | warmth / boom |
|
||||
| 3 | 450 Hz | bell | body / mud |
|
||||
| 4 | 1 kHz | bell | mids / nasal |
|
||||
| 5 | 2.5 kHz | bell | presence / attack |
|
||||
| 6 | 6 kHz | bell | detail / sibilance |
|
||||
| 7 | 10 kHz | high shelf | air |
|
||||
|
||||
Edit the `g_*` values in the `user_eq` `control` block. **Linear, not dB**
|
||||
(`+3 dB ≈ 1.41`, `−3 dB ≈ 0.71`). Keep each between `0.5` (−6 dB) and `2.0`
|
||||
(+6 dB). Run `./apply.sh` after.
|
||||
|
||||
### Presets — the 8 `g_*` values, `g_0`…`g_7`
|
||||
|
||||
| Preset | 70 | 110 | 220 | 450 | 1k | 2.5k | 6k | 10k |
|
||||
|---|---|---|---|---|---|---|---|---|
|
||||
| **Reference** (flat) | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 | 1.00 |
|
||||
| Rock / Pop | 1.00 | 1.26 | 1.00 | 0.94 | 1.00 | 1.12 | 1.19 | 1.12 |
|
||||
| Classical / Acoustic | 1.00 | 1.00 | 1.06 | 1.00 | 1.00 | 1.00 | 1.12 | 1.12 |
|
||||
| Electronic / Hip-Hop | 1.26 | 1.19 | 1.00 | 0.94 | 1.00 | 1.00 | 1.06 | 1.00 |
|
||||
| Movie — dialogue | 0.84 | 0.94 | 1.00 | 1.06 | 1.19 | 1.19 | 1.06 | 1.00 |
|
||||
| Movie — action | 1.41 | 1.12 | 1.00 | 1.00 | 1.00 | 1.06 | 1.12 | 1.12 |
|
||||
| Late-night (low level) | 0.63 | 0.79 | 1.00 | 1.00 | 1.06 | 1.12 | 1.00 | 0.94 |
|
||||
|
||||
## Tuning knobs
|
||||
|
||||
In `graph.json`, `multiband_compressor.control`:
|
||||
If the woofers still bottom out or anything distorts, in order of preference:
|
||||
|
||||
| Key | Now | Effect |
|
||||
|---|---|---|
|
||||
| `al_0` | `0.142` (≈ −17 dB) | Bass clamp threshold — lower = clamps sooner |
|
||||
| `cr_0` | `10.0` | Bass ratio — raise toward 20:1+ for true limiting |
|
||||
| `at_0` | `10.0` ms | Lower to ~5 ms if kick transients still poke through |
|
||||
| `ce_1` / `ce_2` / `ce_3` | `1` | Set to `0` to disable bands 1–3 and compress **only** the bass |
|
||||
| `sf_1` | `120.0` Hz | Lower to ~90–100 Hz to keep band 0 off the low mids |
|
||||
| Where | Key | Now | Effect |
|
||||
|---|---|---|---|
|
||||
| `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 = < 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` | `4.0` ms | Lower toward ~3 ms if kick transients poke through (adds some LF harmonic distortion) |
|
||||
|
||||
In `graph.json`, woofer convolvers (`convLW` / `convRW`): `gain 1.5` sets actual
|
||||
woofer SPL — the dynamics ceiling won't save you if this is too hot; back toward
|
||||
`1.35` if heavy bass still distorts.
|
||||
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_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.
|
||||
|
||||
`tlim.control` `limit` (`-1`) is the tweeter ceiling — rarely needs touching, but
|
||||
**keep `tlim` present even if you disable it** (`limit` high), because it also
|
||||
holds the tweeter/woofer time alignment.
|
||||
|
||||
## Install
|
||||
|
||||
Full instructions — prerequisites, the LV2 plugin dependencies (LSP, SWH, and a
|
||||
source build of Bankstown), the FIR files, verification and troubleshooting —
|
||||
are in **[INSTALL.md](INSTALL.md)**.
|
||||
|
||||
Short version, once the prerequisites are met:
|
||||
|
||||
```sh
|
||||
./apply.sh
|
||||
```
|
||||
|
||||
This copies `graph.json` to `/usr/share/t2-linux-audio/15_1/graph.json` (needs
|
||||
`sudo`) and restarts WirePlumber (`systemctl --user restart wireplumber`).
|
||||
|
||||
Requires the `t2-linux-audio` / `t2-apple-audio-dsp` package to already be
|
||||
installed (it provides the FIR `.wav` files, `51-t2-dsp.conf`, and the
|
||||
`mic.json` graph).
|
||||
copies `graph.json` to `/usr/share/t2-linux-audio/15_1/graph.json` (needs `sudo`)
|
||||
and restarts WirePlumber. Requires the `t2-linux-audio` / `t2-apple-audio-dsp`
|
||||
package (FIR `.wav` files, `51-t2-dsp.conf`, `mic.json`) and the LV2 plugins the
|
||||
graph loads.
|
||||
|
||||
## Revert
|
||||
|
||||
@@ -102,4 +278,5 @@ systemctl --user restart wireplumber
|
||||
```
|
||||
|
||||
Note: a `t2-linux-audio` package update will overwrite the installed file and
|
||||
silently revert these changes — re-run `./apply.sh` afterward.
|
||||
silently revert these changes — re-run `./apply.sh` afterward. See
|
||||
[INSTALL.md § 8](INSTALL.md#8-after-a-system-update).
|
||||
|
||||
132
graph.json
132
graph.json
@@ -3,6 +3,22 @@
|
||||
"media.name": "MacBook Pro 15,1 DSP Speakers",
|
||||
"filter.graph": {
|
||||
"nodes": [
|
||||
{
|
||||
"type": "lv2",
|
||||
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
|
||||
"name": "user_eq",
|
||||
"control": {
|
||||
"enabled": 1, "mode": 0, "g_in": 1.0, "g_out": 1.0,
|
||||
"ft_0": 5, "f_0": 70.0, "g_0": 1.0, "q_0": 0.7, "s_0": 0,
|
||||
"ft_1": 1, "f_1": 110.0, "g_1": 1.0, "q_1": 1.0,
|
||||
"ft_2": 1, "f_2": 220.0, "g_2": 1.0, "q_2": 1.0,
|
||||
"ft_3": 1, "f_3": 450.0, "g_3": 1.0, "q_3": 1.0,
|
||||
"ft_4": 1, "f_4": 1000.0, "g_4": 1.0, "q_4": 1.0,
|
||||
"ft_5": 1, "f_5": 2500.0, "g_5": 1.0, "q_5": 1.0,
|
||||
"ft_6": 1, "f_6": 6000.0, "g_6": 1.0, "q_6": 1.0,
|
||||
"ft_7": 3, "f_7": 10000.0, "g_7": 1.0, "q_7": 0.7, "s_7": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "lv2",
|
||||
"plugin": "http://lsp-plug.in/plugins/lv2/para_equalizer_x16_stereo",
|
||||
@@ -10,24 +26,24 @@
|
||||
"control": {
|
||||
"enabled": 1,
|
||||
"mode": 0,
|
||||
"g_in": 0.7,
|
||||
"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_1": 1, "f_1": 31.5, "g_1": 1.4, "q_1": 1.41, "s_1": 0, "xm_1": 0, "fm_1": 0,
|
||||
"ft_2": 1, "f_2": 50.0, "g_2": 1.9, "q_2": 1.41, "s_2": 0, "xm_2": 0, "fm_2": 0,
|
||||
"ft_3": 1, "f_3": 80.0, "g_3": 2.5, "q_3": 1.41, "s_3": 0, "xm_3": 0, "fm_3": 0,
|
||||
"ft_4": 1, "f_4": 125.0, "g_4": 2.0, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0,
|
||||
"ft_5": 1, "f_5": 200.0, "g_5": 1.5, "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,
|
||||
"ft_8": 1, "f_8": 800.0, "g_8": 1.09, "q_8": 1.41, "s_8": 0, "xm_8": 0, "fm_8": 0,
|
||||
"ft_9": 1, "f_9": 1250.0, "g_9": 0.85, "q_9": 1.41, "s_9": 0, "xm_9": 0, "fm_9": 0,
|
||||
"ft_10": 1, "f_10": 2000.0, "g_10": 0.8, "q_10": 1.41, "s_10": 0, "xm_10": 0, "fm_10": 0,
|
||||
"ft_11": 1, "f_11": 3150.0, "g_11": 0.8, "q_11": 1.41, "s_11": 0, "xm_11": 0, "fm_11": 0,
|
||||
"ft_12": 1, "f_12": 5000.0, "g_12": 0.8, "q_12": 1.41, "s_12": 0, "xm_12": 0, "fm_12": 0,
|
||||
"ft_13": 1, "f_13": 8000.0, "g_13": 0.8, "q_13": 1.41, "s_13": 0, "xm_13": 0, "fm_13": 0,
|
||||
"ft_14": 1, "f_14": 12500.0, "g_14": 0.82, "q_14": 1.41, "s_14": 0, "xm_14": 0, "fm_14": 0,
|
||||
"ft_15": 1, "f_15": 20000.0, "g_15": 0.97, "q_15": 1.41, "s_15": 0, "xm_15": 0, "fm_15": 0
|
||||
"ft_0": 2, "f_0": 20.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": 2.51, "q_1": 1.41, "s_1": 0, "xm_1": 0, "fm_1": 0,
|
||||
"ft_2": 1, "f_2": 50.0, "g_2": 2.24, "q_2": 1.41, "s_2": 0, "xm_2": 0, "fm_2": 0,
|
||||
"ft_3": 1, "f_3": 80.0, "g_3": 2.00, "q_3": 1.41, "s_3": 0, "xm_3": 0, "fm_3": 0,
|
||||
"ft_4": 1, "f_4": 125.0, "g_4": 1.78, "q_4": 1.41, "s_4": 0, "xm_4": 0, "fm_4": 0,
|
||||
"ft_5": 1, "f_5": 200.0, "g_5": 1.58, "q_5": 1.41, "s_5": 0, "xm_5": 0, "fm_5": 0,
|
||||
"ft_6": 1, "f_6": 315.0, "g_6": 1.41, "q_6": 1.41, "s_6": 0, "xm_6": 0, "fm_6": 0,
|
||||
"ft_7": 1, "f_7": 500.0, "g_7": 1.26, "q_7": 1.41, "s_7": 0, "xm_7": 0, "fm_7": 0,
|
||||
"ft_8": 1, "f_8": 800.0, "g_8": 1.12, "q_8": 1.41, "s_8": 0, "xm_8": 0, "fm_8": 0,
|
||||
"ft_9": 1, "f_9": 1250.0, "g_9": 1.06, "q_9": 1.41, "s_9": 0, "xm_9": 0, "fm_9": 0,
|
||||
"ft_10": 1, "f_10": 2000.0, "g_10": 1.0, "q_10": 1.41, "s_10": 0, "xm_10": 0, "fm_10": 0,
|
||||
"ft_11": 1, "f_11": 3150.0, "g_11": 1.0, "q_11": 1.41, "s_11": 0, "xm_11": 0, "fm_11": 0,
|
||||
"ft_12": 1, "f_12": 5000.0, "g_12": 0.94, "q_12": 1.41, "s_12": 0, "xm_12": 0, "fm_12": 0,
|
||||
"ft_13": 1, "f_13": 8000.0, "g_13": 0.94, "q_13": 1.41, "s_13": 0, "xm_13": 0, "fm_13": 0,
|
||||
"ft_14": 1, "f_14": 12500.0, "g_14": 1.0, "q_14": 1.41, "s_14": 0, "xm_14": 0, "fm_14": 0,
|
||||
"ft_15": 1, "f_15": 20000.0, "g_15": 1.0, "q_15": 1.41, "s_15": 0, "xm_15": 0, "fm_15": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -37,8 +53,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
|
||||
@@ -50,20 +66,26 @@
|
||||
"name": "multiband_compressor",
|
||||
"control": {
|
||||
"enabled": 1,
|
||||
"mode": 0,
|
||||
"mode": 1,
|
||||
"g_in": 1.0,
|
||||
"g_out": 1.0,
|
||||
"g_out": 1.15,
|
||||
"g_dry": 0.0001,
|
||||
"g_wet": 1.0,
|
||||
"cbe_1": 1, "sf_1": 120.0,
|
||||
"cbe_2": 1, "sf_2": 400.0,
|
||||
"cbe_3": 1, "sf_3": 1000.0,
|
||||
"cbe_4": 0, "cbe_5": 0, "cbe_6": 0, "cbe_7": 0,
|
||||
"ce_0": 1, "at_0": 10.0, "rt_0": 100.0, "cr_0": 10.0, "kn_0": 0.15, "al_0": 0.142, "mk_0": 1.5, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
|
||||
"ce_1": 1, "at_1": 20.0, "rt_1": 200.0, "cr_1": 3.0, "kn_1": 0.5, "al_1": 0.226, "mk_1": 2.0, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
|
||||
"ce_2": 1, "at_2": 20.0, "rt_2": 200.0, "cr_2": 3.0, "kn_2": 0.5, "al_2": 0.226, "mk_2": 2.0, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
|
||||
"ce_3": 1, "at_3": 20.0, "rt_3": 200.0, "cr_3": 1.5, "kn_3": 0.5, "al_3": 0.398, "mk_3": 1.41, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
|
||||
"ce_4": 0, "ce_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,
|
||||
"ce_0": 1, "at_0": 5.0, "rt_0": 100.0, "cr_0": 30.0, "kn_0": 0.10, "al_0": 0.090, "mk_0": 1.0, "bth_0": 1.0, "bsa_0": 1.0, "scm_0": 0,
|
||||
"ce_1": 1, "at_1": 5.0, "rt_1": 90.0, "cr_1": 20.0, "kn_1": 0.12, "al_1": 0.095, "mk_1": 1.2, "bth_1": 1.0, "bsa_1": 1.0, "scm_1": 0,
|
||||
"ce_2": 1, "at_2": 4.0, "rt_2": 80.0, "cr_2": 16.0, "kn_2": 0.15, "al_2": 0.105, "mk_2": 1.3, "bth_2": 1.0, "bsa_2": 1.0, "scm_2": 0,
|
||||
"ce_3": 1, "at_3": 4.0, "rt_3": 70.0, "cr_3": 12.0, "kn_3": 0.18, "al_3": 0.115, "mk_3": 1.4, "bth_3": 1.0, "bsa_3": 1.0, "scm_3": 0,
|
||||
"ce_4": 1, "at_4": 4.0, "rt_4": 60.0, "cr_4": 10.0, "kn_4": 0.20, "al_4": 0.125, "mk_4": 1.5, "bth_4": 1.0, "bsa_4": 1.0, "scm_4": 0,
|
||||
"ce_5": 1, "at_5": 3.5, "rt_5": 50.0, "cr_5": 8.0, "kn_5": 0.25, "al_5": 0.140, "mk_5": 1.5, "bth_5": 1.0, "bsa_5": 1.0, "scm_5": 0,
|
||||
"ce_6": 1, "at_6": 3.0, "rt_6": 40.0, "cr_6": 6.0, "kn_6": 0.30, "al_6": 0.165, "mk_6": 1.4, "bth_6": 1.0, "bsa_6": 1.0, "scm_6": 0,
|
||||
"ce_7": 1, "at_7": 2.5, "rt_7": 30.0, "cr_7": 4.0, "kn_7": 0.40, "al_7": 0.300, "mk_7": 1.2, "bth_7": 1.0, "bsa_7": 1.0, "scm_7": 0
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -73,7 +95,7 @@
|
||||
"control": {
|
||||
"ingain": 0,
|
||||
"limit": -1,
|
||||
"release": 0.8
|
||||
"release": 0.35
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -117,7 +139,7 @@
|
||||
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
|
||||
],
|
||||
"channel": 0,
|
||||
"gain": 1.0
|
||||
"gain": 1.1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -131,7 +153,7 @@
|
||||
"/usr/share/t2-linux-audio/15_1/tweeters-96k.wav"
|
||||
],
|
||||
"channel": 0,
|
||||
"gain": 1.0
|
||||
"gain": 1.1
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -145,7 +167,7 @@
|
||||
"/usr/share/t2-linux-audio/15_1/woofers-96k.wav"
|
||||
],
|
||||
"channel": 0,
|
||||
"gain": 1.5
|
||||
"gain": 1.2
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -159,11 +181,33 @@
|
||||
"/usr/share/t2-linux-audio/15_1/woofers-96k.wav"
|
||||
],
|
||||
"channel": 0,
|
||||
"gain": 1.5
|
||||
"gain": 1.2
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "lv2",
|
||||
"name": "wlim",
|
||||
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
|
||||
"control": {
|
||||
"ingain": 0,
|
||||
"limit": -2,
|
||||
"release": 0.25
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "lv2",
|
||||
"name": "tlim",
|
||||
"plugin": "http://plugin.org.uk/swh-plugins/fastLookaheadLimiter",
|
||||
"control": {
|
||||
"ingain": 0,
|
||||
"limit": -1,
|
||||
"release": 0.15
|
||||
}
|
||||
}
|
||||
],
|
||||
"links": [
|
||||
{"output": "user_eq:out_l", "input": "equalizer:in_l"},
|
||||
{"output": "user_eq:out_r", "input": "equalizer:in_r"},
|
||||
{"output": "equalizer:out_l", "input": "virtualbass:in_l"},
|
||||
{"output": "equalizer:out_r", "input": "virtualbass:in_r"},
|
||||
{"output": "virtualbass:out_l", "input": "multiband_compressor:in_l"},
|
||||
@@ -177,17 +221,21 @@
|
||||
{"output": "copyL:Out", "input": "convLT:In"},
|
||||
{"output": "copyL:Out", "input": "convLW:In"},
|
||||
{"output": "copyR:Out", "input": "convRT:In"},
|
||||
{"output": "copyR:Out", "input": "convRW:In"}
|
||||
{"output": "copyR:Out", "input": "convRW:In"},
|
||||
{"output": "convLW:Out", "input": "wlim:in_1"},
|
||||
{"output": "convRW:Out", "input": "wlim:in_2"},
|
||||
{"output": "convLT:Out", "input": "tlim:in_1"},
|
||||
{"output": "convRT:Out", "input": "tlim:in_2"}
|
||||
],
|
||||
"inputs": [
|
||||
"equalizer:in_l",
|
||||
"equalizer:in_r"
|
||||
"user_eq:in_l",
|
||||
"user_eq:in_r"
|
||||
],
|
||||
"outputs": [
|
||||
"convLW:Out",
|
||||
"convRW:Out",
|
||||
"convLT:Out",
|
||||
"convRT:Out"
|
||||
"wlim:out_1",
|
||||
"wlim:out_2",
|
||||
"tlim:out_1",
|
||||
"tlim:out_2"
|
||||
],
|
||||
"capture.volumes": [
|
||||
{
|
||||
|
||||
76
mb-compressor-params.md
Normal file
76
mb-compressor-params.md
Normal file
@@ -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)`.
|
||||
Reference in New Issue
Block a user