Skip to content

Commit 90eb0bf

Browse files
committed
future-proof list of node processing buffers
1 parent 7168747 commit 90eb0bf

File tree

16 files changed

+181
-179
lines changed

16 files changed

+181
-179
lines changed

crates/firewheel-core/src/node.rs

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -186,8 +186,19 @@ pub struct EmptyConfig;
186186

187187
/// A dyn-compatible [`AudioNode`].
188188
pub trait DynAudioNode {
189+
/// Get information about this node.
190+
///
191+
/// This method is only called once after the node is added to the audio graph.
189192
fn info(&self) -> AudioNodeInfo;
193+
194+
/// Construct a processor for this node.
190195
fn processor(&self, stream_info: &StreamInfo) -> Box<dyn AudioNodeProcessor>;
196+
197+
/// If [`AudioNodeInfo::call_update_method`] was set to `true`, then the Firewheel
198+
/// context will call this method on every update cycle.
199+
///
200+
/// * `id` - The ID of this node.
201+
/// * `configuration` - The custom configuration of this node.
191202
fn update(&mut self, cx: UpdateContext) {
192203
let _ = cx;
193204
}
@@ -236,22 +247,14 @@ pub trait AudioNodeProcessor: 'static + Send {
236247
/// If any output buffers contain all zeros up to `samples` (silent),
237248
/// then mark that buffer as silent in [`ProcInfo::out_silence_mask`].
238249
///
239-
/// * `inputs` - The input buffers.
240-
/// * `outputs` - The output buffers,
241-
/// * `events` - A list of events for this node to process.
250+
/// * `buffers` - The buffers of data to process.
242251
/// * `proc_info` - Additional information about the process.
243-
/// * `scratch_buffers` - A list of extra scratch buffers that can be
244-
/// used for processing. This removes the need for nodes to allocate
245-
/// their own scratch buffers. Each buffer has a length of
246-
/// [`StreamInfo::max_block_frames`]. These buffers are shared across
247-
/// all nodes, so assume that they contain junk data.
252+
/// * `events` - A list of events for this node to process.
248253
fn process(
249254
&mut self,
250-
inputs: &[&[f32]],
251-
outputs: &mut [&mut [f32]],
252-
events: NodeEventList,
255+
buffers: ProcBuffers,
253256
proc_info: &ProcInfo,
254-
scratch_buffers: ScratchBuffers,
257+
events: NodeEventList,
255258
) -> ProcessStatus;
256259

257260
/// Called when the audio stream has been stopped.
@@ -269,12 +272,33 @@ pub trait AudioNodeProcessor: 'static + Send {
269272

270273
pub const NUM_SCRATCH_BUFFERS: usize = 8;
271274

272-
/// A list of extra scratch buffers that can be
273-
/// used for processing. This removes the need for nodes to allocate
274-
/// their own scratch buffers. Each buffer has a length of
275-
/// [`StreamInfo::max_block_frames`]. These buffers are shared across
276-
/// all nodes, so assume that they contain junk data.
277-
pub type ScratchBuffers<'a, 'b> = &'a mut [&'b mut [f32]; NUM_SCRATCH_BUFFERS];
275+
/// The buffers used in [`AudioNodeProcessor::process`].
276+
pub struct ProcBuffers<'a, 'b, 'c, 'd> {
277+
/// The audio input buffers.
278+
///
279+
/// The number of channels will always equal the [`ChannelConfig::num_inputs`]
280+
/// value that was returned in [`AudioNode::info`].
281+
///
282+
/// Each channel slice will have a length of [`ProcInfo::frames`].
283+
pub inputs: &'a [&'b [f32]],
284+
285+
/// The audio input buffers.
286+
///
287+
/// The number of channels will always equal the [`ChannelConfig::num_outputs`]
288+
/// value that was returned in [`AudioNode::info`].
289+
///
290+
/// Each channel slice will have a length of [`ProcInfo::frames`].
291+
///
292+
/// These buffers may contain junk data.
293+
pub outputs: &'a mut [&'b mut [f32]],
294+
295+
/// A list of extra scratch buffers that can be used for processing.
296+
/// This removes the need for nodes to allocate their own scratch buffers.
297+
/// Each buffer has a length of [`StreamInfo::max_block_frames`]. These
298+
/// buffers are shared across all nodes, so assume that they contain junk
299+
/// data.
300+
pub scratch_buffers: &'c mut [&'d mut [f32]; NUM_SCRATCH_BUFFERS],
301+
}
278302

