tympan-ladspa
A Rust framework for writing LADSPA plugins.
tympan-ladspa provides Rust abstractions over the LADSPA (Linux Audio
Developer's Simple Plugin API) C interface, enabling Rust applications to
implement audio plugins suitable for hosts such as PipeWire's filter-chain,
Ardour, and other LADSPA-compatible processors.
Status
Intended functionality complete. Every "In scope" item from
docs/overview.md is implemented, and every
non-speculative entry in
ADR 0005 (the
tiered CI plan) is wired up:
Framework
- Layer 1 — low-level LADSPA FFI (
src/raw/). - Layer 2 — realtime primitives (
src/realtime/): theRealtimeContexttype-level marker, a lock-free SPSC ring buffer, and aLogSinkwrapper packaging the "log from realtime, drain off-thread" pattern. - Layer 3 — user-facing API:
Plugintrait,PortswithPortDescriptorbuilders, and theplugin_entry!declarative macro that exports a LADSPAladspa_descriptorentry point.
Examples
Three reference plugins under examples/:
gain/— minimal linear gain; alloc-free invariant pinned in CI.noise-gate/— hysteresis gate; multi-control + per-instance state demonstration.delay/— feedback delay; demonstratesLogSinkend-to-end.
CI
| Tier | Checks |
|---|---|
| 1 | cargo fmt --check, cargo clippy --workspace --all-targets --all-features -- -D warnings, cargo build/test on stable and MSRV 1.75, nm -D symbol audit |
| 2 | ladspa-sdk integration: analyseplugin (metadata round-trip) and applyplugin (full lifecycle + numerical check) for every example; assert_no_alloc integration test pinning CLAUDE.md Prohibition 1 on Plugin::run; AddressSanitizer on the workspace (nightly + -Zbuild-std) |
| 3 | ThreadSanitizer (nightly + -Zbuild-std) — validates the SPSC ring buffer's Acquire/Release ordering on the 50 000-item concurrent exchange; cargo-deny supply-chain hygiene (advisories + licences + bans + sources) |
The framework is usable: write impl Plugin for MyPlugin and
tympan_ladspa::plugin_entry!(MyPlugin) in a cdylib crate and the
resulting .so loads in any LADSPA host. See
examples/gain/ for the minimal recipe and
docs/plugin-author-guide.md for the
collected practical recipes (panic strategy, UNIQUE_ID,
symbol visibility, realtime debugging, common pitfalls).
Future work
The two ADR 0005 Tier 3 items not yet on CI are documented as
deferred in the ADR itself: the strace syscall allow-list
(fragile in the presence of glibc / kernel updates) and the
criterion regression bench (no realtime-path baseline yet — the
current run() is a thin slice iterator). Both have re-evaluation
triggers spelled out in
ADR 0005 § Trigger for revisiting.
A multi-plugin cdylib variant of plugin_entry! is sketched in
src/macros.rs's docstring; not implemented
because no in-tree consumer currently needs it.
Naming
Tympan — the tympanal organ of moths, a membrane-based ultrasound sensor on the abdomen of pyralid and noctuid moths. Evolved to detect the echolocation calls of bats. The name reflects the library's role: a thin membrane between the host audio engine and user-space Rust code.
Quickstart for plugin authors
Add a new cdylib crate that depends on this framework, declare your
plugin, and invoke the entry-point macro:
# Cargo.toml
[]
= "my-plugin"
= "0.1.0"
= "2021"
[]
= ["cdylib"]
[]
= "0.1"
// src/lib.rs
use ;
;
plugin_entry!;
Build with cargo build --release and copy
target/release/libmy_plugin.so to ~/.ladspa/. See
examples/gain/ for the same shape as a complete
crate.
Development
The project's CI runs cargo fmt --check, cargo clippy --workspace --all-targets -- -D warnings, cargo test, and an examples job
that drives every example plugin end-to-end through the LADSPA SDK
(.github/workflows/ci.yml;
ADR 0005 records
the tiered verification strategy).
To run the same fmt and clippy checks locally before every git push,
opt into the repository's pre-push hook:
The hook lives in .githooks/pre-push. It is a
no-op when no *.rs, Cargo.toml, or Cargo.lock files changed in
the pushed range, so documentation-only pushes are not slowed down.
Bypass it for a single push with git push --no-verify.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Contribution
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.
Documentation
| Doc | Content |
|---|---|
docs/overview.md |
Project purpose, scope, comparison to existing implementations |
docs/architecture.md |
API design and module layout |
docs/plugin-author-guide.md |
Practical recipes for writing a plugin on top of the framework |
docs/references.md |
LADSPA spec, PipeWire integration, prior art |
docs/decisions/ |
Architectural Decision Records |
docs/handoff-protocol.md |
Session handoff protocol for long-running work |
Examples
| Example | Description |
|---|---|
examples/gain/ |
Minimal linear-gain plugin. Smallest viable consumer of the framework. Pinned alloc-free in CI. |
examples/noise-gate/ |
Hysteresis noise gate. Demonstrates multi-control input, per-instance state, and DSP logic factored into a pure function for unit testing. |
examples/delay/ |
Feedback delay line. Pre-allocates a Vec<f32> ring in instantiate, resets state in activate, exercises three control ports and the audio_in_out escape hatch. |