Skip to main content

xsynth_core/channel_group/
mod.rs

1use std::sync::Arc;
2
3use crate::{
4    channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent, VoiceChannel},
5    helpers::{prepapre_cache_vec, sum_simd},
6    AudioPipe, AudioStreamParams,
7};
8
9mod config;
10pub use config::*;
11mod events;
12pub use events::*;
13use rayon::prelude::*;
14
15const MAX_EVENT_CACHE_SIZE: u32 = 1024 * 1024;
16
17/// Represents a MIDI synthesizer within XSynth.
18///
19/// Manages multiple VoiceChannel objects at once. For info about MIDI CC
20/// support, please see the documentation of the `VoiceChannel` struct.
21pub struct ChannelGroup {
22    thread_pool: Option<rayon::ThreadPool>,
23    cached_event_count: u32,
24    channel_events_cache: Box<[Vec<ChannelAudioEvent>]>,
25    sample_cache_vecs: Box<[Vec<f32>]>,
26    channels: Box<[VoiceChannel]>,
27    audio_params: AudioStreamParams,
28}
29
30impl ChannelGroup {
31    /// Creates a new ChannelGroup with the given configuration.
32    /// See the `ChannelGroupConfig` documentation for the available options.
33    pub fn new(config: ChannelGroupConfig) -> Self {
34        let mut channels = Vec::new();
35        let mut channel_events_cache = Vec::new();
36        let mut sample_cache_vecs = Vec::new();
37
38        // Thread pool for individual channels to split between keys
39        let channel_pool = match config.parallelism.key {
40            ThreadCount::None => None,
41            ThreadCount::Auto => Some(Arc::new(rayon::ThreadPoolBuilder::new().build().unwrap())),
42            ThreadCount::Manual(threads) => Some(Arc::new(
43                rayon::ThreadPoolBuilder::new()
44                    .num_threads(threads)
45                    .build()
46                    .unwrap(),
47            )),
48        };
49
50        // Thread pool for splitting channels between threads
51        let group_pool = match config.parallelism.channel {
52            ThreadCount::None => None,
53            ThreadCount::Auto => Some(rayon::ThreadPoolBuilder::new().build().unwrap()),
54            ThreadCount::Manual(threads) => Some(
55                rayon::ThreadPoolBuilder::new()
56                    .num_threads(threads)
57                    .build()
58                    .unwrap(),
59            ),
60        };
61
62        let channel_count = match config.format {
63            SynthFormat::Midi => 16,
64            SynthFormat::Custom { channels } => channels,
65        };
66
67        for _ in 0..channel_count {
68            channels.push(VoiceChannel::new(
69                config.channel_init_options,
70                config.audio_params,
71                channel_pool.clone(),
72            ));
73            channel_events_cache.push(Vec::new());
74            sample_cache_vecs.push(Vec::new());
75        }
76
77        if config.format == SynthFormat::Midi {
78            channels[9].push_events_iter(std::iter::once(ChannelEvent::Config(
79                ChannelConfigEvent::SetPercussionMode(true),
80            )));
81        }
82
83        Self {
84            thread_pool: group_pool,
85            cached_event_count: 0,
86            channel_events_cache: channel_events_cache.into_boxed_slice(),
87            channels: channels.into_boxed_slice(),
88            sample_cache_vecs: sample_cache_vecs.into_boxed_slice(),
89            audio_params: config.audio_params,
90        }
91    }
92
93    /// Sends a SynthEvent to the ChannelGroup.
94    /// See the `SynthEvent` documentation for more information.
95    pub fn send_event(&mut self, event: SynthEvent) {
96        match event {
97            SynthEvent::Channel(channel, event) => match event {
98                ChannelEvent::Audio(e) => {
99                    if let Some(events) = self.channel_events_cache.get_mut(channel as usize) {
100                        events.push(e);
101                        self.cached_event_count += 1;
102                        if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
103                            self.flush_events();
104                        }
105                    }
106                }
107                ChannelEvent::Config(_) => {
108                    if let Some(channel) = self.channels.get_mut(channel as usize) {
109                        channel.process_event(event);
110                    }
111                }
112            },
113            SynthEvent::AllChannels(event) => match event {
114                ChannelEvent::Audio(e) => {
115                    for channel in self.channel_events_cache.iter_mut() {
116                        channel.push(e);
117                    }
118                    self.cached_event_count += self.channel_events_cache.len() as u32;
119                    if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
120                        self.flush_events();
121                    }
122                }
123                ChannelEvent::Config(_) => {
124                    for channel in self.channels.iter_mut() {
125                        channel.process_event(event.clone());
126                    }
127                }
128            },
129        }
130    }
131
132    fn flush_events(&mut self) {
133        if self.cached_event_count == 0 {
134            return;
135        }
136
137        match self.thread_pool.as_ref() {
138            Some(pool) => {
139                let channels = &mut self.channels;
140                let channel_events_cache = &mut self.channel_events_cache;
141
142                pool.install(move || {
143                    channels
144                        .par_iter_mut()
145                        .zip(channel_events_cache.par_iter_mut())
146                        .for_each(|(channel, events)| {
147                            channel.push_events_iter(events.drain(..).map(ChannelEvent::Audio));
148                        });
149                });
150            }
151            None => {
152                for (channel, events) in self
153                    .channels
154                    .iter_mut()
155                    .zip(self.channel_events_cache.iter_mut())
156                {
157                    channel.push_events_iter(events.drain(..).map(ChannelEvent::Audio));
158                }
159            }
160        }
161
162        self.cached_event_count = 0;
163    }
164
165    fn render_to(&mut self, buffer: &mut [f32]) {
166        self.flush_events();
167        buffer.fill(0.0);
168
169        match self.thread_pool.as_ref() {
170            Some(pool) => {
171                let len = buffer.len();
172                let channels = &mut self.channels;
173                let sample_cache_vecs = &mut self.sample_cache_vecs;
174                pool.install(move || {
175                    channels
176                        .par_iter_mut()
177                        .zip(sample_cache_vecs.par_iter_mut())
178                        .for_each(|(channel, samples)| {
179                            prepapre_cache_vec(samples, len, 0.0);
180                            channel.read_samples(samples.as_mut_slice());
181                        });
182
183                    for vec in sample_cache_vecs.iter_mut() {
184                        sum_simd(vec, buffer);
185                    }
186                });
187            }
188            None => {
189                let len = buffer.len();
190
191                for (channel, samples) in self
192                    .channels
193                    .iter_mut()
194                    .zip(self.sample_cache_vecs.iter_mut())
195                {
196                    prepapre_cache_vec(samples, len, 0.0);
197                    channel.read_samples(samples.as_mut_slice());
198                }
199
200                for vec in self.sample_cache_vecs.iter_mut() {
201                    sum_simd(vec, buffer);
202                }
203            }
204        }
205    }
206
207    /// Returns the active voice count of the synthesizer.
208    pub fn voice_count(&self) -> u64 {
209        self.channels
210            .iter()
211            .map(|c| c.get_channel_stats().voice_count())
212            .sum()
213    }
214}
215
216impl AudioPipe for ChannelGroup {
217    fn stream_params(&self) -> &AudioStreamParams {
218        &self.audio_params
219    }
220
221    fn read_samples_unchecked(&mut self, to: &mut [f32]) {
222        self.render_to(to);
223    }
224}
225
226#[cfg(test)]
227mod tests {
228    use crate::{
229        channel::{ChannelAudioEvent, ChannelEvent, ChannelInitOptions},
230        channel_group::{
231            ChannelGroupConfig, ParallelismOptions, SynthEvent, SynthFormat, ThreadCount,
232        },
233        AudioStreamParams, ChannelCount,
234    };
235
236    use super::ChannelGroup;
237
238    #[test]
239    fn out_of_range_channel_event_is_ignored() {
240        let mut group = ChannelGroup::new(ChannelGroupConfig {
241            channel_init_options: ChannelInitOptions::default(),
242            format: SynthFormat::Custom { channels: 1 },
243            audio_params: AudioStreamParams::new(44_100, ChannelCount::Stereo),
244            parallelism: ParallelismOptions {
245                channel: ThreadCount::None,
246                key: ThreadCount::None,
247            },
248        });
249
250        group.send_event(SynthEvent::Channel(
251            1,
252            ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: 60, vel: 100 }),
253        ));
254
255        assert_eq!(group.voice_count(), 0);
256    }
257}