audio_processor_graph/
lib.rs

1// Augmented Audio: Audio libraries and applications
2// Copyright (c) 2022 Pedro Tacla Yamada
3//
4// The MIT License (MIT)
5//
6// Permission is hereby granted, free of charge, to any person obtaining a copy
7// of this software and associated documentation files (the "Software"), to deal
8// in the Software without restriction, including without limitation the rights
9// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10// copies of the Software, and to permit persons to whom the Software is
11// furnished to do so, subject to the following conditions:
12//
13// The above copyright notice and this permission notice shall be included in
14// all copies or substantial portions of the Software.
15//
16// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22// THE SOFTWARE.
23
24//! WIP - Draft of a version of https://github.com/RustAudio/dsp-chain which will work with the
25//! `audio-processor-traits` crate (support for abstract `AudioBuffer` / `AudioProcessor`s).
26
27use std::cell::UnsafeCell;
28use std::collections::HashMap;
29use std::ops::Deref;
30
31use daggy::Walker;
32use thiserror::Error;
33
34use audio_garbage_collector::{make_shared, make_shared_cell, Shared, SharedCell};
35use audio_processor_traits::simple_processor::MonoAudioProcessor;
36use audio_processor_traits::{
37    AudioBuffer, AudioContext, AudioProcessor, AudioProcessorSettings, NoopAudioProcessor,
38};
39use augmented_oscillator::Oscillator;
40
41#[cfg(test)]
42mod test_allocator;
43
44pub type NodeIndex = daggy::NodeIndex<u32>;
45pub type ConnectionIndex = daggy::EdgeIndex<u32>;
46
47/// Default static processor type
48pub type DefaultProcessor = NoopAudioProcessor<f32>;
49
50/// Non-generic AudioProcessorGraphImpl
51pub type AudioProcessorGraph = AudioProcessorGraphImpl<DefaultProcessor>;
52
53/// Non-generic AudioProcessorGraphHandleImpl
54pub type AudioProcessorGraphHandle = AudioProcessorGraphHandleImpl<DefaultProcessor>;
55
56struct BufferCell<BufferType>(UnsafeCell<BufferType>);
57
58unsafe impl<BufferType> Sync for BufferCell<BufferType> {}
59
60struct ProcessorCell<P>(UnsafeCell<P>);
61
62unsafe impl<P> Sync for ProcessorCell<P> {}
63
64pub struct AudioProcessorGraphHandleImpl<P> {
65    input_node: NodeIndex,
66    output_node: NodeIndex,
67    dag: SharedCell<daggy::Dag<(), ()>>,
68    process_order: SharedCell<Vec<NodeIndex>>,
69    audio_processor_settings: SharedCell<Option<AudioProcessorSettings>>,
70    processors: SharedCell<HashMap<NodeIndex, Shared<ProcessorCell<NodeType<P>>>>>,
71    buffers: SharedCell<HashMap<ConnectionIndex, Shared<BufferCell<AudioBuffer<f32>>>>>,
72}
73
74impl<P: Send + 'static + AudioProcessor> AudioProcessorGraphHandleImpl<P> {
75    pub fn add_node(&self, mut processor: NodeType<P>) -> NodeIndex {
76        let mut processors = self.processors.get().deref().clone();
77        let mut dag = self.dag.get().deref().clone();
78        let index = dag.add_node(());
79
80        if let Some(settings) = self.audio_processor_settings.get().deref() {
81            let mut context = AudioContext::from(*settings);
82            match processor {
83                NodeType::Simple(ref mut processor) => processor.prepare(&mut context),
84                NodeType::Static(ref mut processor) => processor.prepare(&mut context),
85                _ => {}
86            }
87        }
88
89        let processor_ref = make_shared(ProcessorCell(UnsafeCell::new(processor)));
90        processors.insert(index, processor_ref);
91
92        self.processors.set(make_shared(processors));
93        self.dag.set(make_shared(dag));
94        index
95    }
96
97    pub fn add_connection(
98        &self,
99        source: NodeIndex,
100        destination: NodeIndex,
101    ) -> Result<ConnectionIndex, AudioProcessorGraphError> {
102        let mut buffers = self.buffers.get().deref().clone();
103
104        let mut dag = self.dag.get().deref().clone();
105        let edge = dag
106            .add_edge(source, destination, ())
107            .map_err(|_| AudioProcessorGraphError::WouldCycle)?;
108        let new_order = daggy::petgraph::algo::toposort(&dag, None)
109            .map_err(|_| AudioProcessorGraphError::WouldCycle)?;
110
111        let mut buffer = AudioBuffer::empty();
112        if let Some(settings) = self.audio_processor_settings.get().deref() {
113            buffer.resize(settings.output_channels, settings.block_size);
114        }
115
116        let buffer = make_shared(BufferCell(UnsafeCell::new(buffer)));
117
118        buffers.insert(edge, buffer);
119        self.buffers.set(make_shared(buffers));
120
121        self.dag.set(make_shared(dag));
122        self.process_order.set(make_shared(new_order));
123
124        Ok(edge)
125    }
126
127    pub fn clear(&self) {
128        let mut dag = self.dag.get().deref().clone();
129        dag.clear_edges();
130        self.dag.set(make_shared(dag));
131    }
132
133    pub fn input(&self) -> NodeIndex {
134        self.input_node
135    }
136
137    pub fn output(&self) -> NodeIndex {
138        self.output_node
139    }
140}
141
142#[derive(Debug, Error)]
143pub enum AudioProcessorGraphError {
144    #[error("Adding this connection would result in a cycle")]
145    WouldCycle,
146}
147
148pub enum NodeType<P> {
149    Simple(Box<dyn AudioProcessor<SampleType = f32> + Send>),
150    Static(P),
151    None,
152}
153
154impl From<Box<dyn AudioProcessor<SampleType = f32> + Send>> for NodeType<NoopAudioProcessor<f32>> {
155    fn from(inner: Box<dyn AudioProcessor<SampleType = f32> + Send>) -> Self {
156        NodeType::Simple(inner)
157    }
158}
159
160pub struct AudioProcessorGraphImpl<P> {
161    input_node: NodeIndex,
162    output_node: NodeIndex,
163    handle: Shared<AudioProcessorGraphHandleImpl<P>>,
164    temporary_buffer: AudioBuffer<f32>,
165}
166
167impl<P: Send + 'static + AudioProcessor> Default for AudioProcessorGraphImpl<P> {
168    fn default() -> Self {
169        Self::new(daggy::Dag::default())
170    }
171}
172
173impl<P: Send + 'static + AudioProcessor> AudioProcessorGraphImpl<P> {
174    fn new(mut dag: daggy::Dag<(), ()>) -> Self {
175        let _input_proc: NodeType<P> = NodeType::Simple(Box::<NoopAudioProcessor<f32>>::default());
176        let input_node = dag.add_node(());
177        let _output_proc: NodeType<P> = NodeType::Simple(Box::<NoopAudioProcessor<f32>>::default());
178        let output_node = dag.add_node(());
179
180        AudioProcessorGraphImpl {
181            input_node,
182            output_node,
183            handle: make_shared(AudioProcessorGraphHandleImpl {
184                input_node,
185                output_node,
186                dag: make_shared_cell(dag),
187                process_order: make_shared_cell(Vec::new()),
188                audio_processor_settings: make_shared_cell(None),
189                processors: make_shared_cell(HashMap::new()),
190                buffers: make_shared_cell(HashMap::new()),
191            }),
192            temporary_buffer: AudioBuffer::empty(),
193        }
194    }
195
196    pub fn from_handle(handle: Shared<AudioProcessorGraphHandleImpl<P>>) -> Self {
197        Self {
198            input_node: handle.input_node,
199            output_node: handle.output_node,
200            handle,
201            temporary_buffer: AudioBuffer::empty(),
202        }
203    }
204
205    pub fn input(&self) -> NodeIndex {
206        self.input_node
207    }
208
209    pub fn output(&self) -> NodeIndex {
210        self.output_node
211    }
212
213    pub fn handle(&self) -> &Shared<AudioProcessorGraphHandleImpl<P>> {
214        &self.handle
215    }
216
217    pub fn add_node(&mut self, processor: NodeType<P>) -> NodeIndex {
218        self.handle.add_node(processor)
219    }
220
221    pub fn add_connection(
222        &mut self,
223        source: NodeIndex,
224        destination: NodeIndex,
225    ) -> Result<ConnectionIndex, AudioProcessorGraphError> {
226        self.handle.add_connection(source, destination)
227    }
228}
229
230impl<P: AudioProcessor<SampleType = f32>> AudioProcessor for AudioProcessorGraphImpl<P> {
231    type SampleType = f32;
232
233    fn prepare(&mut self, context: &mut AudioContext) {
234        let settings = context.settings;
235        self.temporary_buffer
236            .resize(settings.output_channels(), settings.block_size());
237
238        self.handle
239            .audio_processor_settings
240            .set(make_shared(Some(settings)));
241        let buffers = self.handle.buffers.get();
242        for buffer_ref in buffers.values() {
243            let buffer = buffer_ref.deref().0.get();
244            unsafe {
245                (*buffer).resize(settings.output_channels(), settings.block_size());
246            }
247        }
248
249        let handle = self.handle.deref();
250
251        let processors = handle.processors.get();
252        let process_order = handle.process_order.get();
253        let process_order = process_order.deref();
254
255        for node_index in process_order {
256            if let Some(processor) = handle
257                .dag
258                .get()
259                .node_weight(*node_index)
260                .and(processors.get(node_index))
261            {
262                unsafe {
263                    match &mut *processor.deref().0.get() {
264                        NodeType::Simple(processor) => {
265                            processor.prepare(context);
266                        }
267                        NodeType::Static(processor) => {
268                            processor.prepare(context);
269                        }
270                        NodeType::None => {}
271                    }
272                }
273            }
274        }
275    }
276
277    fn process(&mut self, context: &mut AudioContext, data: &mut AudioBuffer<Self::SampleType>) {
278        let num_channels = data.num_channels();
279        let num_samples = data.num_samples();
280        // TODO: this is bad, but I'm not sure how to handle variable size buffers (maybe process multiple times)
281        self.temporary_buffer.resize(num_channels, num_samples);
282
283        let handle = self.handle.deref();
284        let dag = handle.dag.get();
285        let dag = dag.deref();
286        let processors = handle.processors.get();
287        let buffers = handle.buffers.get();
288        let process_order = handle.process_order.get();
289        let process_order = process_order.deref();
290
291        // Push inputs in
292        let mut outputs = dag.children(self.input_node);
293        while let Some((connection_id, _)) = outputs.walk_next(dag) {
294            if let Some(buffer_ref) = dag
295                .edge_weight(connection_id)
296                .and(buffers.get(&connection_id))
297            {
298                let buffer = unsafe { &mut *buffer_ref.deref().0.get() };
299                buffer.resize(num_channels, num_samples);
300                buffer.copy_from(data);
301            }
302        }
303
304        for node_index in process_order {
305            let node_index = *node_index;
306
307            if node_index == self.input_node || node_index == self.output_node {
308                continue;
309            }
310
311            // Silence the temporary buffer
312            for sample in self.temporary_buffer.slice_mut() {
313                *sample = 0.0
314            }
315
316            let inputs = dag.parents(node_index);
317            for (connection_id, _) in inputs.iter(dag) {
318                if let Some(buffer_ref) = dag
319                    .edge_weight(connection_id)
320                    .and(buffers.get(&connection_id))
321                {
322                    let buffer = buffer_ref.deref().0.get();
323                    unsafe {
324                        let buffer = &mut *buffer;
325                        buffer.resize(data.num_channels(), data.num_samples());
326                        self.temporary_buffer.add(buffer);
327                    }
328                }
329            }
330
331            if let Some(processor_ref) =
332                dag.node_weight(node_index).and(processors.get(&node_index))
333            {
334                let processor = processor_ref.deref().0.get();
335                let processor = unsafe { &mut *processor };
336
337                match processor {
338                    NodeType::Simple(processor) => {
339                        processor.process(context, &mut self.temporary_buffer);
340                    }
341                    NodeType::Static(processor) => {
342                        processor.process(context, &mut self.temporary_buffer);
343                    }
344                    NodeType::None => {}
345                }
346            }
347
348            let mut outputs = dag.children(node_index);
349            while let Some((connection_id, _)) = outputs.walk_next(dag) {
350                if let Some(buffer_ref) = dag
351                    .edge_weight(connection_id)
352                    .and(buffers.get(&connection_id))
353                {
354                    let buffer = buffer_ref.deref().0.get();
355                    let buffer = unsafe { &mut *buffer };
356                    buffer.resize(num_channels, num_samples);
357                    buffer.copy_from(&self.temporary_buffer);
358                }
359            }
360        }
361
362        // Push outputs out
363        let inputs = dag.parents(self.output_node);
364
365        // Clear input
366        for d in data.slice_mut() {
367            *d = 0.0;
368        }
369
370        for (connection_id, _) in inputs.iter(dag) {
371            if let Some(buffer_ref) = dag
372                .edge_weight(connection_id)
373                .and(buffers.get(&connection_id))
374            {
375                let buffer = buffer_ref.deref().0.get();
376
377                data.add(unsafe { &*buffer });
378            }
379        }
380    }
381}
382
383#[cfg(test)]
384mod test {
385    use std::time::Duration;
386
387    use assert_no_alloc::assert_no_alloc;
388    use audio_processor_testing_helpers::{assert_f_eq, test_level_equivalence};
389    use audio_processor_testing_helpers::{rms_level, sine_buffer};
390
391    use audio_processor_traits::audio_buffer::AudioBuffer;
392    use audio_processor_traits::simple_processor::MonoCopyProcessor;
393    use audio_processor_utility::gain::GainProcessor;
394    use audio_processor_utility::pan::PanProcessor;
395    use augmented_oscillator::Oscillator;
396
397    use super::*;
398
399    #[test]
400    fn test_create_graph() {
401        let _ = AudioProcessorGraph::default();
402    }
403
404    #[test]
405    fn test_create_graph_and_add_node() {
406        let mut graph = AudioProcessorGraph::default();
407        graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
408            GainProcessor::default(),
409        ))));
410    }
411
412    #[test]
413    fn test_create_graph_and_add_2_nodes() {
414        let mut graph = AudioProcessorGraph::default();
415        let gain1 = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
416            GainProcessor::default(),
417        ))));
418        let gain2 = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
419            GainProcessor::default(),
420        ))));
421        let _connection_id = graph.add_connection(gain1, gain2).unwrap();
422    }
423
424    #[test]
425    fn test_create_heterogeneous_graph() {
426        let mut graph = AudioProcessorGraph::default();
427        let gain = Box::new(MonoCopyProcessor::new(GainProcessor::default()));
428        let gain = graph.add_node(NodeType::Simple(gain));
429        let pan = graph.add_node(NodeType::Simple(Box::new(PanProcessor::default())));
430        let _connection_id = graph.add_connection(gain, pan).unwrap();
431    }
432
433    #[test]
434    fn test_process_empty_graph_is_silent() {
435        type BufferType = AudioBuffer<f32>;
436
437        let mut settings = AudioProcessorSettings::default();
438        settings.input_channels = 1;
439        settings.output_channels = 1;
440        settings.block_size = 1000;
441        let mut context = AudioContext::from(settings);
442
443        let mut empty_buffer = BufferType::empty();
444        empty_buffer.resize(1, 1000);
445        let mut graph = AudioProcessorGraph::default();
446
447        assert!((rms_level(empty_buffer.channel(0)) - 0.0).abs() < f32::EPSILON);
448
449        let mut process_buffer = empty_buffer.clone();
450        graph.prepare(&mut context);
451
452        assert_no_alloc(|| {
453            graph.process(&mut context, &mut process_buffer);
454        });
455
456        test_level_equivalence(
457            empty_buffer.channel(0),
458            process_buffer.channel(0),
459            1000,
460            1000,
461            f32::EPSILON,
462        );
463    }
464
465    #[test]
466    fn test_empty_graph_passthrough() {
467        let mut context = AudioContext::default();
468        context.settings.input_channels = 1;
469        context.settings.output_channels = 1;
470        let mut buffer = AudioBuffer::empty();
471        buffer.resize(1, 4);
472        buffer.set(0, 0, 1.0);
473        buffer.set(0, 1, 2.0);
474        buffer.set(0, 2, 3.0);
475        buffer.set(0, 3, 4.0);
476
477        let mut graph = AudioProcessorGraph::default();
478        graph.add_connection(graph.input(), graph.output()).unwrap();
479        graph.prepare(&mut context);
480        assert_no_alloc(|| {
481            graph.process(&mut context, &mut buffer);
482        });
483        assert_f_eq!(*buffer.get(0, 0), 1.0);
484        assert_f_eq!(*buffer.get(0, 1), 2.0);
485        assert_f_eq!(*buffer.get(0, 2), 3.0);
486        assert_f_eq!(*buffer.get(0, 3), 4.0);
487    }
488
489    #[test]
490    fn test_single_node_graph() {
491        let settings = AudioProcessorSettings::default();
492        let mut context = AudioContext::from(settings);
493        context.settings.input_channels = 1;
494        context.settings.output_channels = 1;
495        context.settings.block_size = 4;
496
497        let mut buffer = AudioBuffer::empty();
498        buffer.resize(1, 4);
499        buffer.set(0, 0, 1.0);
500        buffer.set(0, 1, 2.0);
501        buffer.set(0, 2, 3.0);
502        buffer.set(0, 3, 4.0);
503
504        struct Sum10Node {}
505        impl MonoAudioProcessor for Sum10Node {
506            type SampleType = f32;
507            fn m_process(
508                &mut self,
509                _context: &mut AudioContext,
510                sample: Self::SampleType,
511            ) -> Self::SampleType {
512                sample + 10.0
513            }
514        }
515
516        let sum_10_node = MonoCopyProcessor::new(Sum10Node {});
517
518        let mut graph = AudioProcessorGraph::default();
519        let sum_10_node = graph.add_node(NodeType::Simple(Box::new(sum_10_node)));
520        graph.add_connection(graph.input(), sum_10_node).unwrap();
521        graph.add_connection(sum_10_node, graph.output()).unwrap();
522        graph.prepare(&mut context);
523        assert_no_alloc(|| {
524            graph.process(&mut context, &mut buffer);
525        });
526        assert_f_eq!(*buffer.get(0, 0), 11.0);
527        assert_f_eq!(*buffer.get(0, 1), 12.0);
528        assert_f_eq!(*buffer.get(0, 2), 13.0);
529        assert_f_eq!(*buffer.get(0, 3), 14.0);
530    }
531
532    #[test]
533    fn test_series_node_graph() {
534        let settings = AudioProcessorSettings::default();
535        let mut context = AudioContext::from(settings);
536        context.settings.input_channels = 1;
537        context.settings.output_channels = 1;
538        context.settings.block_size = 4;
539
540        let mut buffer = AudioBuffer::empty();
541        buffer.resize(1, 4);
542        buffer.set(0, 0, 1.0);
543        buffer.set(0, 1, 2.0);
544        buffer.set(0, 2, 3.0);
545        buffer.set(0, 3, 4.0);
546
547        #[derive(Clone)]
548        struct Mult10Node {}
549        impl MonoAudioProcessor for Mult10Node {
550            type SampleType = f32;
551            fn m_process(
552                &mut self,
553                _context: &mut AudioContext,
554                sample: Self::SampleType,
555            ) -> Self::SampleType {
556                sample * 10.0
557            }
558        }
559
560        let mult_10_node = Mult10Node {};
561
562        let mut graph = AudioProcessorGraph::default();
563        let node1 = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
564            mult_10_node.clone(),
565        ))));
566        let node2 = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
567            mult_10_node,
568        ))));
569        graph.add_connection(graph.input(), node1).unwrap();
570        graph.add_connection(node1, node2).unwrap();
571        graph.add_connection(node2, graph.output()).unwrap();
572        graph.prepare(&mut context);
573        assert_no_alloc(|| {
574            graph.process(&mut context, &mut buffer);
575        });
576        assert_f_eq!(*buffer.get(0, 0), 100.0);
577        assert_f_eq!(*buffer.get(0, 1), 200.0);
578        assert_f_eq!(*buffer.get(0, 2), 300.0);
579        assert_f_eq!(*buffer.get(0, 3), 400.0);
580    }
581
582    #[test]
583    fn test_buffer_in_series() {
584        let settings = AudioProcessorSettings::default();
585        let mut context = AudioContext::from(settings);
586        context.settings.input_channels = 1;
587        context.settings.output_channels = 1;
588        let mut buffer = AudioBuffer::empty();
589        buffer.resize(1, 4);
590        buffer.set(0, 0, 1.0);
591        buffer.set(0, 1, 2.0);
592        buffer.set(0, 2, 3.0);
593        buffer.set(0, 3, 4.0);
594
595        #[derive(Clone)]
596        struct Mult10Node {}
597        impl AudioProcessor for Mult10Node {
598            type SampleType = f32;
599            fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<f32>) {
600                for sample in buffer.slice_mut() {
601                    *sample *= 10.0
602                }
603            }
604        }
605
606        let mult_10_node = Mult10Node {};
607
608        let mut graph = AudioProcessorGraph::default();
609        let node1 = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
610        let node2 = graph.add_node(NodeType::Simple(Box::new(mult_10_node)));
611        graph.add_connection(graph.input(), node1).unwrap();
612        graph.add_connection(node1, node2).unwrap();
613        graph.add_connection(node2, graph.output()).unwrap();
614        graph.prepare(&mut context);
615        assert_no_alloc(|| {
616            graph.process(&mut context, &mut buffer);
617        });
618        assert_f_eq!(*buffer.get(0, 0), 100.0);
619        assert_f_eq!(*buffer.get(0, 1), 200.0);
620        assert_f_eq!(*buffer.get(0, 2), 300.0);
621        assert_f_eq!(*buffer.get(0, 3), 400.0);
622    }
623
624    #[test]
625    fn test_buffer_in_parallel() {
626        let mut settings = AudioProcessorSettings::default();
627        settings.input_channels = 1;
628        settings.output_channels = 1;
629        settings.block_size = 4;
630
631        let mut context = AudioContext::from(settings);
632        let mut buffer = AudioBuffer::empty();
633        buffer.resize(1, 4);
634        buffer.set(0, 0, 1.0);
635        buffer.set(0, 1, 2.0);
636        buffer.set(0, 2, 3.0);
637        buffer.set(0, 3, 4.0);
638
639        #[derive(Clone)]
640        struct Mult10Node {}
641        impl AudioProcessor for Mult10Node {
642            type SampleType = f32;
643            fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<f32>) {
644                for sample in buffer.slice_mut() {
645                    *sample *= 10.0
646                }
647            }
648        }
649
650        let mult_10_node = Mult10Node {};
651
652        let mut graph = AudioProcessorGraph::default();
653        let node1 = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
654        let node2 = graph.add_node(NodeType::Simple(Box::new(mult_10_node)));
655        graph.add_connection(graph.input(), node1).unwrap();
656        graph.add_connection(node1, graph.output()).unwrap();
657        graph.add_connection(graph.input(), node2).unwrap();
658        graph.add_connection(node2, graph.output()).unwrap();
659        graph.prepare(&mut context);
660
661        assert_no_alloc(|| {
662            graph.process(&mut context, &mut buffer);
663        });
664
665        assert_f_eq!(*buffer.get(0, 0), 20.0);
666        assert_f_eq!(*buffer.get(0, 1), 40.0);
667        assert_f_eq!(*buffer.get(0, 2), 60.0);
668        assert_f_eq!(*buffer.get(0, 3), 80.0);
669    }
670
671    #[test]
672    fn test_more_complex_graph() {
673        // input -> mult-10 -> mult-10 -> output
674        //     \> mult-10 -> mult-10 ----/
675        let settings = AudioProcessorSettings::default();
676        let mut context = AudioContext::from(settings);
677        context.settings.input_channels = 1;
678        context.settings.output_channels = 1;
679        context.settings.block_size = 4;
680
681        let mut buffer = AudioBuffer::empty();
682        buffer.resize(1, 4);
683        buffer.set(0, 0, 1.0);
684        buffer.set(0, 1, 2.0);
685        buffer.set(0, 2, 3.0);
686        buffer.set(0, 3, 4.0);
687
688        #[derive(Clone)]
689        struct Mult10Node {}
690        impl AudioProcessor for Mult10Node {
691            type SampleType = f32;
692            fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<f32>) {
693                for sample in buffer.slice_mut() {
694                    *sample *= 10.0
695                }
696            }
697        }
698
699        let mult_10_node = Mult10Node {};
700
701        let mut graph = AudioProcessorGraph::default();
702        let node_a1 = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
703        let node_a2 = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
704        let node_b1 = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
705        let node_b2 = graph.add_node(NodeType::Simple(Box::new(mult_10_node)));
706        graph.add_connection(graph.input(), node_a1).unwrap();
707        graph.add_connection(node_a1, node_a2).unwrap();
708        graph.add_connection(node_a2, graph.output()).unwrap();
709        graph.add_connection(graph.input(), node_b1).unwrap();
710        graph.add_connection(node_b1, node_b2).unwrap();
711        graph.add_connection(node_b2, graph.output()).unwrap();
712        graph.prepare(&mut context);
713        assert_no_alloc(|| {
714            graph.process(&mut context, &mut buffer);
715        });
716        assert_f_eq!(*buffer.get(0, 0), 200.0);
717        assert_f_eq!(*buffer.get(0, 1), 400.0);
718        assert_f_eq!(*buffer.get(0, 2), 600.0);
719        assert_f_eq!(*buffer.get(0, 3), 800.0);
720    }
721
722    #[test]
723    fn test_30_node_graph() {
724        // input -> mult-10 -> mult-10 -> output
725        //     \> mult-10 -> mult-10 ----/
726        let settings = AudioProcessorSettings::default();
727        let mut context = AudioContext::from(settings);
728        context.settings.input_channels = 1;
729        context.settings.output_channels = 1;
730        context.settings.block_size = 4;
731
732        let mut buffer = AudioBuffer::empty();
733        buffer.resize(1, 4);
734        buffer.set(0, 0, 1.0);
735        buffer.set(0, 1, 2.0);
736        buffer.set(0, 2, 3.0);
737        buffer.set(0, 3, 4.0);
738
739        #[derive(Clone)]
740        struct Mult10Node {}
741        impl AudioProcessor for Mult10Node {
742            type SampleType = f32;
743            fn process(&mut self, _context: &mut AudioContext, buffer: &mut AudioBuffer<f32>) {
744                for sample in buffer.slice_mut() {
745                    *sample *= 10.0
746                }
747            }
748        }
749
750        let mult_10_node = Mult10Node {};
751
752        let mut graph = AudioProcessorGraph::default();
753        for _i in 0..10 {
754            let mut current_idx = graph.input();
755            for _i in 0..3 {
756                let node = graph.add_node(NodeType::Simple(Box::new(mult_10_node.clone())));
757                graph.add_connection(current_idx, node).unwrap();
758                current_idx = node;
759            }
760            graph.add_connection(current_idx, graph.output()).unwrap();
761        }
762        graph.prepare(&mut context);
763        assert_no_alloc(|| {
764            graph.process(&mut context, &mut buffer);
765        });
766        assert_f_eq!(*buffer.get(0, 0), 10000.0);
767        assert_f_eq!(*buffer.get(0, 1), 20000.0);
768        assert_f_eq!(*buffer.get(0, 2), 30000.0);
769        assert_f_eq!(*buffer.get(0, 3), 40000.0);
770    }
771
772    #[test]
773    fn test_process_empty_graph_passes_through_sine() {
774        type BufferType = AudioBuffer<f32>;
775
776        let mut settings = AudioProcessorSettings::default();
777        settings.set_input_channels(1);
778        settings.set_output_channels(1);
779        let sine_buffer: BufferType = BufferType::from_interleaved(
780            1,
781            &sine_buffer(settings.sample_rate, 440.0, Duration::from_millis(3)),
782        );
783        let mut context = AudioContext::from(settings);
784        assert_eq!(sine_buffer.num_channels(), 1);
785
786        let mut graph = AudioProcessorGraph::default();
787        graph.add_connection(graph.input(), graph.output()).unwrap();
788
789        let mut process_buffer = sine_buffer.clone();
790        graph.prepare(&mut context);
791        assert_no_alloc(|| {
792            graph.process(&mut context, &mut process_buffer);
793        });
794
795        test_level_equivalence(
796            sine_buffer.channel(0),
797            process_buffer.channel(0),
798            settings.block_size(),
799            settings.block_size(),
800            f32::EPSILON,
801        );
802    }
803
804    #[test]
805    fn test_process_sine_generator_is_silent_if_disconnected() {
806        type BufferType = AudioBuffer<f32>;
807
808        let settings = AudioProcessorSettings::default();
809        let mut context = AudioContext::from(settings);
810        context.settings.input_channels = 1;
811        context.settings.output_channels = 1;
812        context.settings.block_size = 1000;
813
814        let mut oscillator = Oscillator::sine(settings.sample_rate);
815        oscillator.set_frequency(440.0);
816        let oscillator = OscillatorProcessor { oscillator };
817
818        let mut empty_buffer: BufferType = AudioBuffer::empty();
819        empty_buffer.resize(1, 1000);
820
821        let mut process_buffer = AudioBuffer::empty();
822        process_buffer.resize(1, 1000);
823
824        // Unconnected
825        let mut graph = AudioProcessorGraph::default();
826        graph.prepare(&mut context);
827        let _oscillator_idx = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
828            oscillator,
829        ))));
830        assert_no_alloc(|| {
831            graph.process(&mut context, &mut process_buffer);
832        });
833
834        test_level_equivalence(
835            empty_buffer.channel(0),
836            process_buffer.channel(0),
837            1000,
838            1000,
839            f32::EPSILON,
840        );
841    }
842
843    #[test]
844    fn test_process_sine_generator_is_silent_if_only_connected_to_input() {
845        let settings = AudioProcessorSettings::default();
846        let mut context = AudioContext::from(settings);
847        context.settings.input_channels = 1;
848        context.settings.output_channels = 1;
849        context.settings.block_size = 1000;
850
851        let mut oscillator = Oscillator::sine(settings.sample_rate);
852        oscillator.set_frequency(440.0);
853        let oscillator = OscillatorProcessor { oscillator };
854
855        let mut empty_buffer = AudioBuffer::empty();
856        empty_buffer.resize(1, 1000);
857
858        let mut process_buffer = AudioBuffer::empty();
859        process_buffer.resize(1, 1000);
860
861        // Connected to input only
862        let mut graph = AudioProcessorGraph::default();
863        graph.prepare(&mut context);
864        let oscillator_idx = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
865            oscillator,
866        ))));
867        graph
868            .add_connection(graph.input_node, oscillator_idx)
869            .unwrap();
870        assert_no_alloc(|| {
871            graph.process(&mut context, &mut process_buffer);
872        });
873
874        test_level_equivalence(
875            empty_buffer.channel(0),
876            process_buffer.channel(0),
877            1000,
878            1000,
879            f32::EPSILON,
880        );
881    }
882
883    #[test]
884    fn test_two_nodes_get_summed_if_connected_to_output() {
885        let settings = AudioProcessorSettings::default();
886        let mut context = AudioContext::from(settings);
887        context.settings.input_channels = 1;
888        context.settings.output_channels = 1;
889
890        struct Node1;
891        impl MonoAudioProcessor for Node1 {
892            type SampleType = f32;
893            fn m_process(
894                &mut self,
895                _context: &mut AudioContext,
896                sample: Self::SampleType,
897            ) -> Self::SampleType {
898                sample + 1.0
899            }
900        }
901
902        struct Node2;
903        impl MonoAudioProcessor for Node2 {
904            type SampleType = f32;
905            fn m_process(
906                &mut self,
907                _context: &mut AudioContext,
908                sample: Self::SampleType,
909            ) -> Self::SampleType {
910                sample + 2.0
911            }
912        }
913
914        let node1 = Node1 {};
915        let node2 = Node2 {};
916
917        let mut graph = AudioProcessorGraph::default();
918        graph.prepare(&mut context);
919
920        let input_idx = graph.input();
921        let output_idx = graph.output();
922        let node1_idx = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(node1))));
923        let node2_idx = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(node2))));
924        graph.add_connection(input_idx, node1_idx).unwrap();
925        graph.add_connection(input_idx, node2_idx).unwrap();
926        graph.add_connection(node1_idx, output_idx).unwrap();
927        graph.add_connection(node2_idx, output_idx).unwrap();
928
929        let mut process_buffer = AudioBuffer::empty();
930        process_buffer.resize(1, 3);
931        assert_no_alloc(|| {
932            graph.process(&mut context, &mut process_buffer);
933        });
934        let output = process_buffer.channel(0).to_vec();
935        assert_eq!(output, vec![3.0, 3.0, 3.0]);
936    }
937
938    #[test]
939    fn test_process_sine_generator_in_the_graph_produces_sine() {
940        type BufferType = AudioBuffer<f32>;
941        let settings = AudioProcessorSettings::default();
942        let mut context = AudioContext::from(settings);
943        context.settings.input_channels = 1;
944        context.settings.output_channels = 1;
945        context.settings.block_size = 1000;
946
947        let mut oscillator = Oscillator::sine(settings.sample_rate);
948        oscillator.set_frequency(440.0);
949        let oscillator = OscillatorProcessor { oscillator };
950
951        let reference_sine: BufferType = BufferType::from_interleaved(
952            1,
953            &sine_buffer(
954                settings.sample_rate,
955                440.0,
956                Duration::from_secs_f32((1.0 / settings.sample_rate) * 1000.0),
957            ),
958        );
959
960        let mut process_buffer = AudioBuffer::empty();
961        process_buffer.resize(1, 1000);
962
963        let mut graph = AudioProcessorGraph::default();
964        graph.prepare(&mut context);
965        let oscillator_idx = graph.add_node(NodeType::Simple(Box::new(MonoCopyProcessor::new(
966            oscillator,
967        ))));
968        graph
969            .add_connection(graph.input_node, oscillator_idx)
970            .unwrap();
971        graph
972            .add_connection(oscillator_idx, graph.output_node)
973            .unwrap();
974        assert_no_alloc(|| {
975            graph.process(&mut context, &mut process_buffer);
976        });
977
978        test_level_equivalence(
979            reference_sine.channel(0),
980            process_buffer.channel(0),
981            1000,
982            1000,
983            f32::EPSILON,
984        );
985    }
986}
987
988// Testing helper
989#[derive(Clone)]
990pub struct OscillatorProcessor {
991    pub oscillator: Oscillator<f32>,
992}
993
994impl MonoAudioProcessor for OscillatorProcessor {
995    type SampleType = f32;
996
997    fn m_prepare(&mut self, context: &mut AudioContext) {
998        self.oscillator
999            .set_sample_rate(context.settings.sample_rate);
1000    }
1001
1002    fn m_process(
1003        &mut self,
1004        _context: &mut AudioContext,
1005        _sample: Self::SampleType,
1006    ) -> Self::SampleType {
1007        self.oscillator.next_sample()
1008    }
1009}