-
-
Notifications
You must be signed in to change notification settings - Fork 1
/
node.rs
429 lines (390 loc) · 14.6 KB
/
node.rs
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
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
use downcast_rs::Downcast;
use std::{any::Any, error::Error, fmt::Debug, hash::Hash, sync::Arc};
use crate::{
clock::{ClockSamples, ClockSeconds, EventDelay},
sample_resource::SampleResource,
ChannelConfig, ChannelCount, SilenceMask, StreamInfo,
};
/// A globally unique identifier for a node.
#[derive(Clone, Copy)]
pub struct NodeID {
pub idx: thunderdome::Index,
pub debug_name: &'static str,
}
impl NodeID {
pub const DANGLING: Self = Self {
idx: thunderdome::Index::DANGLING,
debug_name: "dangling",
};
}
impl Default for NodeID {
fn default() -> Self {
Self::DANGLING
}
}
impl PartialEq for NodeID {
fn eq(&self, other: &Self) -> bool {
self.idx == other.idx
}
}
impl Eq for NodeID {}
impl Ord for NodeID {
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
self.idx.cmp(&other.idx)
}
}
impl PartialOrd for NodeID {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
Some(self.cmp(other))
}
}
impl Hash for NodeID {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.idx.hash(state);
}
}
impl Debug for NodeID {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}-{}-{}",
self.debug_name,
self.idx.slot(),
self.idx.generation()
)
}
}
/// The trait describing an audio node in an audio graph.
///
/// # Audio Node Lifecycle:
///
/// 1. The user constructs a new node instance using a custom constructor
/// defined by the node.
/// 2. The host calls [`AudioNode::info`] and [`AudioNode::debug_name`] to
/// get information from the node.
/// 3. The host checks the channel configuration with the info, and then
/// calls [`AudioNode::channel_config_supported`] for a final check on the
/// channel configuration. If the channel configuration is invalid, then
/// the node will be discarded (dropped).
/// 4. The host calls [`AudioNode::activate`]. If successful, then the
/// [`AudioNodeProcessor`] counterpart is sent to the audio thread for
/// processing (there may be a delay before processing starts). If the
/// node returns an error and the node was just added to the graph, then
/// the node will be discarded (dropped).
/// 5. Activated state:
/// * In this state, the user may get a mutable reference to the node
/// via its [`NodeID`] and then downcasting. Note that the user can only
/// access the node mutably this way when it is in the activated state,
/// so there is no need to check for this activated state and return an
/// error in the Node's custom methods.
/// * If the node specified that it wants updates via
/// [`AudioNodeInfo::updates`], then the host will call
/// [`AudioNode::update`] periodically (i.e. once every frame).
/// 6. The host deactivates the node by calling [`AudioNode::deactivate`].
/// If the audio stream did not crash, then the processor counterpart
/// is returned for any additional cleanup.
/// 7. Here, the node may either be activated again or dropped.
pub trait AudioNode: 'static + Downcast {
/// The name of this type of audio node for debugging purposes.
fn debug_name(&self) -> &'static str;
/// Return information about this audio node.
fn info(&self) -> AudioNodeInfo;
/// Return `Ok` if the given channel configuration is supported, or
/// an error if it is not.
///
/// Note that the host already checks if `num_inputs` and `num_outputs`
/// is within the range given in [`AudioNode::info`], so there is no
/// need for the node to check that here.
fn channel_config_supported(
&self,
channel_config: ChannelConfig,
) -> Result<(), Box<dyn Error>> {
let _ = channel_config;
Ok(())
}
/// Activate the audio node for processing.
///
/// Note the host will call [`AudioNode::channel_config_supported`] with
/// the given number of inputs and outputs before calling this method, and
/// it will only call this method if that method returned `Ok`.
fn activate(
&mut self,
stream_info: &StreamInfo,
channel_config: ChannelConfig,
) -> Result<Box<dyn AudioNodeProcessor>, Box<dyn Error>>;
/// Called when the processor counterpart has been deactivated
/// and dropped.
///
/// If the audio graph counterpart has gracefully shut down, then
/// the processor counterpart is returned.
fn deactivate(&mut self, processor: Option<Box<dyn AudioNodeProcessor>>) {
let _ = processor;
}
/// A method that gets called periodically (i.e. once every frame).
///
/// This method will only be called if [`AudioNodeInfo::updates`]
/// was set to `true`.
fn update(&mut self) {}
}
downcast_rs::impl_downcast!(AudioNode);
/// Information about an [`AudioNode`]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct AudioNodeInfo {
/// The minimum number of input buffers this node supports
pub num_min_supported_inputs: ChannelCount,
/// The maximum number of input buffers this node supports
pub num_max_supported_inputs: ChannelCount,
/// The minimum number of output buffers this node supports
pub num_min_supported_outputs: ChannelCount,
/// The maximum number of output buffers this node supports
pub num_max_supported_outputs: ChannelCount,
/// Whether or not the number of input channels must match the
/// number of output channels.
pub equal_num_ins_and_outs: bool,
/// The defaul channel configuration for this node
pub default_channel_config: ChannelConfig,
/// Whether or not to call the `update` method on this node.
///
/// If you do not need this, set this to `false` to save
/// some performance overhead.
///
/// By default this is set to `false`.
pub updates: bool,
/// Whether or not this node reads any events in
/// [`AudioNodeProcessor::process`].
///
/// Setting this to `false` will skip allocating an event
/// buffer for this node.
///
/// By default this is set to `true`.
pub uses_events: bool,
}
impl Default for AudioNodeInfo {
fn default() -> Self {
Self {
num_min_supported_inputs: ChannelCount::default(),
num_max_supported_inputs: ChannelCount::default(),
num_min_supported_outputs: ChannelCount::default(),
num_max_supported_outputs: ChannelCount::default(),
default_channel_config: ChannelConfig::default(),
equal_num_ins_and_outs: false,
updates: false,
uses_events: true,
}
}
}
/// The trait describing the realtime processor counterpart to an
/// [`AudioNode`].
pub trait AudioNodeProcessor: 'static + Send {
/// Process the given block of audio. Only process data in the
/// buffers up to `samples`.
///
/// The node *MUST* either return `ProcessStatus::ClearAllOutputs`
/// or fill all output buffers with data.
///
/// If any output buffers contain all zeros up to `samples` (silent),
/// then mark that buffer as silent in [`ProcInfo::out_silence_mask`].
fn process(
&mut self,
inputs: &[&[f32]],
outputs: &mut [&mut [f32]],
events: NodeEventIter,
proc_info: ProcInfo,
) -> ProcessStatus;
}
/// Additional information for processing audio
#[derive(Debug, Clone)]
pub struct ProcInfo {
/// The number of samples in this processing block.
pub samples: usize,
/// An optional optimization hint on which input channels contain
/// all zeros (silence). The first bit (`0b1`) is the first channel,
/// the second bit is the second channel, and so on.
pub in_silence_mask: SilenceMask,
/// An optional optimization hint on which output channels contain
/// all zeros (silence). The first bit (`0b1`) is the first channel,
/// the second bit is the second channel, and so on.
pub out_silence_mask: SilenceMask,
/// The current time of the internal clock in units of seconds.
///
/// This uses the clock from the OS's audio API so it should be quite
/// accurate. This value has also been adjusted to match the clock in
/// the main thread.
///
/// This value correctly accounts for any output underflows that may
/// occur.
pub clock_seconds: ClockSeconds,
/// The total number of samples that have been processed since the
/// start of the audio stream.
///
/// This value can be used for more accurate timing than
/// [`ProcInfo::clock_secs`], but note it does *NOT* account for any
/// output underflows that may occur.
pub clock_samples: ClockSamples,
/// Flags indicating the current status of the audio stream
pub stream_status: StreamStatus,
}
bitflags::bitflags! {
/// Flags indicating the current status of the audio stream
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct StreamStatus: u32 {
/// Some input data was discarded because of an overflow condition
/// at the audio driver.
const INPUT_OVERFLOW = 0b01;
/// The output buffer ran low, likely producing a break in the
/// output sound.
const OUTPUT_UNDERFLOW = 0b10;
}
}
/// The status of processing buffers in an audio node.
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
pub enum ProcessStatus {
/// No output buffers were modified. If this is returned, then
/// the engine will automatically clear all output buffers
/// for you as efficiently as possible.
#[default]
ClearAllOutputs,
/// No output buffers were modified. If this is returned, then
/// the engine will automatically copy the input buffers to
/// their corresponding output buffers for you as efficiently
/// as possible.
Bypass,
/// All output buffers were filled with data.
OutputsModified { out_silence_mask: SilenceMask },
}
impl ProcessStatus {
/// All output buffers were filled with non-silence.
pub const fn outputs_not_silent() -> Self {
Self::OutputsModified {
out_silence_mask: SilenceMask::NONE_SILENT,
}
}
/// All output buffers were filled with data.
pub const fn outputs_modified(out_silence_mask: SilenceMask) -> Self {
Self::OutputsModified { out_silence_mask }
}
}
/// An event sent to an [`AudioNode`].
pub struct NodeEvent {
/// The ID of the node that should receive the event.
pub node_id: NodeID,
/// The delay of this event.
///
/// Note the following event types will ignore this value and execute
/// the event immediately instead:
/// * [`NodeEventType::Pause`]
/// * [`NodeEventType::Resume`]
/// * [`NodeEventType::Stop`]
pub delay: EventDelay,
/// The type of event.
pub event: NodeEventType,
}
/// An event type associated with an [`AudioNode`].
pub enum NodeEventType {
/// Pause this node and all of its queued delayed events.
///
/// Note this event type cannot be delayed.
Pause,
/// Resume this node and all of its queued delayed events.
///
/// Note this event type cannot be delayed.
Resume,
/// Stop this node and discard all of its queued delayed events.
///
/// Note this event type cannot be delayed.
Stop,
/// Enable/disable this node.
///
/// Note the node must implement this event type for this to take
/// effect.
SetEnabled(bool),
/// Set the value of an `f32` parameter.
FloatParam {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: f32,
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Set the value of an `f64` parameter.
FloatParamF64 {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: f64,
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Set the value of an `i32` parameter.
IntParam {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: i32,
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Set the value of an `i64` parameter.
IntParamI64 {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: i64,
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Set the value of a `bool` parameter.
BoolParam {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: bool,
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Set the value of a parameter containing three
/// `f32` elements.
Vector3DParam {
/// The unique ID of the paramater.
id: u32,
/// The parameter value.
value: [f32; 3],
/// Set this to `false` to request the node to immediately jump
/// to this new value without smoothing (may cause audible
/// clicking or stair-stepping artifacts).
smoothing: bool,
},
/// Play a sample to completion.
///
/// (Even though this event is only used by the `OneShotSamplerNode`,
/// because it is so common, define it here so the event doesn't have
/// to be allocated every time.)
PlaySample {
/// The sample resource to play.
sample: Arc<dyn SampleResource>,
/// The normalized volume to play this sample at (where `0.0` is mute
/// and `1.0` is unity gain.)
///
/// Note, this value cannot be changed while the sample is playing.
/// Use a `VolumeNode` for that instead.
normalized_volume: f32,
/// If `true`, then all other voices currently being played in this
/// node will be stopped.
stop_other_voices: bool,
},
/// Custom event type.
Custom(Box<dyn Any + Send>),
// TODO: Animation (automation) event types.
}
pub type NodeEventIter<'a> = std::collections::vec_deque::IterMut<'a, NodeEventType>;