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.
| 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.
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.
cargo add belaImplement 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.
- Scope — what is wrapped, what is left out on purpose and why, and what is merely not written yet
- Cross-compilation setup
- Board facts — measured values from the actual board
- Connecting the board over Ethernet — USB Ethernet adapter setup, and why it is not a transfer speedup
- Multithreaded rendering — what
threadCountdoes on the board, how the safe API divides a block across the render threads, and what it measurably buys - MIDI — what part of Bela's MIDI code the crate
wraps, and why output leaves
renderthrough a queue of the crate's own rather than Bela's - Release procedure
- Changelog / Contributing
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.
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.
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.