akiomik/bela-rs

Rust bindings for the Bela core API, targeting Bela Gem on PocketBeagle 2 (aarch64)

★ 1Forks 0RustGitHub ↗Compare
am6254am62xbeladspembeddedpocket-beaglepocketbeaglepocketbeagle2

README

bela-rs

CI codecov Crates.io Version Crates.io Version

Rust bindings for the Bela core API, targeting Bela Gem on PocketBeagle 2 (aarch64-unknown-linux-gnu).

Status: works on hardware, API not yet settled. The examples cross-build on a host and produce sound on a Bela Gem Stereo. The scope is the core audio API plus MIDI — the other C++ libraries are not wrapped, and the API may change in any 0.x release.

Crates

Crate Description
bela-sys Raw FFI bindings to libbela, a C surface over Bela's Midi, and NE10's FFT
bela Safe API: settings builder, real-time render trait, RAII lifecycle, MIDI, FFT

The scope is the C core API (BelaContext, setup/render/cleanup, Bela_initAudio/Bela_startAudio/...) and MIDI, which is C++ and reached through a shim this workspace compiles — see MIDI.

The FFT is reached differently again: bela-sys declares NE10's real-to-complex transform (libNE10.so.10, the library Bela's own Fft class calls) and calls it directly, with no shim and no C++; bela wraps that as RealFft — see FFT.

Bela's other C++ libraries — the browser scope, Trill, the GUI and the rest — are not wrapped, and neither is one corner of the core API: the Multiplexer Capelet accessors, for an accessory that cannot be attached to a Gem at all. What is wrapped, what is left out on purpose and why, and what is merely not written yet are in docs/scope.md.

The integration model is the officially supported one: a standalone binary that defines the render callbacks and links libbela, cross-compiled on a host machine and copied to the board.

Boards

Bela Gem only. The older boards — Bela and Bela Mini — are out of scope, and not for want of a second target triple: they are armv7, while the vendored headers, the libraries build.rs links and every number in Board facts come from a Gem image. A port needs its own headers, its own real-time runtime and its own measurements.

The measurements are the part that cannot be borrowed. Nothing here is claimed about a board that was not measured on one, and there is no older board here to measure: Bela no longer sells them, and points Bela Mini owners at Bela Gem Stereo instead. A port is welcome from someone who can check it against the hardware.

Quick start

cargo add bela

Implement BelaApplication and hand it to Bela::run. render must be real-time safe: no allocation, blocking, system calls or panics.

use bela::{Bela, BelaApplication, RenderContext, Settings, SetupContext, ThreadInfo};

struct Passthrough;

impl BelaApplication for Passthrough {
    // Nothing to carry from one block to the next.
    type RenderState = ();

    fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}

    fn render(&self, _state: &mut (), context: &mut RenderContext) {
        let channels = context
            .audio_in_channels()
            .min(context.audio_out_channels());
        // This thread's share of the block; with one render thread,
        // all of it.
        for frame in context.audio_frame_range() {
            for channel in 0..channels {
                let sample = context.audio_read(frame, channel);
                context.audio_write(frame, channel, sample);
            }
        }
    }
}

fn main() -> Result<(), bela::Error> {
    Bela::run(Passthrough, &Settings::new())
}

The shape — an application shared as &self, one RenderState per render thread, a context that writes only this thread's frames — is what lets Settings::thread_count use all four of a Bela Gem's cores for one block. It is the same code either way; see Multithreaded rendering.

Building requires a sysroot synced from the board and a small build.rs of your own to relay link arguments; see docs/cross-compile.md and bela's downstream setup for the one-time setup.

export BELA_SYSROOT="$PWD/bela-sysroot"
cargo build --release --target aarch64-unknown-linux-gnu
scp target/aarch64-unknown-linux-gnu/release/my-app [email protected]:
ssh -t [email protected] 'systemctl stop bela_daemon && ./my-app'

Set panic = "abort" in the release profile: a panic crossing the audio callback boundary aborts the process either way.

Documentation

License

Licensed under either of

at your option.

Note: the Bela core software that final binaries link against is licensed under the LGPL 3.0. That obligation applies to the linked binary, not to these crates.

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.

Contributors

akiomikdependabot[bot]

Issues