use std::sync::{atomic::AtomicU64, Arc};
use crate::{
effects::MultiChannelBiQuad,
helpers::{prepapre_cache_vec, sum_simd},
voice::VoiceControlData,
AudioStreamParams, ChannelCount,
};
use xsynth_soundfonts::FilterType;
use self::{control::ControlEventData, key::KeyData, params::VoiceChannelParams};
use super::AudioPipe;
use rayon::prelude::*;
mod channel_sf;
mod control;
mod key;
mod params;
mod voice_buffer;
mod voice_spawner;
mod event;
pub use event::*;
pub(crate) use control::ValueLerp;
pub use params::VoiceChannelStatsReader;
struct Key {
data: KeyData,
audio_cache: Vec<f32>,
event_cache: Vec<KeyNoteEvent>,
}
impl Key {
pub fn new(key: u8, shared_voice_counter: Arc<AtomicU64>, options: ChannelInitOptions) -> Self {
Key {
data: KeyData::new(key, shared_voice_counter, options),
audio_cache: Vec::new(),
event_cache: Vec::new(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq)]
#[cfg_attr(
feature = "serde",
derive(serde::Deserialize, serde::Serialize),
serde(default)
)]
pub struct ChannelInitOptions {
pub fade_out_killing: bool,
}
#[allow(clippy::derivable_impls)]
impl Default for ChannelInitOptions {
fn default() -> Self {
Self {
fade_out_killing: false,
}
}
}
pub struct VoiceChannel {
key_voices: Vec<Key>,
params: VoiceChannelParams,
threadpool: Option<Arc<rayon::ThreadPool>>,
stream_params: AudioStreamParams,
control_event_data: ControlEventData,
voice_control_data: VoiceControlData,
cutoff: MultiChannelBiQuad,
}
impl VoiceChannel {
pub fn new(
options: ChannelInitOptions,
stream_params: AudioStreamParams,
threadpool: Option<Arc<rayon::ThreadPool>>,
) -> VoiceChannel {
fn fill_key_array<T, F: Fn(u8) -> T>(func: F) -> Vec<T> {
let mut vec = Vec::with_capacity(128);
for i in 0..128 {
vec.push(func(i));
}
vec
}
let params = VoiceChannelParams::new(stream_params);
let shared_voice_counter = params.stats.voice_counter.clone();
VoiceChannel {
params,
key_voices: fill_key_array(|i| Key::new(i, shared_voice_counter.clone(), options)),
threadpool,
stream_params,
control_event_data: ControlEventData::new_defaults(stream_params.sample_rate),
voice_control_data: VoiceControlData::new_defaults(),
cutoff: MultiChannelBiQuad::new(
stream_params.channels.count() as usize,
FilterType::LowPass,
stream_params.sample_rate as f32 / 2.0,
stream_params.sample_rate as f32,
None,
),
}
}
fn apply_channel_effects(&mut self, out: &mut [f32]) {
let control = &mut self.control_event_data;
match self.stream_params.channels {
ChannelCount::Mono => {
for sample in out.iter_mut() {
let vol = control.volume.get_next() * control.expression.get_next();
let vol = vol.powi(2);
*sample *= vol;
}
}
ChannelCount::Stereo => {
for sample in out.chunks_mut(2) {
let vol = control.volume.get_next() * control.expression.get_next();
let vol = vol.powi(2);
sample[0] *= vol;
sample[1] *= vol;
}
for sample in out.chunks_mut(2) {
let pan = control.pan.get_next();
sample[0] *= ((pan * std::f32::consts::PI / 2.0).cos()).min(1.0);
sample[1] *= ((pan * std::f32::consts::PI / 2.0).sin()).min(1.0);
}
}
}
if let Some(cutoff) = control.cutoff {
self.cutoff
.set_filter_type(FilterType::LowPass, cutoff, control.resonance);
self.cutoff.process(out);
}
}
fn push_key_events_and_render(&mut self, out: &mut [f32]) {
self.params.load_program();
out.fill(0.0);
match self.threadpool.as_ref() {
Some(pool) => {
let len = out.len();
let key_voices = &mut self.key_voices;
let params = &self.params;
let control_data = &self.voice_control_data;
pool.install(|| {
key_voices.par_iter_mut().for_each(move |key| {
for e in key.event_cache.drain(..) {
key.data
.send_event(e, control_data, ¶ms.channel_sf, params.layers);
}
prepapre_cache_vec(&mut key.audio_cache, len, 0.0);
key.data.render_to(&mut key.audio_cache);
});
});
for key in self.key_voices.iter() {
sum_simd(&key.audio_cache, out);
}
}
None => {
for key in self.key_voices.iter_mut() {
for e in key.event_cache.drain(..) {
key.data.send_event(
e,
&self.voice_control_data,
&self.params.channel_sf,
self.params.layers,
);
}
key.data.render_to(out);
}
}
}
self.apply_channel_effects(out);
}
fn propagate_voice_controls(&mut self) {
for key in self.key_voices.iter_mut() {
key.data.process_controls(&self.voice_control_data);
}
}
fn kill_voices_in_exclusive_class(&mut self, class: u8) {
for key in self.key_voices.iter_mut() {
key.data.kill_by_exclusive_class(class);
}
}
pub fn process_event(&mut self, event: ChannelEvent) {
self.push_events_iter(std::iter::once(event));
}
pub fn push_events_iter<T: Iterator<Item = ChannelEvent>>(&mut self, iter: T) {
for e in iter {
match e {
ChannelEvent::Audio(audio) => match audio {
ChannelAudioEvent::NoteOn { key, vel } => {
let classes: Vec<_> = self
.params
.channel_sf
.exclusive_classes_attack(key, vel)
.collect();
for class in classes {
self.kill_voices_in_exclusive_class(class);
}
if let Some(key) = self.key_voices.get_mut(key as usize) {
let ev = KeyNoteEvent::On(vel);
key.event_cache.push(ev);
}
}
ChannelAudioEvent::NoteOff { key } => {
if let Some(key) = self.key_voices.get_mut(key as usize) {
let ev = KeyNoteEvent::Off;
key.event_cache.push(ev);
}
}
ChannelAudioEvent::AllNotesOff => {
for key in self.key_voices.iter_mut() {
let ev = KeyNoteEvent::AllOff;
key.event_cache.push(ev);
}
}
ChannelAudioEvent::AllNotesKilled => {
for key in self.key_voices.iter_mut() {
let ev = KeyNoteEvent::AllKilled;
key.event_cache.push(ev);
}
}
ChannelAudioEvent::ResetControl => {
self.reset_control();
}
ChannelAudioEvent::Control(control) => {
self.process_control_event(control);
}
ChannelAudioEvent::ProgramChange(preset) => {
self.params.set_preset(preset);
}
ChannelAudioEvent::SystemReset => {
for key in self.key_voices.iter_mut() {
key.event_cache.clear();
key.event_cache.push(KeyNoteEvent::AllKilled);
}
self.reset_control();
self.reset_program();
}
},
ChannelEvent::Config(config) => self.params.process_config_event(config),
}
}
}
pub fn get_channel_stats(&self) -> VoiceChannelStatsReader {
let stats = self.params.stats.clone();
VoiceChannelStatsReader::new(stats)
}
}
impl AudioPipe for VoiceChannel {
fn stream_params(&self) -> &AudioStreamParams {
&self.params.constant.stream_params
}
fn read_samples_unchecked(&mut self, out: &mut [f32]) {
self.push_key_events_and_render(out);
}
}