279303
/// Additional information for processing audio
280304
pub struct ProcInfo<'a> {

crates/firewheel-core/src/node/dummy.rs

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
use crate::{channel_config::ChannelConfig, event::NodeEventList, StreamInfo};
22

3-
use super::{
4-
AudioNode, AudioNodeInfo, AudioNodeProcessor, ProcInfo, ProcessStatus, ScratchBuffers,
5-
};
3+
use super::{AudioNode, AudioNodeInfo, AudioNodeProcessor, ProcBuffers, ProcInfo, ProcessStatus};
64

75
/// A "dummy" [`AudioNode`], a node which does nothing.
86
#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
@@ -38,11 +36,9 @@ struct DummyProcessor;
3836
impl AudioNodeProcessor for DummyProcessor {
3937
fn process(
4038
&mut self,
41-
_inputs: &[&[f32]],
42-
_outputs: &mut [&mut [f32]],
43-
_events: NodeEventList,
39+
_buffers: ProcBuffers,
4440
_proc_info: &ProcInfo,
45-
_scratch_buffers: ScratchBuffers,
41+
_events: NodeEventList,
4642
) -> ProcessStatus {
4743
ProcessStatus::Bypass
4844
}

crates/firewheel-graph/src/graph/compiler/schedule.rs

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use smallvec::SmallVec;
33
use std::fmt::Debug;
44

55
use firewheel_core::{
6-
node::{AudioNodeProcessor, ProcessStatus},
6+
node::{AudioNodeProcessor, ProcBuffers, ProcessStatus, NUM_SCRATCH_BUFFERS},
77
SilenceMask,
88
};
99

@@ -339,16 +339,11 @@ impl CompiledSchedule {
339339
(read_outputs)(outputs.as_slice(), silence_mask);
340340
}
341341

342-
pub fn process(
342+
pub fn process<'a, 'b>(
343343
&mut self,
344344
frames: usize,
345-
mut process: impl FnMut(
346-
NodeID,
347-
SilenceMask,
348-
SilenceMask,
349-
&[&[f32]],
350-
&mut [&mut [f32]],
351-
) -> ProcessStatus,
345+
scratch_buffers: &'a mut [&'b mut [f32]; NUM_SCRATCH_BUFFERS],
346+
mut process: impl FnMut(NodeID, SilenceMask, SilenceMask, ProcBuffers) -> ProcessStatus,
352347
) {
353348
let frames = frames.min(self.max_block_frames);
354349

@@ -409,8 +404,11 @@ impl CompiledSchedule {
409404
scheduled_node.id,
410405
in_silence_mask,
411406
out_silence_mask,
412-
inputs.as_slice(),
413-
outputs.as_mut_slice(),
407+
ProcBuffers {
408+
inputs: inputs.as_slice(),
409+
outputs: outputs.as_mut_slice(),
410+
scratch_buffers,
411+
},
414412
);
415413

416414
let clear_buffer = |buffer_index: usize, silence_flag: &mut bool| {

crates/firewheel-graph/src/processor.rs

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,8 @@ use firewheel_core::{
1616
dsp::{buffer::ChannelBuffer, declick::DeclickValues},
1717
event::{NodeEvent, NodeEventList},
1818
node::{
19-
AudioNodeProcessor, NodeID, ProcInfo, ProcessStatus, StreamStatus, TransportInfo,
20-
NUM_SCRATCH_BUFFERS,
19+
AudioNodeProcessor, NodeID, ProcBuffers, ProcInfo, ProcessStatus, StreamStatus,
20+
TransportInfo, NUM_SCRATCH_BUFFERS,
2121
},
2222
SilenceMask, StreamInfo,
2323
};
@@ -531,11 +531,11 @@ impl FirewheelProcessorInner {
531531

532532
schedule_data.schedule.process(
533533
block_frames,
534+
&mut scratch_buffers,
534535
|node_id: NodeID,
535536
in_silence_mask: SilenceMask,
536537
out_silence_mask: SilenceMask,
537-
inputs: &[&[f32]],
538-
outputs: &mut [&mut [f32]]|
538+
proc_buffers: ProcBuffers|
539539
-> ProcessStatus {
540540
let Some(node_entry) = self.nodes.get_mut(node_id.0) else {
541541
return ProcessStatus::Bypass;
@@ -546,13 +546,9 @@ impl FirewheelProcessorInner {
546546
proc_info.in_silence_mask = in_silence_mask;
547547
proc_info.out_silence_mask = out_silence_mask;
548548

549-
let status = node_entry.processor.process(
550-
inputs,
551-
outputs,
552-
events,
553-
&proc_info,
554-
&mut scratch_buffers,
555-
);
549+
let status = node_entry
550+
.processor
551+
.process(proc_buffers, &proc_info, events);
556552

557553
node_entry.event_indices.clear();
558554

crates/firewheel-nodes/src/beep_test.rs

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ use firewheel_core::{
44
dsp::volume::{Volume, DEFAULT_AMP_EPSILON},
55
event::NodeEventList,
66
node::{
7-
AudioNode, AudioNodeInfo, AudioNodeProcessor, EmptyConfig, ProcInfo, ProcessStatus,
8-
ScratchBuffers,
7+
AudioNode, AudioNodeInfo, AudioNodeProcessor, EmptyConfig, ProcBuffers, ProcInfo,
8+
ProcessStatus,
99
},
1010
};
1111

@@ -80,13 +80,11 @@ struct Processor {
8080
impl AudioNodeProcessor for Processor {
8181
fn process(
8282
&mut self,
83-
_inputs: &[&[f32]],
84-
outputs: &mut [&mut [f32]],
85-
events: NodeEventList,
83+
buffers: ProcBuffers,
8684
_proc_info: &ProcInfo,
87-
_scratch_buffers: ScratchBuffers,
85+
events: NodeEventList,
8886
) -> ProcessStatus {
89-
let Some(out) = outputs.first_mut() else {
87+
let Some(out) = buffers.outputs.first_mut() else {
9088
return ProcessStatus::ClearAllOutputs;
9189
};
9290

crates/firewheel-nodes/src/peak_meter.rs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ use firewheel_core::{
77
dsp::volume::{amp_to_db, DbMeterNormalizer},
88
event::NodeEventList,
99
node::{
10-
AudioNode, AudioNodeInfo, AudioNodeProcessor, EmptyConfig, ProcInfo, ProcessStatus,
11-
NUM_SCRATCH_BUFFERS,
10+
AudioNode, AudioNodeInfo, AudioNodeProcessor, EmptyConfig, ProcBuffers, ProcInfo,
11+
ProcessStatus,
1212
},
1313
StreamInfo,
1414
};
@@ -245,11 +245,9 @@ struct Processor<const NUM_CHANNELS: usize> {
245245
impl<const NUM_CHANNELS: usize> AudioNodeProcessor for Processor<NUM_CHANNELS> {
246246
fn process(
247247
&mut self,
248-
inputs: &[&[f32]],
249-
_outputs: &mut [&mut [f32]],
250-
_events: NodeEventList,
248+
buffers: ProcBuffers,
251249
proc_info: &ProcInfo,
252-
_scratch_buffers: &mut [&mut [f32]; NUM_SCRATCH_BUFFERS],
250+
_events: NodeEventList,
253251
) -> ProcessStatus {
254252
let enabled = self.shared_state.enabled.load(Ordering::Relaxed);
255253

@@ -264,7 +262,8 @@ impl<const NUM_CHANNELS: usize> AudioNodeProcessor for Processor<NUM_CHANNELS> {
264262
return ProcessStatus::Bypass;
265263
}
266264

267-
for (i, (in_ch, peak_shared)) in inputs
265+
for (i, (in_ch, peak_shared)) in buffers
266+
.inputs
268267
.iter()
269268
.zip(self.shared_state.peak_gains.iter())
270269
.enumerate()

crates/firewheel-nodes/src/sampler.rs

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use firewheel_core::{
1919
volume::{Volume, DEFAULT_AMP_EPSILON},
2020
},
2121
event::{NodeEventList, NodeEventType, SequenceCommand},
22-
node::{AudioNode, AudioNodeInfo, AudioNodeProcessor, ProcInfo, ProcessStatus, ScratchBuffers},
22+
node::{AudioNode, AudioNodeInfo, AudioNodeProcessor, ProcBuffers, ProcInfo, ProcessStatus},
2323
sample_resource::SampleResource,
2424
SilenceMask, StreamInfo,
2525
};
@@ -737,11 +737,9 @@ impl SamplerProcessor {
737737
impl AudioNodeProcessor for SamplerProcessor {
738738
fn process(
739739
&mut self,
740-
_inputs: &[&[f32]],
741-
outputs: &mut [&mut [f32]],
742-
mut events: NodeEventList,
740+
buffers: ProcBuffers,
743741
proc_info: &ProcInfo,
744-
_scratch_buffers: ScratchBuffers,
742+
mut events: NodeEventList,
745743
) -> ProcessStatus {
746744
events.for_each(|event| match event {
747745
NodeEventType::SequenceCommand(command) => {
@@ -752,7 +750,7 @@ impl AudioNodeProcessor for SamplerProcessor {
752750

753751
match command {
754752
SequenceCommand::StartOrRestart { delay } => {
755-
self.stop(proc_info.declick_values, outputs.len());
753+
self.stop(proc_info.declick_values, buffers.outputs.len());
756754

757755
self.playback_state = PlaybackState::Playing;
758756

@@ -808,7 +806,7 @@ impl AudioNodeProcessor for SamplerProcessor {
808806
}
809807
}
810808
SequenceCommand::Stop => {
811-
self.stop(proc_info.declick_values, outputs.len());
809+
self.stop(proc_info.declick_values, buffers.outputs.len());
812810

813811
self.playback_state = PlaybackState::Stopped;
814812
}
@@ -824,9 +822,9 @@ impl AudioNodeProcessor for SamplerProcessor {
824822
params,
825823
start_immediately,
826824
} => {
827-
self.stop(proc_info.declick_values, outputs.len());
825+
self.stop(proc_info.declick_values, buffers.outputs.len());
828826

829-
self.set_sequence(&mut params.sequence, outputs.len());
827+
self.set_sequence(&mut params.sequence, buffers.outputs.len());
830828

831829
if self.params.sequence.is_none() {
832830
return;
@@ -855,13 +853,17 @@ impl AudioNodeProcessor for SamplerProcessor {
855853
+ (seconds.fract() * self.sample_rate).round() as u64
856854
};
857855

858-
self.set_playhead(playhead_frames, proc_info.declick_values, outputs.len());
856+
self.set_playhead(
857+
playhead_frames,
858+
proc_info.declick_values,
859+
buffers.outputs.len(),
860+
);
859861
}
860862
SamplerEvent::SetPlayheadSamples(playhead_frames) => {
861863
self.set_playhead(
862864
*playhead_frames,
863865
proc_info.declick_values,
864-
outputs.len(),
866+
buffers.outputs.len(),
865867
);
866868
}
867869
}
@@ -907,7 +909,7 @@ impl AudioNodeProcessor for SamplerProcessor {
907909
.do_loop(sample_state.num_times_looped_back);
908910

909911
let (finished, n_channels) = self.process_internal(
910-
outputs,
912+
buffers.outputs,
911913
proc_info.frames,
912914
looping,
913915
proc_info.declick_values,
@@ -938,7 +940,12 @@ impl AudioNodeProcessor for SamplerProcessor {
938940
}
939941
}
940942

941-
for (i, out_buf) in outputs.iter_mut().enumerate().skip(num_filled_channels) {
943+
for (i, out_buf) in buffers
944+
.outputs
945+
.iter_mut()
946+
.enumerate()
947+
.skip(num_filled_channels)
948+
{
942949
if !proc_info.out_silence_mask.is_channel_silent(i) {
943950
out_buf[..proc_info.frames].fill(0.0);
944951
}
@@ -960,7 +967,7 @@ impl AudioNodeProcessor for SamplerProcessor {
960967
let copy_frames = proc_info.frames.min(declicker.frames_left);
961968
let start_frame = fade_out_frames - declicker.frames_left;
962969

963-
for (out_buf, tmp_buf) in outputs.iter_mut().zip(tmp_buffers.iter()) {
970+
for (out_buf, tmp_buf) in buffers.outputs.iter_mut().zip(tmp_buffers.iter()) {
964971
for (os, &ts) in out_buf[..copy_frames]
965972
.iter_mut()
966973
.zip(tmp_buf[start_frame..start_frame + copy_frames].iter())
@@ -978,10 +985,10 @@ impl AudioNodeProcessor for SamplerProcessor {
978985
}
979986
}
980987

981-
let out_silence_mask = if num_filled_channels >= outputs.len() {
988+
let out_silence_mask = if num_filled_channels >= buffers.outputs.len() {
982989
SilenceMask::NONE_SILENT
983990
} else {
984-
let mut mask = SilenceMask::new_all_silent(outputs.len());
991+
let mut mask = SilenceMask::new_all_silent(buffers.outputs.len());
985992
for i in 0..num_filled_channels {
986993
mask.set_channel(i, false);
987994
}

0 commit comments

Comments
 (0)