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
use std::sync::Arc;

use crate::{
    channel::{ChannelAudioEvent, ChannelEvent, ChannelInitOptions, VoiceChannel},
    helpers::sum_simd,
    AudioPipe, AudioStreamParams,
};

mod events;
pub use events::*;
use rayon::prelude::*;

const MAX_EVENT_CACHE_SIZE: u32 = 1024 * 1024;

/// Represents a MIDI synthesizer within XSynth.
///
/// Manages multiple VoiceChannel objects at once.
pub struct ChannelGroup {
    thread_pool: rayon::ThreadPool,
    cached_event_count: u32,
    channel_events_cache: Box<[Vec<ChannelAudioEvent>]>,
    sample_cache_vecs: Box<[Vec<f32>]>,
    channels: Box<[VoiceChannel]>,
    audio_params: AudioStreamParams,
}

/// Options for initializing a new ChannelGroup.
pub struct ChannelGroupConfig {
    /// Channel initialization options (same for all channels).
    /// See the `ChannelInitOptions` documentation for more information.
    pub channel_init_options: ChannelInitOptions,

    /// Amount of VoiceChannel objects to be created
    /// (Number of MIDI channels)
    /// The MIDI 1 spec uses 16 channels.
    pub channel_count: u32,

    /// A vector which specifies which of the created channels (indexes) will be used for drums.
    ///
    /// For example in a conventional 16 MIDI channel setup where channel 10 is used for
    /// drums, the vector would be set as vec!\[9\] (counting from 0).
    pub drums_channels: Vec<u32>,

    /// Parameters of the output audio.
    /// See the `AudioStreamParams` documentation for more information.
    pub audio_params: AudioStreamParams,

    /// Whether or not to use a threadpool to render individual keys' voices.
    /// Regardless, each MIDI channel uses its own thread. This setting
    /// adds more fine-grained threading per key rather than per channel.
    pub use_threadpool: bool,
}

impl ChannelGroup {
    /// Creates a new ChannelGroup with the given configuration.
    /// See the `ChannelGroupConfig` documentation for the available options.
    pub fn new(config: ChannelGroupConfig) -> Self {
        let mut channels = Vec::new();
        let mut channel_events_cache = Vec::new();
        let mut sample_cache_vecs = Vec::new();

        // Thread pool for individual channels to split between keys
        let pool = if config.use_threadpool {
            Some(Arc::new(rayon::ThreadPoolBuilder::new().build().unwrap()))
        } else {
            None
        };

        for i in 0..config.channel_count {
            let mut init = config.channel_init_options;
            init.drums_only = config.drums_channels.clone().into_iter().any(|c| c == i);

            channels.push(VoiceChannel::new(init, config.audio_params, pool.clone()));
            channel_events_cache.push(Vec::new());
            sample_cache_vecs.push(Vec::new());
        }

        // Thread pool for splitting channels between threads
        let thread_pool = rayon::ThreadPoolBuilder::new().build().unwrap();

        Self {
            thread_pool,
            cached_event_count: 0,
            channel_events_cache: channel_events_cache.into_boxed_slice(),
            channels: channels.into_boxed_slice(),
            sample_cache_vecs: sample_cache_vecs.into_boxed_slice(),
            audio_params: config.audio_params,
        }
    }

    /// Sends a SynthEvent to the ChannelGroup.
    /// See the `SynthEvent` documentation for more information.
    pub fn send_event(&mut self, event: SynthEvent) {
        match event {
            SynthEvent::Channel(channel, event) => {
                self.channel_events_cache[channel as usize].push(event);
                self.cached_event_count += 1;
                if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
                    self.flush_events();
                }
            }
            SynthEvent::AllChannels(event) => {
                for channel in self.channel_events_cache.iter_mut() {
                    channel.push(event.clone());
                }
                self.cached_event_count += self.channel_events_cache.len() as u32;
                if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
                    self.flush_events();
                }
            }
            SynthEvent::ChannelConfig(config) => {
                for channel in self.channels.iter_mut() {
                    channel.process_event(ChannelEvent::Config(config.clone()));
                }
            }
        }
    }

    fn flush_events(&mut self) {
        if self.cached_event_count == 0 {
            return;
        }

        let thread_pool = &mut self.thread_pool;
        let channels = &mut self.channels;
        let channel_events_cache = &mut self.channel_events_cache;

        thread_pool.install(move || {
            channels
                .par_iter_mut()
                .zip(channel_events_cache.par_iter_mut())
                .for_each(|(channel, events)| {
                    channel.push_events_iter(events.drain(..).map(ChannelEvent::Audio));
                });
        });

        self.cached_event_count = 0;
    }

    fn render_to(&mut self, buffer: &mut [f32]) {
        self.flush_events();

        let thread_pool = &mut self.thread_pool;
        let channels = &mut self.channels;
        let sample_cache_vecs = &mut self.sample_cache_vecs;

        buffer.fill(0.0);
        thread_pool.install(move || {
            channels
                .par_iter_mut()
                .zip(sample_cache_vecs.par_iter_mut())
                .for_each(|(channel, samples)| {
                    samples.resize(buffer.len(), 0.0);
                    channel.read_samples(samples.as_mut_slice());
                });

            for vec in sample_cache_vecs.iter_mut() {
                sum_simd(vec, buffer);
                vec.clear();
            }
        });
    }

    /// Returns the active voice count of the synthesizer.
    pub fn voice_count(&self) -> u64 {
        self.channels
            .iter()
            .map(|c| c.get_channel_stats().voice_count())
            .sum()
    }
}

impl AudioPipe for ChannelGroup {
    fn stream_params(&self) -> &AudioStreamParams {
        &self.audio_params
    }

    fn read_samples_unchecked(&mut self, to: &mut [f32]) {
        self.render_to(to);
    }
}