Manguebyte

Open-source audio framework for Rust

Tested, not promised.

Manguebyte builds audio plugins and applications in Rust: DSP and instruments, metering, MIDI, files, devices, plugin editors and hosting, and native CLAP, VST 3 and LV2 export from one line each. Its goal is to be for Rust what JUCE is for C++, and then to go further. Every claim on this site links to the test that proves it.

Get started Read the source

Young project. The API will change before 1.0. Plugin editors open in Win32 windows and are tested in CI, but nobody has opened one in a host by hand yet. See what is missing.

Manguebyte: a crab wearing headphones and playing an electric guitar

01

Four things it proves on every commit

Matching JUCE is the baseline. These are places where Manguebyte already does something JUCE 9.0.4 does not, each one backed by a test that runs in CI. The full list has fifteen, with a source for everything said about JUCE and the limit of every claim.

  1. Automation lands on the exact sample

    Every parameter change arrives with the frame it falls on and is applied on that frame: in the test harness, through CLAP, and through VST 3, where every point of every automation queue is delivered.

    In JUCE 9.0.4
    A processor reads each parameter once per block.
    Limit
    A processor chooses whether to use the timing. Reading a value once per block is still valid, and cheaper.

    Proof

  2. The same sound at any block size

    One song rendered with six block patterns, from a steady 64 frames to an irregular 1, 511, 7, 300, 64, 2, 129, must match bit for bit. The first time this test ran, it failed, and exposed a real bug.

    In JUCE 9.0.4
    Whether the sound depends on the host’s buffer size is up to each plugin.
    Limit
    It covers what follows events. A value read once per block changes at block boundaries by definition.

    Proof

  3. No heap on the audio thread, or CI fails

    The tests replace the global allocator with one that counts. If any processor, or the CLAP wrapper, allocates or frees while processing, the build is red. It has already caught a mutex that allocates on macOS.

    In JUCE 9.0.4
    Real-time safety is a rule in the documentation.
    Limit
    A test that runs, not a compile-time proof. It catches heap use on the paths the tests exercise.

    Proof

  4. CLAP and VST 3 from one line each, checked by their own validators

    export_clap! and export_vst3! turn the same plugin into both formats. CI runs the CLAP project’s validator and Steinberg’s on an instrument and an effect; all 537 VST 3 tests pass on Windows, Linux and macOS. Both were also tested by hand in REAPER, and export_lv2! adds a third format.

    In JUCE 9.0.4
    CLAP export is not available natively.
    Limit
    Editors are tested in CI, not yet opened in a host by hand. LV2 is new: lilv loads it in CI, no host has run it yet. No AU or AAX.

    Proof

All fifteen claims, with proofs and limits

02

A whole plugin, then one line per format

This is the example effect from the repository: a sidechain ducker. It describes itself, splits each block at its events, and is exported twice. None of the code below was written for this page.

  1. One description

    INFO and LAYOUT are constants. The same description becomes the CLAP descriptor, the VST 3 class, whose id is the same on every platform, and the LV2 plugin’s Turtle files.

  2. Events on their frame

    split_at_events renders up to each event, applies it and carries on, without copying or allocating. That call is all a plugin does to get sample-accurate automation.

  3. Three formats, three lines

    The macros are the whole integration, and editor hands the wrappers the plugin’s editor. cargo mangue then builds a .clap, a .vst3 and an .lv2 bundle in each system’s layout.

Rust
impl Plugin for Duck {
    const INFO: PluginInfo = PluginInfo {
        id: "org.manguebyte.duck",
        name: "Mangue Duck",
        vendor: "Manguebyte",
        version: env!("CARGO_PKG_VERSION"),
        description: "A sidechain ducker, as an example of the Manguebyte framework",
        kind: PluginKind::Effect,
    };
    const LAYOUT: AudioLayout = AudioLayout {
        inputs: 2,
        outputs: 2,
        sidechain: 2,
    };
    type Params = DuckParams;
    // ... new, params and prepare are left out here
    fn process(&mut self, buffer: &mut AudioBuffer<f32>, context: &ProcessContext<'_, f32>) {
        self.cursor = 0;
        let sidechain = context.sidechain;
        split_at_events(self, buffer, context.events, Self::handle, |duck, piece| {
            duck.render(piece, sidechain)
        });
        self.publish_reduction();
    }
    // ... reset
    fn editor(&self) -> Option<Box<dyn EditorSpec>> {
        Some(Box::new(self.editor_spec()))
    }
}

