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                    self.channel_events_cache[channel as usize].push(e);
100                    self.cached_event_count += 1;
101                    if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
102                        self.flush_events();
103                    }
104                }
105                ChannelEvent::Config(_) => self.channels[channel as usize].process_event(event),
106            },
107            SynthEvent::AllChannels(event) => match event {
108                ChannelEvent::Audio(e) => {
109                    for channel in self.channel_events_cache.iter_mut() {
110                        channel.push(e);
111                    }
112                    self.cached_event_count += self.channel_events_cache.len() as u32;
113                    if self.cached_event_count > MAX_EVENT_CACHE_SIZE {
114                        self.flush_events();
115                    }
116                }
117                ChannelEvent::Config(_) => {
118                    for channel in self.channels.iter_mut() {
119                        channel.process_event(event.clone());
120                    }
121                }
122            },
123        }
124    }
125
126    fn flush_events(&mut self) {
127        if self.cached_event_count == 0 {
128            return;
129        }
130
131        match self.thread_pool.as_ref() {
132            Some(pool) => {
133                let channels = &mut self.channels;
134                let channel_events_cache = &mut self.channel_events_cache;
135
136                pool.install(move || {
137                    channels
138                        .par_iter_mut()
139                        .zip(channel_events_cache.par_iter_mut())
140                        .for_each(|(channel, events)| {
141                            channel.push_events_iter(events.drain(..).map(ChannelEvent::Audio));
142                        });
143                });
144            }
145            None => {
146                for (channel, events) in self
147                    .channels
148                    .iter_mut()
149                    .zip(self.channel_events_cache.iter_mut())
150                {
151                    channel.push_events_iter(events.drain(..).map(ChannelEvent::Audio));
152                }
153            }
154        }
155
156        self.cached_event_count = 0;
157    }
158
159    fn render_to(&mut self, buffer: &mut [f32]) {
160        self.flush_events();
161        buffer.fill(0.0);
162
163        match self.thread_pool.as_ref() {
164            Some(pool) => {
165                let len = buffer.len();
166                let channels = &mut self.channels;
167                let sample_cache_vecs = &mut self.sample_cache_vecs;
168                pool.install(move || {
169                    channels
170                        .par_iter_mut()
171                        .zip(sample_cache_vecs.par_iter_mut())
172                        .for_each(|(channel, samples)| {
173                            prepapre_cache_vec(samples, len, 0.0);
174                            channel.read_samples(samples.as_mut_slice());
175                        });
176
177                    for vec in sample_cache_vecs.iter_mut() {
178                        sum_simd(vec, buffer);
179                    }
180                });
181            }
182            None => {
183                let len = buffer.len();
184
185                for (channel, samples) in self
186                    .channels
187                    .iter_mut()
188                    .zip(self.sample_cache_vecs.iter_mut())
189                {
190                    prepapre_cache_vec(samples, len, 0.0);
191                    channel.read_samples(samples.as_mut_slice());
192                }
193
194                for vec in self.sample_cache_vecs.iter_mut() {
195                    sum_simd(vec, buffer);
196                }
197            }
198        }
199    }
200
201    /// Returns the active voice count of the synthesizer.
202    pub fn voice_count(&self) -> u64 {
203        self.channels
204            .iter()
205            .map(|c| c.get_channel_stats().voice_count())
206            .sum()
207    }
208}
209
210impl AudioPipe for ChannelGroup {
211    fn stream_params(&self) -> &AudioStreamParams {
212        &self.audio_params
213    }
214
215    fn read_samples_unchecked(&mut self, to: &mut [f32]) {
216        self.render_to(to);
217    }
218}