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.
218 lines
12 KiB
Markdown
218 lines
12 KiB
Markdown
# Bass Foundry
|
||
|
||
**Rust CLAP plugins for writing, designing and mixing bass music in FL Studio.**
|
||
|
||
Website: [fl.narl.io](https://fl.narl.io)
|
||
|
||

|
||

|
||

|
||

|
||

|
||
|
||
Riddim and dubstep basses change patch on every switch-up. Fixed-frequency tools
|
||
then act differently on every note: the sub jumps, an EQ cut lands on another
|
||
harmonic, the distortion gets fizzy on one note and dull on the next. Most plugins
|
||
in this suite follow the played note, so a setting means the same thing on every
|
||
note. Each plugin is a separate product with its own version, guide and release.
|
||
|
||
## Plugins
|
||
|
||
| Plugin | What it does | Load it on | Latency | Docs |
|
||
| --- | --- | --- | --- | --- |
|
||
| **[Rivet](docs/rivet/README.md)** | Tells a MIDI motif as a story on a storyboard of roles and joins the seams into one phrase | Channel Rack | none | [Guide](docs/rivet/README.md) · [Changes](docs/rivet/CHANGELOG.md) |
|
||
| **[Patina](docs/patina/README.md)** | Smooths harsh, ringing resonances while plain harmonic tone and transients pass | Any mixer track | one analysis frame | [Guide](docs/patina/README.md) · [Changes](docs/patina/CHANGELOG.md) |
|
||
| **[Anvil](docs/anvil/README.md)** | Replaces the sub with a clean mono sine: one level and one start phase on every note, locked to the kick | Bass bus, kick as sidechain | 80 ms | [Guide](docs/anvil/README.md) · [Changes](docs/anvil/CHANGELOG.md) |
|
||
| **[Crucible](docs/crucible/README.md)** | Records the last bars all the time, then slices, cleans and exports key-labelled samples with one button | End of the chain to resample | none | [Guide](docs/crucible/README.md) · [Changes](docs/crucible/CHANGELOG.md) |
|
||
| **[Lathe](docs/lathe/README.md)** | EQ with one band on each harmonic of the played note: a cut on harmonic 3 stays on harmonic 3 | Bass, after the synth | ≈ 67 ms | [Guide](docs/lathe/README.md) · [Changes](docs/lathe/CHANGELOG.md) |
|
||
| **[Temper](docs/temper/README.md)** | Distortion that drives every note the same: level and tone filters follow the note | Bass | ≈ 67 ms + oversampling | [Guide](docs/temper/README.md) · [Changes](docs/temper/CHANGELOG.md) |
|
||
| **[Weld](docs/weld/README.md)** | Phase-aligns one bass layer to another layer through the sidechain, so the layers sum in the mids instead of cancelling | One layer's track, other layer as sidechain | none | [Guide](docs/weld/README.md) · [Changes](docs/weld/CHANGELOG.md) |
|
||
| **[Knurl](docs/knurl/README.md)** | Gives every cycle of the note its own level, timing and grit: in-tune subharmonic growl, rattle and fry on every note | Bass | ≈ 71 ms | [Guide](docs/knurl/README.md) · [Changes](docs/knurl/CHANGELOG.md) |
|
||
| **[Planish](docs/planish/README.md)** | Upward and downward compression (OTT) in bands set by harmonic number: band 2 holds harmonics 2–4 on every note | Bass | ≈ 67 ms | [Guide](docs/planish/README.md) · [Changes](docs/planish/CHANGELOG.md) |
|
||
| **[Braze](docs/braze/README.md)** | Adds a detuned copy whose beating stays on the tempo grid on every note: reese movement in time | Bass | ≈ 67 ms | [Guide](docs/braze/README.md) · [Changes](docs/braze/CHANGELOG.md) |
|
||
| **[Quench](docs/quench/README.md)** | Clips the top as hard as you like while the sub passes clean: the low end keeps its level under a loud master | Master or bass bus, last | < 1 ms (oversampling) | [Guide](docs/quench/README.md) · [Changes](docs/quench/CHANGELOG.md) |
|
||
| **[Ballast](docs/ballast/README.md)** | Levels every note or switch-up to one loudness with a single gain each: no compression inside a part | Bass, after the sound design | ≈ 172 ms | [Guide](docs/ballast/README.md) · [Changes](docs/ballast/CHANGELOG.md) |
|
||
|
||
Plugins with latency report it to the host, so FL Studio compensates the delay.
|
||
Anvil, Lathe, Temper, Knurl, Planish, Braze and Ballast detect notes from the audio. They can also take the
|
||
pitch from MIDI. The order of the plugins matters: see [Signal chain](docs/chain.md).
|
||
|
||
The names come from metalwork: a **rivet** joins pieces; **patina** changes a
|
||
surface and keeps its character; an **anvil** is the solid base that everything
|
||
is struck on; a **crucible** melts material down to be cast again; a **lathe**
|
||
turns a piece so each cut lands the same all the way round; **tempering** sets
|
||
how hard and how tough a metal ends up; a **weld** fuses two pieces into one;
|
||
**knurling** presses a ridged grip pattern into a surface; **planishing**
|
||
hammers a surface smooth and even; **brazing** joins two metals with a filler; **quenching** hardens hot metal in one
|
||
fast step; **ballast** is the weight that keeps a ship level.
|
||
The suite is the **foundry**: the workshop where the bass gets cast, shaped and
|
||
finished. Hosts list the plugins under the vendor name *Bass Foundry*.
|
||
|
||
## Editors
|
||
|
||
<table>
|
||
<tr>
|
||
<td width="50%"><a href="docs/rivet/README.md"><img src="docs/rivet/assets/editor.png" alt="Rivet editor"></a><br><b>Rivet</b>: storyboard composition</td>
|
||
<td width="50%"><a href="docs/patina/README.md"><img src="docs/patina/assets/editor.png" alt="Patina editor"></a><br><b>Patina</b>: resonance smoothing</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/anvil/README.md"><img src="docs/anvil/assets/editor.png" alt="Anvil editor"></a><br><b>Anvil</b>: sub anchor</td>
|
||
<td><a href="docs/crucible/README.md"><img src="docs/crucible/assets/editor.png" alt="Crucible editor"></a><br><b>Crucible</b>: resampler</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/lathe/README.md"><img src="docs/lathe/assets/editor.png" alt="Lathe editor"></a><br><b>Lathe</b>: harmonic EQ</td>
|
||
<td><a href="docs/temper/README.md"><img src="docs/temper/assets/editor.png" alt="Temper editor"></a><br><b>Temper</b>: tracked distortion</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/weld/README.md"><img src="docs/weld/assets/editor.png" alt="Weld editor"></a><br><b>Weld</b>: layer phase alignment</td>
|
||
<td><a href="docs/knurl/README.md"><img src="docs/knurl/assets/editor.png" alt="Knurl editor"></a><br><b>Knurl</b>: cycle texture</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/planish/README.md"><img src="docs/planish/assets/editor.png" alt="Planish editor"></a><br><b>Planish</b>: harmonic OTT</td>
|
||
<td><a href="docs/braze/README.md"><img src="docs/braze/assets/editor.png" alt="Braze editor"></a><br><b>Braze</b>: tempo-locked beating</td>
|
||
</tr>
|
||
<tr>
|
||
<td><a href="docs/quench/README.md"><img src="docs/quench/assets/editor.png" alt="Quench editor"></a><br><b>Quench</b>: sub-safe clipper</td>
|
||
<td><a href="docs/ballast/README.md"><img src="docs/ballast/assets/editor.png" alt="Ballast editor"></a><br><b>Ballast</b>: switch-up leveller</td>
|
||
</tr>
|
||
</table>
|
||
|
||
The previews are rendered headless from the real editor code by
|
||
`tools/editor-check`, so they match what the plugin draws.
|
||
|
||
## Install
|
||
|
||
1. Open the plugin's release on Gitea. Every plugin has its own release tag,
|
||
for example `lathe-v0.1.0`.
|
||
2. Download `<plugin>-windows-x86_64-<version>.zip`. You can check it against
|
||
the `.zip.sha256` file next to it.
|
||
3. Extract the ZIP and copy the `.clap` file into:
|
||
|
||
```text
|
||
C:\Program Files\Common Files\CLAP\
|
||
```
|
||
|
||
4. In FL Studio 2024.1 or newer, open **Options → Manage plugins**, enable
|
||
**Verify plugins**, and click **Find installed plugins**.
|
||
5. Load Rivet in the Channel Rack. Load the other plugins in mixer effect slots.
|
||
|
||
Keep the font license (`licenses/`) with redistributed packages.
|
||
|
||
Every branch and pull-request pipeline also builds a combined Windows preview
|
||
ZIP, `bass-foundry-windows-x86_64.zip`, in its **windows** job. Use it to test unreleased changes.
|
||
|
||
> **Upgrading from an earlier build?** The CLAP IDs changed from
|
||
> `io.storyboard.*` to `io.narl.bass-foundry.<product>`, so hosts see each
|
||
> plugin as new. Delete the old `.clap` files, rescan, and add the plugins again
|
||
> in existing projects. Motif Storyboard and Resonance Compressor are now Rivet
|
||
> and Patina, and the vendor is "Bass Foundry".
|
||
|
||
## Build from source
|
||
|
||
You need:
|
||
|
||
- Rust stable and Python 3.11 or newer.
|
||
- For the Windows target on Linux: the `x86_64-pc-windows-gnu` Rust target and
|
||
MinGW (`x86_64-w64-mingw32-gcc`).
|
||
- For the native wrapper tests on Linux: pkg-config and the X11/XCB and OpenGL
|
||
development headers. Rivet's optional standalone also needs ALSA/JACK headers.
|
||
|
||
```sh
|
||
# Tests for all engines, editors and tools
|
||
cargo test --workspace --locked
|
||
|
||
# Formatting, tests and lints for one product, including its CLAP wrapper
|
||
python3 scripts/check-plugin.py --plugin lathe
|
||
|
||
# Windows build, then the ZIPs (all plugins, or --plugin <name>)
|
||
python3 scripts/build-plugin.py --plugin lathe --target x86_64-pc-windows-gnu
|
||
python3 scripts/package-windows.py
|
||
|
||
# Headless editor preview
|
||
cargo run --manifest-path tools/editor-check/Cargo.toml -- target/editor/lathe.png --plugin lathe
|
||
```
|
||
|
||
Plugin names for the scripts are `rivet`, `patina`, `anvil`, `crucible`,
|
||
`lathe`, `temper`, `weld`, `knurl`, `planish`, `braze`, `quench` and `ballast`. Builds go to `dist/<target>/<Name>.clap`. No
|
||
script installs anything into a DAW. [Development and testing](docs/development.md)
|
||
has the full command list.
|
||
|
||
## Quality checks
|
||
|
||
Every push and pull request runs these checks in CI
|
||
(`.gitea/workflows/check.yml`):
|
||
|
||
- **Engine tests.** Every engine has tests for its signal behaviour and latency
|
||
against measured targets.
|
||
- **Realtime safety.** Every audio effect has a test that fails if the audio
|
||
thread allocates memory.
|
||
- **Host tests.** Each wrapper is loaded through the exported CLAP ABI, without
|
||
a DAW. The tests check the ports, the reported latency, state save and load,
|
||
and processing at 44.1, 48 and 96 kHz.
|
||
- **Lints.** `rustfmt` and `clippy -D warnings` for every package and wrapper.
|
||
- **Editor previews.** All editors are rendered headless and kept as CI
|
||
artifacts.
|
||
- **Windows builds.** Every `.clap` is cross-compiled and packaged.
|
||
|
||
Release ZIPs are deterministic and contain checksums for every file. Things CI
|
||
cannot check stay manual: FL Studio routing, native window behaviour, and
|
||
listening.
|
||
|
||
## Repository
|
||
|
||
```text
|
||
plugins/
|
||
catalog.json Product metadata for the build, check and release tools
|
||
rivet/ core · midi · app · preview · plugin
|
||
patina/ core · ui · render · plugin
|
||
anvil/ core · ui · plugin
|
||
crucible/ core · ui · plugin
|
||
lathe/ core · ui · plugin
|
||
temper/ core · ui · plugin
|
||
weld/ core · ui · plugin
|
||
knurl/ core · ui · plugin
|
||
planish/ core · ui · plugin
|
||
braze/ core · ui · plugin
|
||
quench/ core · ui · plugin
|
||
ballast/ core · ui · plugin
|
||
common/
|
||
ui/ Shared look: palette, embedded fonts, knobs, panels, meters
|
||
dsp/ Shared filters, pitch tracking, oversampling, meters, WAV files
|
||
tools/editor-check/ Headless editor renderer for visual QA
|
||
scripts/ Build, check, packaging, release and analysis tools
|
||
docs/<plugin>/ Guide, changelog and editor preview for each plugin
|
||
docs/ideas.md Problems in bass-music production and the plugin ideas for them
|
||
references/ Local reference audio; never distributed
|
||
website/ The fl.narl.io site: Astro, nginx container, reads catalog and previews
|
||
```
|
||
|
||
Each product follows the same split:
|
||
|
||
- `core` is the engine. It is plain Rust, does not depend on a plugin SDK, and
|
||
is tested on its own.
|
||
- `ui` is the egui editor, built on `common-ui`.
|
||
- `plugin` is the CLAP wrapper on a pinned NIH-plug revision.
|
||
|
||
The root Cargo workspace holds the engines, editors and tools. Each wrapper is
|
||
its own workspace with its own lockfile, so SDK dependencies stay separate.
|
||
`plugins/catalog.json` is the single list of products. The scripts and CI read
|
||
it, so adding a plugin starts there.
|
||
|
||
## Releases
|
||
|
||
Each plugin is versioned and released on its own. Its version is the `version`
|
||
in `plugins/<plugin>/plugin/Cargo.toml`. A tag `<plugin>-v<version>`, for example
|
||
`temper-v0.1.0`, starts the release workflow. The workflow builds, checks and
|
||
publishes that plugin's Windows ZIP to a Gitea release. See
|
||
[docs/releases.md](docs/releases.md).
|
||
|
||
## Roadmap
|
||
|
||
[docs/ideas.md](docs/ideas.md) lists the production problems the suite targets,
|
||
the released plugins and the ideas for the next ones.
|
||
|
||
## License
|
||
|
||
ISC, see [LICENSE](LICENSE). The embedded Roboto Condensed fonts are under the
|
||
SIL Open Font License ([OFL.txt](plugins/common/ui/assets/OFL.txt)).
|