Ship a Native Extension
A native extension is a Rust library that resonon loads and calls like a module. Use one to compute
something — a wavetable, a scale, a pattern — that is too slow or impossible in resonon. It runs on
the interpreter thread, not the audio thread: audio processing belongs in a
dsp effect or dsp instrument. See
Native Extensions: Safety & Threading
before you ship one.
You need the Rust toolchain; check it with cargo --version.
Scaffold the project
Section titled “Scaffold the project”resonon new my-ext --nativecd my-extmy-ext/ resonon.toml # manifest, with a [native] section src/ lib.non # the resonon-facing API native/ # the Rust crate Cargo.toml src/lib.rs bin/ # built libraries go hereExpose functions and classes
Section titled “Expose functions and classes”In native/src/lib.rs, mark each function with #[ext_fn], and list it in resonon_extension!:
use resonon_ext::prelude::*;
#[ext_fn(doc = "Doubles a number")]fn double(x: f64) -> f64 { x * 2.0}
resonon_extension! { name: "my_ext", functions: [double]}For an object that keeps state, add a class, a constructor and methods. Every constructor and
method goes into functions, and the class into classes:
use resonon_ext::prelude::*;
/// A step sequencer that cycles through values.#[ext_class]struct Stepper { values: Vec<f64>, index: usize,}
#[ext_new(doc = "Create a stepper from an array of values")]fn stepper_new(values: Vec<f64>) -> Stepper { Stepper { values, index: 0 }}
#[ext_method(Stepper, doc = "Get the next value in the sequence")]fn stepper_next(s: &mut Stepper) -> f64 { if s.values.is_empty() { return 0.0; } let val = s.values[s.index % s.values.len()]; s.index += 1; val}
resonon_extension! { name: "my_ext", functions: [stepper_new, stepper_next], classes: [Stepper]}The macros and the types they accept are in the Native Extensions reference.
Wrap them in lib.non
Section titled “Wrap them in lib.non”Users don’t call Rust directly. src/lib.non is an ordinary module whose functions call into the
extension with __ext(extension, function, ...args). Each #[ext_class] is declared as a
native class of the same name, whose methods
pass this back to the extension:
//! my-ext — custom pattern generators.
/// Doubles a number.fn double(x) { return __ext("my_ext", "double", x);}
/// A step sequencer that cycles through values.native class Stepper { /// The next value in the sequence. fn next() -> Number { return __ext("my_ext", "stepper_next", this); }}
/// Creates a stepper from values.fn Stepper(values) -> Stepper { return __ext("my_ext", "stepper_new", values);}An object the extension returns is a Stepper because lib.non declares one. An object whose class
no native class declares is refused with an error.
Build and use it
Section titled “Build and use it”resonon buildThis builds the crate in release mode and copies the library into bin/. Import the package by
its name; the module is bound under that name with - changed to _:
use "my-ext";
print(my_ext.double(21)); // 42
let s = my_ext.Stepper(#[60, 62, 64, 67]);print(s.next()); // 60print(s.next()); // 62Reload it while you work
Section titled “Reload it while you work”After a change to the Rust code, build again in a terminal, then reload the extension in your session instead of restarting it:
reload_ext("my_ext");The reload fails while any object from the extension is still alive. Reassign or drop the
variables that hold them first — s above.
Publish prebuilt binaries
Section titled “Publish prebuilt binaries”Build a binary named for your platform, and commit it:
resonon build --distThis writes, for example, bin/my_ext-macos-aarch64.dylib. The scaffolded .gitignore already
ignores the local build and keeps these. Build and commit one per platform you support, then push
the repository.
When someone runs resonon pkg install gh:you/my-ext, resonon builds the extension from source.
If that build fails — on a machine without the Rust toolchain, say — the committed binary for their
platform is used instead.
Related
Section titled “Related”- Native Extensions — macros, types, and where resonon looks for the library
- Publish a Package — push a package and tag its versions
- Fix Install and Project Problems — a package that is not found or has nothing in it