manguebyte::clap::export_clap!(Duck);
manguebyte::vst3::export_vst3!(Duck);
manguebyte::lv2::export_lv2!(Duck);
Excerpt from plugins/duck/src/lib.rs at commit c5835bf. Lines beginning // ... mark what is left out.

And a test of it, with no host

Harness plays audio, a sidechain and automation into the plugin inside an ordinary unit test. This one checks that a change of depth lands on frame 1,000 of the block, and not one frame earlier.

Rust
#[test]
fn automation_lands_on_its_frame_with_audio_running_through() {
    let mut harness = Harness::<Duck>::new(RATE, 256);
    let input = steady(1.0, 24_000);
    let loud = steady(1.0, 24_000);
    // Let the follower settle, then ask for a different depth on one
    // particular frame in the middle of a block.
    harness.process_with_sidechain(&input, &loud);
    harness.automate(24_000 + 1_000, "depth", 24.0);
    let output = harness.process_with_sidechain(&input, &loud);

    let before = db_to_gain(-12.0) as f32;
    let after = db_to_gain(-24.0) as f32;
    assert!((output.channel(0)[999] - before).abs() < 1e-4);
    assert!((output.channel(0)[1_000] - after).abs() < 1e-4);
}
From plugins/duck/src/lib.rs at commit c5835bf, as it is in the file.

03

What you get today

Twenty-three crates that work together, each usable on its own, one of them a command-line tool. manguebyte-core has no dependencies at all.

Every feature, by area

04

Where JUCE is ahead

JUCE has more than twenty years of production use behind it, and far more breadth. Today it has, and Manguebyte does not:

  • a complete GUI toolkit, with plugin editor windows on every system and in every format (Manguebyte’s editors have windows on Win32 only, and none has been opened in a host by hand yet);
  • Audio Unit and AAX plugin formats;
  • hosting of AU and LV2 plugins, plugin scanning, and the editors of hosted plugins (Manguebyte hosts CLAP and VST 3 plugins, without their editors);
  • more DSP building blocks, and more file formats;
  • iOS and Android;
  • documentation, tutorials and a large community.

The roadmap says what exists and in what order the gap is being closed.

05

The Manguebyte ecosystem

Two more projects from Recife grow from the same mangue, built to run on Manguebyte.

  • This framework

    Manguebyte

    The open-source audio framework for Rust, under MIT or Apache-2.0. Aratu already plays on its browser engine; Parabolic is to be built on it.

  • In development

    Aratu

    A live-coding language that reads like music: measures, beats and note values first, in Portuguese, Spanish and English. Its editor runs in the browser and plays on Manguebyte.

  • Planned

    Parabolic

    A tool for musicians to record their own instruments and turn them into plugins and samplers without writing code.

More about Aratu

06

Crabs with brains

The name comes from Recife, the city in the north-east of Brazil where this project was born. Recife is built on mangroves, mangue in Portuguese.

In the early 1990s a group of local musicians answered the city’s troubles with a manifesto, Caranguejos com Cérebro, “Crabs with Brains”: plug the rich, muddy roots of the region’s culture into the global network of ideas. The image they chose was a satellite dish stuck in the mangrove mud.

The story of the name

Start with one dependency

Manguebyte is not on crates.io yet. Depend on it straight from Git:

TOML
[dependencies]
manguebyte = { git = "https://github.com/alessonqueirozdev-hub/manguebyte" }
From README.md at commit c5835bf, as it is in the file.

The getting started guide The repository on GitHub