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
use std::sync::{atomic::AtomicI64, Arc};

use crate::{realtime::Processor, AudioProcess, Command, CommandQueue, Context, Timestamp};

use super::context::NotifierStatus;

pub struct Root {
    sample_rate: usize,
    timestamp: Arc<AtomicI64>,
    command_transmitter: CommandTransmitter,
    notifiers: Vec<Box<dyn Fn() -> NotifierStatus>>,
    maximum_frame_count: usize,
}

impl Context for Root {
    fn start(&mut self) {
        self.command_transmitter.send(Command::Start);
    }

    fn stop(&mut self) {
        self.command_transmitter.send(Command::Stop);
    }

    fn current_time(&self) -> Timestamp {
        Timestamp::from_raw_i64(self.timestamp.load(std::sync::atomic::Ordering::Acquire))
    }

    fn get_sample_rate(&self) -> usize {
        self.sample_rate
    }

    fn get_command_queue(&self) -> Box<dyn CommandQueue> {
        Box::new(self.command_transmitter.clone())
    }

    fn add_notifier(&mut self, notifier: Box<dyn Fn() -> NotifierStatus>) {
        self.notifiers.push(notifier);
    }

    fn process_notifications(&mut self) {
        self.notifiers
            .retain(|notifier| (notifier)() == NotifierStatus::Continue);
    }

    fn maximum_frame_count(&self) -> usize {
        self.maximum_frame_count
    }
}

/// Options to control the engine
pub struct EngineOptions {
    sample_rate: usize,
    maximum_channel_count: usize,
    maximum_frame_count: usize,
}

impl Default for EngineOptions {
    fn default() -> Self {
        Self {
            sample_rate: 44_100,
            maximum_channel_count: 2,
            maximum_frame_count: 512,
        }
    }
}

impl EngineOptions {
    /// Specify the sample rate of the engine
    pub fn with_sample_rate(mut self, sample_rate: usize) -> Self {
        self.sample_rate = sample_rate;
        self
    }

    /// Specify the maximum channel count between any two nodes in the engine
    ///
    /// A bigger channel count will use more memory
    pub fn with_maximum_channel_count(mut self, maximum_channel_count: usize) -> Self {
        self.maximum_channel_count = maximum_channel_count;
        self
    }

    /// Specify the maximum frame count that will be processed in a single block
    ///
    /// A bigger frame count will result in DSP nodes using more memory.
    /// A smaller frame count might result in higher CPU usage.
    ///
    /// It is okay to call process() with more frames, but they will be internally
    /// processed in smaller blocks.
    pub fn with_maximum_frame_count(mut self, maximum_frame_count: usize) -> Self {
        self.maximum_frame_count = maximum_frame_count;
        self
    }
}

/// Create an engine with the default options
///
/// This returns a pair:
///
/// * The `Context` is the root context. This will be required to create most
///   nodes and should be kept in scope for the lifetime of the application
///
/// * The `AudioProcess` is used to generate audio. This might be passed to a
///   different thread if used in a realtime context, or it might be kept in
///   the main thread if used offline.
pub fn create_engine() -> (Box<dyn Context>, Box<dyn AudioProcess + Send>) {
    create_engine_with_options(EngineOptions::default())
}

/// Create an audio engine with custom options
///
/// This returns a pair:
///
/// * The `Context` is the root context. This will be required to create most
///   nodes and should be kept in scope for the lifetime of the application
///
/// * The `AudioProcess` is used to generate audio. This might be passed to a
///   different thread if used in a realtime context, or it might be kept in
///   the main thread if used offline.
pub fn create_engine_with_options(
    options: EngineOptions,
) -> (Box<dyn Context>, Box<dyn AudioProcess + Send>) {
    let (command_transmitter, command_receiver) = CommandTransmitter::new();

    let timestamp = Arc::new(AtomicI64::new(0));

    let processor = Box::new(Processor::new(
        options.sample_rate,
        options.maximum_channel_count,
        options.maximum_frame_count,
        command_receiver,
        Arc::clone(&timestamp),
    ));

    let engine = Box::new(Root {
        sample_rate: options.sample_rate,
        timestamp,
        command_transmitter,
        notifiers: Vec::new(),
        maximum_frame_count: options.maximum_frame_count,
    });

    (engine, processor)
}

#[derive(Clone)]
struct CommandTransmitter {
    command_tx: crossbeam::channel::Sender<Command>,
}

impl CommandTransmitter {
    fn new() -> (Self, crossbeam::channel::Receiver<Command>) {
        let (command_tx, command_rx) = crossbeam::channel::unbounded();
        (Self { command_tx }, command_rx)
    }
}

impl CommandQueue for CommandTransmitter {
    fn send(&self, command: Command) {
        let _ = self.command_tx.send(command);
    }
}