1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
//! Safe Rust API for real-time audio on [Bela Gem].
//!
//! Built on top of the raw FFI bindings in [`bela_sys`]. User code
//! implements the [`BelaApplication`] trait and hands an instance to
//! [`Bela::run`]:
//!
//! ```ignore
//! use bela::{Bela, BelaApplication, RenderContext, Settings, SetupContext, ThreadInfo};
//!
//! struct Passthrough;
//!
//! impl BelaApplication for Passthrough {
//! type RenderState = ();
//!
//! fn create_render_state(&mut self, _thread: ThreadInfo, _context: &SetupContext) {}
//!
//! fn render(&self, _state: &mut (), context: &mut RenderContext) {
//! // Copy audio input to audio output, for this thread's frames...
//! }
//! }
//!
//! fn main() -> Result<(), bela::Error> {
//! Bela::run(Passthrough, &Settings::new().period_size(64))
//! }
//! ```
//!
//! # One application model, one or four threads
//!
//! Bela can render a block on several threads at once — a Bela Gem has
//! four cores — and it does so by calling `render` on all of them
//! simultaneously, for the same block, over the same buffers. Nothing
//! is partitioned on the C side.
//!
//! [`BelaApplication`] is shaped for that, and a single render thread
//! is the same shape with one of everything:
//!
//! - the application is shared as `&self` while rendering, so whatever
//! `render` mutates lives in a
//! [`RenderState`](BelaApplication::RenderState), one per thread;
//! - [`RenderContext`] reads the whole block but writes only this
//! thread's [`audio_frame_range`](RenderContext::audio_frame_range),
//! and the ranges tile the block exactly;
//! - [`render_pre`](BelaApplication::render_pre) and
//! [`render_post`](BelaApplication::render_post) bracket the parallel
//! section on the main audio thread, with the whole block and every
//! render state to themselves — where per-block preparation and
//! mixing down belong.
//!
//! [`Settings::thread_count`] chooses how many threads; nothing else
//! about an application changes with it. What Bela actually does, and
//! how it was measured, is in `docs/multithreaded-rendering.md`.
//!
//! # Everything else
//!
//! Work that must not happen in `render` — file and network I/O,
//! expensive calculations, anything that allocates or blocks — belongs
//! in an [`AuxiliaryTask`], which `render` triggers with a real-time
//! safe `schedule` call.
//!
//! Debugging output from the audio thread goes through
//! [`rt_println!`], which formats into a fixed-size stack buffer and
//! hands it to Bela's real-time print function — `println!` allocates
//! and blocks, and is forbidden in `render`.
//!
//! Whether rendering fits within its block deadline is answered by
//! [`Settings::cpu_monitoring`], which makes
//! [`BlockContext::cpu_usage`] report how much of each block the audio
//! thread uses, and by [`CpuTimer`], which measures one section at a
//! time. Without them the first sign of running out of headroom is a
//! dropout, after the fact.
//!
//! The codec's own volume controls — the line out level, the headphone
//! level and the gain of the preamplifier ahead of the ADC — are set
//! through the [`Bela`] handle, with
//! [`set_line_out_level`](Bela::set_line_out_level) and its siblings.
//! They can be set before audio starts as well as while it runs, which
//! is what [`Bela::until_stopped`] leaves room for.
//!
//! A built binary stays reconfigurable through Bela's standard
//! command-line options — `--period`, `--verbose`, `--use-analog` and
//! the rest, the same ones every other way of writing a Bela program
//! accepts. [`Bela::run_with_args`] applies them on top of
//! [`Settings`], so the application keeps its own defaults, and
//! [`print_usage`] prints the list.
//!
//! [`Bela`] itself calls into `libbela` and therefore only exists when
//! compiling for the device target (`aarch64-unknown-linux-gnu`); the
//! rest of the crate — [`BelaApplication`], the contexts, [`Settings`]
//! — is target-independent and unit-tested on the host.
//!
//! Binaries should set `panic = "abort"` in their release profile: a
//! panic crossing the audio callback boundary aborts the process either
//! way, and `abort` avoids shipping unwinding machinery.
//!
//! [Bela Gem]: https://bela.io
pub use ;
pub use print_usage;
pub use ;
pub use ;
pub use Error;
pub use ;
pub use ;
pub use Settings;
pub use Bela;
pub use ;
pub use ;
pub use bela_sys;