diff --git a/core/benches/send_events.rs b/core/benches/send_events.rs index 247c62d..976d89e 100644 --- a/core/benches/send_events.rs +++ b/core/benches/send_events.rs @@ -58,7 +58,6 @@ fn criterion_benchmark(c: &mut Criterion) { f.iter(|| { let init = ChannelInitOptions { fade_out_killing: false, - ..Default::default() }; let mut channel = VoiceChannel::new(init, stream_params, None); channel.process_event(ChannelEvent::Config(ChannelConfigEvent::SetSoundfonts( @@ -76,7 +75,6 @@ fn criterion_benchmark(c: &mut Criterion) { f.iter(|| { let init = ChannelInitOptions { fade_out_killing: true, - ..Default::default() }; let mut channel = VoiceChannel::new(init, stream_params, None); channel.process_event(ChannelEvent::Config(ChannelConfigEvent::SetSoundfonts( @@ -94,7 +92,6 @@ fn criterion_benchmark(c: &mut Criterion) { f.iter(|| { let init = ChannelInitOptions { fade_out_killing: false, - ..Default::default() }; let mut channel = VoiceChannel::new(init, stream_params, None); channel.process_event(ChannelEvent::Config(ChannelConfigEvent::SetSoundfonts( @@ -112,7 +109,6 @@ fn criterion_benchmark(c: &mut Criterion) { f.iter(|| { let init = ChannelInitOptions { fade_out_killing: true, - ..Default::default() }; let mut channel = VoiceChannel::new(init, stream_params, None); channel.process_event(ChannelEvent::Config(ChannelConfigEvent::SetSoundfonts( diff --git a/core/src/channel/channel_sf.rs b/core/src/channel/channel_sf.rs index 8910853..7f69e67 100644 --- a/core/src/channel/channel_sf.rs +++ b/core/src/channel/channel_sf.rs @@ -8,11 +8,16 @@ use crate::{ use super::voice_spawner::VoiceSpawnerMatrix; +#[derive(Default, PartialEq, Eq, Clone, Copy, Debug)] +pub struct ProgramDescriptor { + pub bank: u8, + pub preset: u8, +} + pub struct ChannelSoundfont { soundfonts: Vec>, matrix: VoiceSpawnerMatrix, - curr_bank: u8, - curr_preset: u8, + curr_program: ProgramDescriptor, } impl Deref for ChannelSoundfont { @@ -29,8 +34,7 @@ impl ChannelSoundfont { ChannelSoundfont { soundfonts: Vec::new(), matrix: VoiceSpawnerMatrix::new(), - curr_bank: 0, - curr_preset: 0, + curr_program: Default::default(), } } @@ -41,10 +45,9 @@ impl ChannelSoundfont { } } - pub fn change_program(&mut self, bank: u8, preset: u8) { - if self.curr_bank != bank || self.curr_preset != preset { - self.curr_bank = bank; - self.curr_preset = preset; + pub fn change_program(&mut self, program: ProgramDescriptor) { + if self.curr_program != program { + self.curr_program = program; self.rebuild_matrix(); } } @@ -55,8 +58,8 @@ impl ChannelSoundfont { // if a preset/instr. has regions in any bank other than 0, all missing banks will be muted. // For drum patches the same applies with bank and preset switched. - let bank = self.curr_bank; - let preset = self.curr_preset; + let bank = self.curr_program.bank; + let preset = self.curr_program.preset; for k in 0..128u8 { for v in 0..128u8 { diff --git a/core/src/channel/event.rs b/core/src/channel/event.rs index 772174d..1ba71db 100644 --- a/core/src/channel/event.rs +++ b/core/src/channel/event.rs @@ -28,6 +28,10 @@ pub enum ChannelConfigEvent { /// Sets the layer count for the soundfont SetLayerCount(Option), + + /// Controls whether the channel will be standard or percussion. + /// Setting to `true` will make the channel only use percussion patches. + SetPercussionMode(bool), } /// MIDI events for a channel. diff --git a/core/src/channel/mod.rs b/core/src/channel/mod.rs index 6be2f3c..31a6f47 100644 --- a/core/src/channel/mod.rs +++ b/core/src/channel/mod.rs @@ -92,12 +92,10 @@ struct ControlEventData { cutoff: Option, resonance: Option, expression: ValueLerp, - preset: u8, - bank: u8, } impl ControlEventData { - pub fn new_defaults(sample_rate: u32, drums_only: bool) -> Self { + pub fn new_defaults(sample_rate: u32) -> Self { ControlEventData { selected_lsb: -1, selected_msb: -1, @@ -114,8 +112,6 @@ impl ControlEventData { cutoff: None, resonance: None, expression: ValueLerp::new(1.0, sample_rate), - preset: 0, - bank: if drums_only { 128 } else { 0 }, } } } @@ -130,11 +126,6 @@ pub struct ChannelInitOptions { /// /// Default: `false` pub fade_out_killing: bool, - - /// If set to true, the channel will only use drum patches. - /// - /// Default: `false` - pub drums_only: bool, } #[allow(clippy::derivable_impls)] @@ -142,7 +133,6 @@ impl Default for ChannelInitOptions { fn default() -> Self { Self { fade_out_killing: false, - drums_only: false, } } } @@ -173,7 +163,6 @@ pub struct VoiceChannel { threadpool: Option>, stream_params: AudioStreamParams, - options: ChannelInitOptions, /// The helper struct for keeping track of MIDI control event data control_event_data: ControlEventData, @@ -216,12 +205,8 @@ impl VoiceChannel { threadpool, stream_params, - options, - control_event_data: ControlEventData::new_defaults( - stream_params.sample_rate, - options.drums_only, - ), + control_event_data: ControlEventData::new_defaults(stream_params.sample_rate), voice_control_data: VoiceControlData::new_defaults(), cutoff: MultiChannelBiQuad::new( @@ -273,9 +258,7 @@ impl VoiceChannel { } fn push_key_events_and_render(&mut self, out: &mut [f32]) { - self.params - .channel_sf - .change_program(self.control_event_data.bank, self.control_event_data.preset); + self.params.load_program(); out.fill(0.0); match self.threadpool.as_ref() { @@ -332,9 +315,7 @@ impl VoiceChannel { ControlEvent::Raw(controller, value) => match controller { 0x00 => { // Bank select - if !self.options.drums_only { - self.control_event_data.bank = value; - } + self.params.set_bank(value); } 0x64 => { self.control_event_data.selected_lsb = value as i8; @@ -563,7 +544,7 @@ impl VoiceChannel { self.process_control_event(control); } ChannelAudioEvent::ProgramChange(preset) => { - self.control_event_data.preset = preset; + self.params.set_preset(preset); } }, ChannelEvent::Config(config) => self.params.process_config_event(config), @@ -579,8 +560,7 @@ impl VoiceChannel { } fn reset_control(&mut self) { - self.control_event_data = - ControlEventData::new_defaults(self.stream_params.sample_rate, self.options.drums_only); + self.control_event_data = ControlEventData::new_defaults(self.stream_params.sample_rate); self.voice_control_data = VoiceControlData::new_defaults(); self.process_event(ChannelEvent::Audio(ChannelAudioEvent::ProgramChange(0))); self.propagate_voice_controls(); diff --git a/core/src/channel/params.rs b/core/src/channel/params.rs index 7f4ac0f..4101520 100644 --- a/core/src/channel/params.rs +++ b/core/src/channel/params.rs @@ -2,7 +2,10 @@ use std::sync::{atomic::AtomicU64, Arc}; use crate::AudioStreamParams; -use super::{channel_sf::ChannelSoundfont, ChannelConfigEvent}; +use super::{ + channel_sf::{ChannelSoundfont, ProgramDescriptor}, + ChannelConfigEvent, +}; /// Holds the statistics for an instance of VoiceChannel. #[derive(Debug, Clone)] @@ -24,6 +27,7 @@ pub struct VoiceChannelParams { pub stats: VoiceChannelStats, pub layers: Option, pub channel_sf: ChannelSoundfont, + pub program: ProgramDescriptor, pub constant: VoiceChannelConst, } @@ -48,6 +52,7 @@ impl VoiceChannelParams { stats: VoiceChannelStats::new(), layers: Some(4), channel_sf, + program: Default::default(), constant: VoiceChannelConst { stream_params }, } } @@ -60,8 +65,30 @@ impl VoiceChannelParams { ChannelConfigEvent::SetLayerCount(count) => { self.layers = count; } + ChannelConfigEvent::SetPercussionMode(set) => { + if set { + self.program.bank = 128; + } else { + self.program.bank = 0; + } + self.channel_sf.change_program(self.program); + } + } + } + + pub fn set_bank(&mut self, bank: u8) { + if self.program.bank != 128 { + self.program.bank = bank.min(127); } } + + pub fn set_preset(&mut self, preset: u8) { + self.program.preset = preset.min(127); + } + + pub fn load_program(&mut self) { + self.channel_sf.change_program(self.program); + } } impl VoiceChannelStatsReader { diff --git a/core/src/channel_group/config.rs b/core/src/channel_group/config.rs index 62ac15d..fe1397b 100644 --- a/core/src/channel_group/config.rs +++ b/core/src/channel_group/config.rs @@ -1,5 +1,17 @@ use crate::{channel::ChannelInitOptions, AudioStreamParams}; +/// Controls the channel format that will be used in the synthesizer. +#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)] +#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] +pub enum SynthFormat { + /// Standard MIDI format with 16 channels. Channel 10 will be used for percussion. + #[default] + MidiSingle, + + /// Creates a custom number of channels with the default settings. + Custom { channels: u32 }, +} + /// Defines the multithreading options for each task that supports it. #[derive(Clone, Copy, Debug, Eq, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] @@ -69,16 +81,9 @@ pub struct ChannelGroupConfig { /// 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, + /// Defines the format that the synthesizer will use. See the `SynthFormat` + /// documentation for more information. + pub format: SynthFormat, /// Parameters of the output audio. /// See the `AudioStreamParams` documentation for more information. diff --git a/core/src/channel_group/events.rs b/core/src/channel_group/events.rs index d94eda5..8e76da4 100644 --- a/core/src/channel_group/events.rs +++ b/core/src/channel_group/events.rs @@ -1,18 +1,14 @@ -use crate::channel::{ChannelAudioEvent, ChannelConfigEvent}; +use crate::channel::ChannelEvent; /// Wrapper enum for various events to be sent to a MIDI synthesizer. #[derive(Clone, Debug)] #[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))] pub enum SynthEvent { - /// An audio event to be sent to the specified channel. - /// See `ChannelAudioEvent` documentation for more information. - Channel(u32, ChannelAudioEvent), + /// A channel event to be sent to the specified channel. + /// See `ChannelEvent` documentation for more information. + Channel(u32, ChannelEvent), - /// An audio event to be sent to all available channels. + /// A channel event to be sent to all available channels. /// See `ChannelAudioEvent` documentation for more information. - AllChannels(ChannelAudioEvent), - - /// Configuration event for all channels. - /// See `ChannelConfigEvent` documentation for more information. - ChannelConfig(ChannelConfigEvent), + AllChannels(ChannelEvent), } diff --git a/core/src/channel_group/mod.rs b/core/src/channel_group/mod.rs index 9811479..5f78a46 100644 --- a/core/src/channel_group/mod.rs +++ b/core/src/channel_group/mod.rs @@ -1,7 +1,7 @@ use std::sync::Arc; use crate::{ - channel::{ChannelAudioEvent, ChannelEvent, VoiceChannel}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent, VoiceChannel}, helpers::{prepapre_cache_vec, sum_simd}, AudioPipe, AudioStreamParams, }; @@ -59,12 +59,14 @@ impl ChannelGroup { ), }; - 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); + let channel_count = match config.format { + SynthFormat::MidiSingle => 16, + SynthFormat::Custom { channels } => channels, + }; + for _ in 0..channel_count { channels.push(VoiceChannel::new( - init, + config.channel_init_options, config.audio_params, channel_pool.clone(), )); @@ -72,6 +74,12 @@ impl ChannelGroup { sample_cache_vecs.push(Vec::new()); } + if config.format == SynthFormat::MidiSingle { + channels[9].push_events_iter(std::iter::once(ChannelEvent::Config( + ChannelConfigEvent::SetPercussionMode(true), + ))); + } + Self { thread_pool: group_pool, cached_event_count: 0, @@ -86,27 +94,32 @@ impl 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); + SynthEvent::Channel(channel, event) => match event { + ChannelEvent::Audio(e) => { + self.channel_events_cache[channel as usize].push(e); + self.cached_event_count += 1; + if self.cached_event_count > MAX_EVENT_CACHE_SIZE { + self.flush_events(); + } } - self.cached_event_count += self.channel_events_cache.len() as u32; - if self.cached_event_count > MAX_EVENT_CACHE_SIZE { - self.flush_events(); + ChannelEvent::Config(_) => self.channels[channel as usize].process_event(event), + }, + SynthEvent::AllChannels(event) => match event { + ChannelEvent::Audio(e) => { + for channel in self.channel_events_cache.iter_mut() { + channel.push(e); + } + 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())); + ChannelEvent::Config(_) => { + for channel in self.channels.iter_mut() { + channel.process_event(event.clone()); + } } - } + }, } } diff --git a/realtime/examples/bench.rs b/realtime/examples/bench.rs index e6ca4e0..fa83e33 100644 --- a/realtime/examples/bench.rs +++ b/realtime/examples/bench.rs @@ -1,5 +1,5 @@ use std::time::{Duration, Instant}; -use xsynth_core::channel::ChannelAudioEvent; +use xsynth_core::channel::{ChannelAudioEvent, ChannelEvent}; use xsynth_realtime::{RealtimeSynth, SynthEvent}; @@ -12,13 +12,13 @@ fn main() { for _ in 0..100 { synth.send_event(SynthEvent::Channel( 0, - ChannelAudioEvent::NoteOn { key: 0, vel: 5 }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: 0, vel: 5 }), )); } for _ in 0..100 { synth.send_event(SynthEvent::Channel( 0, - ChannelAudioEvent::NoteOff { key: 0 }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOff { key: 0 }), )); } } diff --git a/realtime/examples/midi.rs b/realtime/examples/midi.rs index fb4a831..483e993 100644 --- a/realtime/examples/midi.rs +++ b/realtime/examples/midi.rs @@ -14,7 +14,7 @@ use midi_toolkit::{ }, }; use xsynth_core::{ - channel::{ChannelAudioEvent, ChannelConfigEvent, ControlEvent}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent, ControlEvent}, soundfont::{SampleSoundfont, SoundfontBase}, }; use xsynth_realtime::{RealtimeSynth, SynthEvent}; @@ -49,7 +49,9 @@ fn main() { )]; println!("Loaded"); - sender.send_config(ChannelConfigEvent::SetSoundfonts(soundfonts)); + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(soundfonts), + ))); let stats = synth.get_stats(); thread::spawn(move || loop { @@ -97,36 +99,39 @@ fn main() { Event::NoteOn(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOn { + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: e.key, vel: e.velocity, - }, + }), )); } Event::NoteOff(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOff { key: e.key }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOff { key: e.key }), )); } Event::ControlChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::Raw(e.controller, e.value)), + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::Raw( + e.controller, + e.value, + ))), )); } Event::PitchWheelChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::PitchBendValue( - e.pitch as f32 / 8192.0, + ChannelEvent::Audio(ChannelAudioEvent::Control( + ControlEvent::PitchBendValue(e.pitch as f32 / 8192.0), )), )); } Event::ProgramChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::ProgramChange(e.program), + ChannelEvent::Audio(ChannelAudioEvent::ProgramChange(e.program)), )); } _ => {} diff --git a/realtime/examples/midi2.rs b/realtime/examples/midi2.rs index 1c16dd6..c1a3aa0 100644 --- a/realtime/examples/midi2.rs +++ b/realtime/examples/midi2.rs @@ -5,7 +5,7 @@ use std::{ }; use xsynth_core::{ - channel::{ChannelAudioEvent, ChannelConfigEvent, ControlEvent}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent, ControlEvent}, soundfont::{SampleSoundfont, SoundfontBase}, }; @@ -48,7 +48,9 @@ fn main() { SampleSoundfont::new(sfz, params, Default::default()).unwrap(), )]; - sender.send_config(ChannelConfigEvent::SetSoundfonts(soundfonts)); + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(soundfonts), + ))); let midi = MIDIFile::open(&midi, None).unwrap(); @@ -89,36 +91,39 @@ fn main() { Event::NoteOn(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOn { + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: e.key, vel: e.velocity, - }, + }), )); } Event::NoteOff(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOff { key: e.key }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOff { key: e.key }), )); } Event::ControlChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::Raw(e.controller, e.value)), + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::Raw( + e.controller, + e.value, + ))), )); } Event::PitchWheelChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::PitchBendValue( + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::PitchBendValue( e.pitch as f32 / 8192.0, - )), + ))), )); } Event::ProgramChange(e) => { sender.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::ProgramChange(e.program), + ChannelEvent::Audio(ChannelAudioEvent::ProgramChange(e.program)), )); } _ => {} diff --git a/realtime/examples/test.rs b/realtime/examples/test.rs index cd9d723..d9c720c 100644 --- a/realtime/examples/test.rs +++ b/realtime/examples/test.rs @@ -1,6 +1,6 @@ use std::{sync::Arc, time::Duration}; use xsynth_core::{ - channel::{ChannelAudioEvent, ChannelConfigEvent}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent}, soundfont::{SampleSoundfont, SoundfontBase}, }; @@ -31,7 +31,9 @@ fn main() { SampleSoundfont::new(sfz, params, Default::default()).unwrap(), )]; - sender.send_config(ChannelConfigEvent::SetSoundfonts(soundfonts)); + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(soundfonts), + ))); // for k in 0..127 { // for c in 0..16 { @@ -45,7 +47,7 @@ fn main() { // } sender.send_event(SynthEvent::Channel( 0, - ChannelAudioEvent::NoteOn { key: 10, vel: 127 }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: 10, vel: 127 }), )); std::thread::sleep(Duration::from_secs(10000)); diff --git a/realtime/examples/unload.rs b/realtime/examples/unload.rs index 37263b1..bcb6332 100644 --- a/realtime/examples/unload.rs +++ b/realtime/examples/unload.rs @@ -1,7 +1,7 @@ use std::{sync::Arc, time::Duration}; use xsynth_core::{ - channel::{ChannelAudioEvent, ChannelConfigEvent}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent}, soundfont::{SampleSoundfont, SoundfontBase}, }; @@ -34,11 +34,13 @@ fn main() { )]; println!("Loaded"); - sender.send_config(ChannelConfigEvent::SetSoundfonts(soundfonts)); + sender.send_event(SynthEvent::AllChannels(ChannelEvent::Config( + ChannelConfigEvent::SetSoundfonts(soundfonts), + ))); sender.send_event(SynthEvent::Channel( 0, - ChannelAudioEvent::NoteOn { key: 64, vel: 127 }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: 64, vel: 127 }), )); std::thread::sleep(Duration::from_secs(1)); diff --git a/realtime/src/config.rs b/realtime/src/config.rs index e6bd748..4c2da1f 100644 --- a/realtime/src/config.rs +++ b/realtime/src/config.rs @@ -14,19 +14,13 @@ pub struct XSynthRealtimeConfig { /// Default: `10.0` pub render_window_ms: f64, - /// Amount of VoiceChannel objects to be created. - /// (Number of MIDI channels) + /// Amount of VoiceChannel objects to be created (Number of MIDI channels). + /// The MIDI 1 spec uses 16 channels. If the channel count is 16 or + /// greater, then MIDI channel 10 will be set as the percussion channel. /// /// Default: `16` 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 \[9\] (counting from 0). - /// - /// Default: `[9]` - pub drums_channels: Vec, - /// Controls the multithreading used for rendering per-voice audio for all /// the voices stored in a key for a channel. See the `ThreadCount` documentation /// for the available options. @@ -46,7 +40,6 @@ impl Default for XSynthRealtimeConfig { channel_init_options: Default::default(), render_window_ms: 10.0, channel_count: 16, - drums_channels: vec![9], multithreading: ThreadCount::None, ignore_range: 0..=0, } diff --git a/realtime/src/event_senders.rs b/realtime/src/event_senders.rs index 627a182..4fd6df3 100644 --- a/realtime/src/event_senders.rs +++ b/realtime/src/event_senders.rs @@ -234,29 +234,25 @@ impl RealtimeEventSender { /// See the `SynthEvent` documentation for more information. pub fn send_event(&mut self, event: SynthEvent) { match event { - SynthEvent::Channel(channel, event) => { - self.senders[channel as usize].send_audio(event); - } - SynthEvent::AllChannels(event) => { - for sender in self.senders.iter_mut() { - sender.send_audio(event); + SynthEvent::Channel(channel, event) => match event { + ChannelEvent::Audio(e) => self.senders[channel as usize].send_audio(e), + ChannelEvent::Config(e) => self.senders[channel as usize].send_config(e), + }, + SynthEvent::AllChannels(event) => match event { + ChannelEvent::Audio(e) => { + for sender in self.senders.iter_mut() { + sender.send_audio(e); + } } - } - SynthEvent::ChannelConfig(event) => { - for sender in self.senders.iter_mut() { - sender.send_config(event.clone()); + ChannelEvent::Config(e) => { + for sender in self.senders.iter_mut() { + sender.send_config(e.clone()); + } } - } + }, } } - /// Sends a ChannelConfigEvent to the realtime synthesizer. - /// - /// See the `ChannelConfigEvent` documentation for more information. - pub fn send_config(&mut self, event: ChannelConfigEvent) { - self.send_event(SynthEvent::ChannelConfig(event)) - } - /// Sends a MIDI event as raw bytes. pub fn send_event_u32(&mut self, event: u32) { let head = event & 0xFF; @@ -279,28 +275,31 @@ impl RealtimeEventSender { 0x8 => { self.send_event(SynthEvent::Channel( channel, - ChannelAudioEvent::NoteOff { key: val1!() }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOff { key: val1!() }), )); } 0x9 => { self.send_event(SynthEvent::Channel( channel, - ChannelAudioEvent::NoteOn { + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: val1!(), vel: val2!(), - }, + }), )); } 0xB => { self.send_event(SynthEvent::Channel( channel, - ChannelAudioEvent::Control(ControlEvent::Raw(val1!(), val2!())), + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::Raw( + val1!(), + val2!(), + ))), )); } 0xC => { self.send_event(SynthEvent::Channel( channel, - ChannelAudioEvent::ProgramChange(val1!()), + ChannelEvent::Audio(ChannelAudioEvent::ProgramChange(val1!())), )); } 0xE => { @@ -308,7 +307,9 @@ impl RealtimeEventSender { let value = value as f32 / 8192.0; self.send_event(SynthEvent::Channel( channel, - ChannelAudioEvent::Control(ControlEvent::PitchBendValue(value)), + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::PitchBendValue( + value, + ))), )); } @@ -318,7 +319,9 @@ impl RealtimeEventSender { /// Resets all note and control change data of the realtime synthesizer. pub fn reset_synth(&mut self) { - self.send_event(SynthEvent::AllChannels(ChannelAudioEvent::AllNotesKilled)); + self.send_event(SynthEvent::AllChannels(ChannelEvent::Audio( + ChannelAudioEvent::AllNotesKilled, + ))); for sender in &mut self.senders { for i in 0..128 { @@ -326,6 +329,8 @@ impl RealtimeEventSender { } } - self.send_event(SynthEvent::AllChannels(ChannelAudioEvent::ResetControl)); + self.send_event(SynthEvent::AllChannels(ChannelEvent::Audio( + ChannelAudioEvent::ResetControl, + ))); } } diff --git a/realtime/src/realtime_synth.rs b/realtime/src/realtime_synth.rs index a659e71..5ff012b 100644 --- a/realtime/src/realtime_synth.rs +++ b/realtime/src/realtime_synth.rs @@ -15,7 +15,7 @@ use crossbeam_channel::{bounded, unbounded}; use xsynth_core::{ buffered_renderer::{BufferedRenderer, BufferedRendererStatsReader}, - channel::VoiceChannel, + channel::{ChannelConfigEvent, ChannelEvent, VoiceChannel}, effects::VolumeLimiter, helpers::{prepapre_cache_vec, sum_simd}, AudioPipe, AudioStreamParams, FunctionAudioPipe, @@ -152,13 +152,9 @@ impl RealtimeSynth { let mut thread_handles = vec![]; - for i in 0u32..(config.channel_count) { - let mut init = config.channel_init_options; - if config.drums_channels.clone().into_iter().any(|c| c == i) { - init.drums_only = true; - } - - let mut channel = VoiceChannel::new(init, stream_params, pool.clone()); + for _ in 0u32..(config.channel_count) { + let mut channel = + VoiceChannel::new(config.channel_init_options, stream_params, pool.clone()); let stats = channel.get_channel_stats(); channel_stats.push(stats); @@ -187,6 +183,14 @@ impl RealtimeSynth { thread_handles.push(join_handle); } + if config.channel_count >= 16 { + senders[9] + .send(ChannelEvent::Config(ChannelConfigEvent::SetPercussionMode( + true, + ))) + .unwrap(); + } + let mut vec_cache: VecDeque> = VecDeque::new(); for _ in 0..(config.channel_count) { vec_cache.push_front(Vec::new()); diff --git a/render/examples/render.rs b/render/examples/render.rs index 1c92d99..226f61f 100644 --- a/render/examples/render.rs +++ b/render/examples/render.rs @@ -69,8 +69,7 @@ fn main() { let config = XSynthRenderConfig { group_options: ChannelGroupConfig { channel_init_options: Default::default(), - channel_count: 16, - drums_channels: vec![9], + format: Default::default(), audio_params: AudioStreamParams::new(48000, 2.into()), parallelism: Default::default(), }, diff --git a/render/examples/render_dialog.rs b/render/examples/render_dialog.rs index f2d39a2..fc49755 100644 --- a/render/examples/render_dialog.rs +++ b/render/examples/render_dialog.rs @@ -83,8 +83,7 @@ fn main() { let config = XSynthRenderConfig { group_options: ChannelGroupConfig { channel_init_options: Default::default(), - channel_count: 16, - drums_channels: vec![9], + format: Default::default(), audio_params: AudioStreamParams::new(sample_rate, 2.into()), parallelism: if use_threadpool { Default::default() diff --git a/render/src/builder.rs b/render/src/builder.rs index de185cd..6d8ee87 100644 --- a/render/src/builder.rs +++ b/render/src/builder.rs @@ -6,7 +6,7 @@ use crate::{ use std::sync::Arc; use xsynth_core::{ - channel::{ChannelAudioEvent, ChannelConfigEvent, ControlEvent}, + channel::{ChannelAudioEvent, ChannelConfigEvent, ChannelEvent, ControlEvent}, channel_group::SynthEvent, soundfont::{LoadSfError, SoundfontBase}, }; @@ -23,7 +23,7 @@ use midi_toolkit::{ }, }; -pub use xsynth_core::channel_group::ParallelismOptions; +pub use xsynth_core::channel_group::{ParallelismOptions, SynthFormat}; /// Statistics of an XSynthRender object. pub struct XSynthRenderStats { @@ -86,8 +86,8 @@ impl<'a, ProgressCallback: FnMut(XSynthRenderStats)> XSynthRenderBuilder<'a, Pro self } - pub fn with_channel_count(mut self, channels: u32) -> Self { - self.config.group_options.channel_count = channels; + pub fn with_synth_format(mut self, format: SynthFormat) -> Self { + self.config.group_options.format = format; self } @@ -146,17 +146,17 @@ impl<'a, ProgressCallback: FnMut(XSynthRenderStats)> XSynthRenderBuilder<'a, Pro pub fn run(mut self) -> Result<(), XSynthRenderError> { let mut synth = XSynthRender::new(self.config.clone(), self.out_path.into()); - synth.send_event(SynthEvent::ChannelConfig( + synth.send_event(SynthEvent::AllChannels(ChannelEvent::Config( ChannelConfigEvent::SetSoundfonts( self.soundfonts .drain(..) .collect::>>(), ), - )); + ))); - synth.send_event(SynthEvent::ChannelConfig( + synth.send_event(SynthEvent::AllChannels(ChannelEvent::Config( ChannelConfigEvent::SetLayerCount(self.layer_count), - )); + ))); let midi = MIDIFile::open(self.midi_path, None)?; @@ -185,44 +185,51 @@ impl<'a, ProgressCallback: FnMut(XSynthRenderStats)> XSynthRenderBuilder<'a, Pro Event::NoteOn(e) => { synth.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOn { + ChannelEvent::Audio(ChannelAudioEvent::NoteOn { key: e.key, vel: e.velocity, - }, + }), )); } Event::NoteOff(e) => { synth.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::NoteOff { key: e.key }, + ChannelEvent::Audio(ChannelAudioEvent::NoteOff { key: e.key }), )); } Event::ControlChange(e) => { synth.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::Raw(e.controller, e.value)), + ChannelEvent::Audio(ChannelAudioEvent::Control(ControlEvent::Raw( + e.controller, + e.value, + ))), )); } Event::PitchWheelChange(e) => { synth.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::Control(ControlEvent::PitchBendValue( - e.pitch as f32 / 8192.0, + ChannelEvent::Audio(ChannelAudioEvent::Control( + ControlEvent::PitchBendValue(e.pitch as f32 / 8192.0), )), )); } Event::ProgramChange(e) => { synth.send_event(SynthEvent::Channel( e.channel as u32, - ChannelAudioEvent::ProgramChange(e.program), + ChannelEvent::Audio(ChannelAudioEvent::ProgramChange(e.program)), )); } _ => {} } } } - synth.send_event(SynthEvent::AllChannels(ChannelAudioEvent::AllNotesOff)); - synth.send_event(SynthEvent::AllChannels(ChannelAudioEvent::ResetControl)); + synth.send_event(SynthEvent::AllChannels(ChannelEvent::Audio( + ChannelAudioEvent::AllNotesOff, + ))); + synth.send_event(SynthEvent::AllChannels(ChannelEvent::Audio( + ChannelAudioEvent::ResetControl, + ))); synth.finalize(); Ok(())