feat(ballast): add Ballast switch-up leveller
Ballast gives every part of a bassline one gain that brings it to the target loudness, with no compression inside a part. Parts start at note starts, detected from the audio or taken from MIDI, or on the host's beat grid, so a patch change on the same note counts too. - Loudness is K-weighted over each part's first 100 ms, in 10 ms blocks with a relative gate, and every part is weighted from rest so the last part does not ring into it. - The audio path is delayed by the tracker's lookahead, the window and a 5 ms glide, so each gain is in place on its part's first sample. - Learn takes the next part's loudness as the target. Amount, Range, Measure from (leave the sub out), Gate, Output, Bypass. - Editor shows the last 32 parts in and out, the target, and the spread of the last 16 parts. - Registered in the catalog, workflows, editor-check, README, chain and development docs, and the website copy.
This commit is contained in:
1 parent
d77b88c7a9
commit
d93dd942a8
28 files changed
+4716
-21
No files matched your search
@@ -0,0 +1,17 @@
|
||||
# Ballast changelog
|
||||
|
||||
## Unreleased (0.1.0)
|
||||
|
||||
- Introduce Ballast, a switch-up leveller: every part of a bassline gets one
|
||||
gain that brings it to the Target loudness. No compression inside a part.
|
||||
- Parts start at note starts (detected from the audio, or from MIDI) or on the
|
||||
host's beat grid, from 1 bar to 1/16.
|
||||
- Loudness is K-weighted (ITU-R BS.1770) over each part's first 100 ms, in
|
||||
10 ms blocks with a relative gate, and each part is weighted from rest.
|
||||
- The gain glides over 5 ms and is in place on the part's first sample.
|
||||
- **Learn** takes the next part's loudness as the target. **Amount**,
|
||||
**Range**, **Measure from** (leave the sub out of the measurement), **Gate**,
|
||||
**Output**, **Bypass**.
|
||||
- Editor: the last 32 parts, their loudness in and out, the target, and the
|
||||
spread of the last 16 parts.
|
||||
- About 172 ms of latency: the tracker's lookahead, the window and the glide.
|
||||
@@ -0,0 +1,80 @@
|
||||
# Ballast
|
||||
|
||||
Ballast levels every note or switch-up of a bassline to one loudness, with a
|
||||
single gain for each. Current version **0.1.0** (unreleased).
|
||||
|
||||
Riddim and dubstep basslines change patch every few beats. Patches at the same
|
||||
peak level still differ in how loud they sound, so each switch-up lands at a
|
||||
different loudness and gets hand-set gain or clipping. A compressor would even
|
||||
them out but also change their envelope and punch. Ballast measures each part's
|
||||
loudness and gives the whole part one gain: inside a part nothing moves.
|
||||
|
||||

