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
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
use Range;
use crate;
/// Which render thread a [`RenderState`](BelaApplication::RenderState)
/// is being made for.
///
/// Handed to
/// [`create_render_state`](BelaApplication::create_render_state), which
/// is called once per thread, in order, before audio starts.
/// A Bela application: user code driven by the audio system callbacks.
///
/// The callbacks come in three groups, and the difference between them
/// is what Bela's multithreaded rendering makes of `self`:
///
/// - [`setup`](BelaApplication::setup),
/// [`create_render_state`](BelaApplication::create_render_state) and
/// [`cleanup`](BelaApplication::cleanup) run once, on their own,
/// outside the real-time context.
/// - [`render_pre`](BelaApplication::render_pre) and
/// [`render_post`](BelaApplication::render_post) run once per block
/// on the main audio thread, bracketing the parallel section. They
/// see the whole block and every render state.
/// - [`render`](BelaApplication::render) runs once per block **on every
/// render thread at the same time**, which is why it takes `&self`.
/// Each call gets one [`RenderState`](BelaApplication::RenderState),
/// exclusively, and one thread's share of the output buffers.
///
/// That shape is Bela's, not this crate's: with
/// [`Settings::thread_count`](crate::Settings::thread_count) above 1,
/// libbela calls `render` concurrently on every thread, for the same
/// block, with the same user data and the same unpartitioned buffers.
/// See `docs/multithreaded-rendering.md` for the measurements. One
/// render thread is the same model with one state and one partition
/// covering the block, so there is nothing extra to write for it.
///
/// # Where the mutable state goes
///
/// Anything `render` mutates belongs in
/// [`RenderState`](BelaApplication::RenderState): filters, phases,
/// per-thread scratch buffers, counters. The application itself holds
/// what every thread reads — coefficients, tables, handles — and is
/// shared as `&self` while rendering.
///
/// State that is genuinely one thing for the whole block, like an
/// oscillator's phase, is prepared in `render_pre` and folded back in
/// `render_post`; `examples/sine.rs` shows the pattern.
///
/// # Real-time safety
///
/// `render`, `render_pre` and `render_post` run on real-time threads.
/// They must not:
///
/// - allocate or free heap memory,
/// - block (locks, channels, sleeping) or make system calls (including
/// I/O; use [`rt_println!`](crate::rt_println) for debugging, and an
/// [`AuxiliaryTask`](crate::AuxiliaryTask) for work that has to
/// allocate or block),
/// - panic in code paths that can actually be hit — a panic crossing
/// the callback boundary aborts the whole process.
///
/// This is an operational contract rather than a memory-safety one,
/// which is why the trait is safe to implement: breaking it costs
/// dropouts, not undefined behaviour. `setup`, `create_render_state`
/// and `cleanup` run outside the real-time context and are not subject
/// to it (panics still abort the process).
///
/// # Example
///
/// ```
/// use bela::{BelaApplication, RenderContext, SetupContext, ThreadInfo};
///
/// struct Passthrough;
///
/// impl BelaApplication for Passthrough {
/// // Nothing to carry from block to block.
/// 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());
/// // Only this thread's frames; the ranges tile the block.
/// 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);
/// }
/// }
/// }
/// }
/// ```