|
||||
|
||||
## Install on Windows
|
||||
|
||||
Copy `Ballast.clap` into `C:\Program Files\Common Files\CLAP\`. In FL Studio
|
||||
2024.1 or newer, open Options → Manage plugins, enable Verify plugins, and Find
|
||||
installed plugins. Load Ballast in the mixer slot of the bass, after its sound
|
||||
design. Keep the font license with redistributed packages.
|
||||
|
||||
## Work with it
|
||||
|
||||
1. Choose where a part starts with **Parts from**:
|
||||
- **Notes**: every note start, detected from the audio, or from MIDI notes
|
||||
sent to the effect (**Notes from**: AUDIO or MIDI).
|
||||
- **Grid**: every line of the host's beat grid, from 1 bar to 1/16. Use it
|
||||
when a switch-up changes the patch on the same note: the note tracker does
|
||||
not see a new note there.
|
||||
2. Play the bassline and press **LEARN** just before the switch-up that sits
|
||||
right in the mix. Its loudness becomes the **Target**. You can also set
|
||||
Target by hand.
|
||||
3. Watch the display: one bar per part with its loudness as it came in, and a
|
||||
mark where Ballast puts it. The marks line up on the target line.
|
||||
4. Lower **Amount** to keep some of the differences: 50 % halves them.
|
||||
|
||||
The panel shows the spread of the last 16 parts, in and out: the standard
|
||||
deviation of their loudness in dB. In the editor preview four patches spread
|
||||
by 7 dB come out within 0.4 dB; the rest is what Range left.
|
||||
|
||||
Other controls:
|
||||
|
||||
- **Range** is the most boost or cut a part gets.
|
||||
- **Measure from**: the measurement hears only what is above this frequency;
|
||||
the gain still applies to the whole signal. When Anvil makes the sub on the
|
||||
bus, set it to Anvil's Split: Anvil replaces the sub anyway, so only the part
|
||||
above it should decide the gain.
|
||||
- **Gate** is the note-start threshold (Audio). A part quieter than the gate
|
||||
keeps the last part's gain instead of being lifted, so rests and tails are
|
||||
not pulled up.
|
||||
- **Output** trims the level; **Bypass** crossfades to the delayed input.
|
||||
|
||||
### How it measures
|
||||
|
||||
A part's loudness is its K-weighted level (ITU-R BS.1770, the weighting behind
|
||||
LUFS) over its first 100 ms, or over all of it when it is shorter. The level is
|
||||
counted in 10 ms blocks, and blocks 20 dB under the part's loudest block are
|
||||
left out. Each part is weighted from rest, as if it played alone, so the last
|
||||
part's level does not ring into it.
|
||||
|
||||
The gain glides to the new value over 5 ms and is in place on the part's first
|
||||
sample, so the attack already has the new level. Later parts of a long note keep
|
||||
that gain: Ballast does not follow the note's envelope.
|
||||
|
||||
## Timing and limits
|
||||
|
||||
Latency is the note tracker's lookahead (about 67 ms), the 100 ms measuring
|
||||
window and the 5 ms glide, about 172 ms in all, reported to the host for delay
|
||||
compensation. That is too much for playing live through Ballast: add it for the
|
||||
mix.
|
||||
|
||||
In Grid mode the grid follows the host's beat position while it plays and runs
|
||||
on at the last tempo when it stops. A note start found by a change of pitch can
|
||||
come about 20 ms early, inside the gap before the note;
|
||||
the block gate keeps that gap out of the measurement. Two parts closer than
|
||||
10 ms are merged.
|
||||
|
||||
Ballast decides from the first 100 ms of a part. A patch that swells after
|
||||
that, or a note whose loudness depends on what plays with it, is levelled by
|
||||
its start only. It levels loudness, not brightness: two patches at the same
|
||||
loudness can still differ in tone.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 256 KiB |
+10
-5
@@ -1,13 +1,13 @@
|
||||
# Signal chain
|
||||
|
||||
The order of the Bass Foundry plugins in a chain matters. Anvil, Lathe, Temper,
|
||||
Knurl, Planish and Braze each detect the pitch from the audio they receive. If a plugin earlier
|
||||
Knurl, Planish, Braze and Ballast each detect the pitch from the audio they receive. If a plugin earlier
|
||||
in the chain changes the audio, the pitch detection after it can go wrong.
|
||||
|
||||
## Recommended order
|
||||
|
||||
```text
|
||||
bass track: synth → Lathe → Temper → Planish → Braze → Knurl → Patina → Weld
|
||||
bass track: synth → Lathe → Temper → Planish → Braze → Knurl → Patina → Ballast → Weld
|
||||
bass bus: Anvil → (bus compression) → Quench → Crucible
|
||||
master: … → Quench (last, or before a true-peak limiter)
|
||||
```
|
||||
@@ -23,7 +23,12 @@ master: … → Quench (last, or before a true-peak limiter)
|
||||
it reads Loose, move Braze to the bus after Anvil, with Low cut at or above
|
||||
Anvil's Split. There it reads the pitch from Anvil's clean sub, and the sub
|
||||
stays out of the beating.
|
||||
- **Knurl last among the tracked plugins.** See below.
|
||||
- **Ballast after the sound design.** It levels what the patch finally sounds
|
||||
like, so it comes after the distortion, OTT and Patina. It finds note starts
|
||||
like the other tracked plugins: after Knurl, use MIDI or Grid. Weld and Anvil
|
||||
come after it. On the bus with Anvil, set Measure from to Anvil's Split.
|
||||
- **Knurl last among the pitch-tracked plugins.** See below. Ballast only
|
||||
needs note starts; after Knurl, run it from MIDI or the grid.
|
||||
- **Patina after the tracked plugins.** It does not detect pitch, so the order
|
||||
does not change its result.
|
||||
- **Weld after every plugin with latency on its track.** Weld can delay its own
|
||||
@@ -58,8 +63,8 @@ and route the bass's MIDI notes to it.
|
||||
## Latency
|
||||
|
||||
Each tracked plugin adds its own lookahead: Anvil 80 ms, Lathe, Temper,
|
||||
Planish and Braze about 67 ms, Knurl about 71 ms, Patina about 45 ms, Quench
|
||||
under 1 ms. A full chain is
|
||||
Planish and Braze about 67 ms, Knurl about 71 ms, Patina about 45 ms, Ballast
|
||||
about 172 ms, Quench under 1 ms. A full chain is
|
||||
about 400 ms. FL
|
||||
Studio compensates this for playback and rendering, but playing a MIDI keyboard
|
||||
through the full chain is not practical. Write with a shorter chain and add the
|
||||
|
||||
+4
-2
@@ -25,7 +25,8 @@ Patina has `render` for offline WAV evaluation. The shared crates are:
|
||||
|
||||
- `common-ui`: palette, embedded fonts, header, panels, knobs, meters.
|
||||
- `common-dsp`: filters, YIN pitch tracking, the note tracker used by Anvil,
|
||||
Lathe, Temper, Knurl, Planish and Braze, 4× oversampling (also Quench's), meters and WAV files.
|
||||
Lathe, Temper, Knurl, Planish, Braze and Ballast, 4× oversampling (also Quench's),
|
||||
BS.1770 K-weighting (Ballast), meters and WAV files.
|
||||
|
||||
The wrappers stay separate workspaces, each with its own lockfile, so the
|
||||
NIH-plug Git dependency stays out of the root workspace. Host identities and
|
||||
@@ -45,7 +46,7 @@ python3 -m unittest discover -s scripts/tests -v
|
||||
```
|
||||
|
||||
Products: `rivet`, `patina`, `anvil`, `crucible`, `lathe`, `temper`, `weld`,
|
||||
`knurl`, `planish`, `braze`, `quench`.
|
||||
`knurl`, `planish`, `braze`, `quench`, `ballast`.
|
||||
|
||||
`check-plugin.py` runs formatting, tests and clippy for one product's packages
|
||||
and its wrapper, without compiling the other products. Run it for every product
|
||||
@@ -147,6 +148,7 @@ identify plugins by CLAP ID, so the vendor name does not affect saved projects.
|
||||
| Planish | (new) | `io.narl.bass-foundry.planish` |
|
||||
| Braze | (new) | `io.narl.bass-foundry.braze` |
|
||||
| Quench | (new) | `io.narl.bass-foundry.quench` |
|
||||
| Ballast | (new) | `io.narl.bass-foundry.ballast` |
|
||||
|
||||
CLAP IDs use the form `io.narl.bass-foundry.<product>`. Builds tagged before
|
||||
this scheme used `io.storyboard.*` IDs; hosts treat the new IDs as different
|
||||
|
||||
+17
-2
@@ -263,8 +263,8 @@ one period of the note, so heavy settings do not ripple at f0. See [Planish](pla
|
||||
Patina already cut fixed formants on every note, but it also cut a boosted
|
||||
harmonic as hard (5.7 dB), at a position that moved with the note.
|
||||
Open: MIDI as the pitch source.
|
||||
3. Planish (done, released 0.1.0), then Quench (done, unreleased) and Ballast:
|
||||
they fill the mix and master gap.
|
||||
3. Planish (done, released 0.1.0), then Quench and Ballast (both done,
|
||||
unreleased): they fill the mix and master gap.
|
||||
- In parallel, from the research findings, in this order: Weld delay
|
||||
spread (done, Weld 0.3.0); the fatness lane, as the first Assay piece
|
||||
(prototype `scripts/fatness.py`); Anvil Ghost harmonics and Phone listen
|
||||
@@ -376,6 +376,21 @@ the same loudness without compression changing its character. Earlier working
|
||||
name: Hit Leveler. Pairs with Anvil: Anvil levels the sub, Ballast the whole
|
||||
patch.
|
||||
|
||||
Built (unreleased 0.1.0, see [Ballast](ballast/README.md)): one gain per part,
|
||||
parts from note starts or the host grid (a patch change on the same note is not
|
||||
a new note to the tracker), K-weighted loudness of the first 100 ms, Learn for
|
||||
the reference. Found while building it:
|
||||
|
||||
- A note start from a pitch change comes about 20 ms early, in the gap before
|
||||
the note; a plain mean over the window then reads the part ~0.9 dB quiet.
|
||||
Fixed with 10 ms blocks and a relative gate (−20 dB under the loudest block).
|
||||
- The K-weighting filters ring the last part into the next one: a near-silent
|
||||
part after a loud one read −18 LUFS instead of under −60. Fixed by weighting
|
||||
every part from rest.
|
||||
- Next ideas: a brightness match next to loudness (problem 2 names both; with
|
||||
Lathe or Patina doing the EQ), a "follow" option for long notes that swell,
|
||||
and switch-up detection from timbre change instead of the grid.
|
||||
|
||||
### Bellows — pitch-tracked resonator
|
||||
|
||||
A comb or Karplus-style resonator tuned to the detected note. It turns noise,
|
||||
|
||||
Reference in new issue
Block a user