use std::f32::consts;
use crate::bi_quad_filter::BiQuadFilter;
use crate::channel::Channel;
use crate::lfo::Lfo;
use crate::modulation_envelope::ModulationEnvelope;
use crate::oscillator::Oscillator;
use crate::region_ex::RegionEx;
use crate::region_pair::RegionPair;
use crate::soundfont_math::SoundFontMath;
use crate::synthesizer_settings::SynthesizerSettings;
use crate::volume_envelope::VolumeEnvelope;
#[derive(Debug, PartialEq, Eq)]
#[non_exhaustive]
enum VoiceState {
Playing = 0,
ReleaseRequested = 1,
Released = 2,
}
#[derive(Debug)]
#[non_exhaustive]
pub(crate) struct Voice {
vol_env: VolumeEnvelope,
mod_env: ModulationEnvelope,
vib_lfo: Lfo,
mod_lfo: Lfo,
oscillator: Oscillator,
filter: BiQuadFilter,
block: Vec<f32>,
pub(crate) previous_mix_gain_left: f32,
pub(crate) previous_mix_gain_right: f32,
pub(crate) current_mix_gain_left: f32,
pub(crate) current_mix_gain_right: f32,
pub(crate) previous_reverb_send: f32,
pub(crate) previous_chorus_send: f32,
pub(crate) current_reverb_send: f32,
pub(crate) current_chorus_send: f32,
exclusive_class: i32,
channel: i32,
key: i32,
velocity: i32,
note_gain: f32,
cutoff: f32,
resonance: f32,
vib_lfo_to_pitch: f32,
mod_lfo_to_pitch: f32,
mod_env_to_pitch: f32,
mod_lfo_to_cutoff: i32,
mod_env_to_cutoff: i32,
dynamic_cutoff: bool,
mod_lfo_to_volume: f32,
dynamic_volume: bool,
instrument_pan: f32,
instrument_reverb: f32,
instrument_chorus: f32,
smoothed_cutoff: f32,
cached_pan_angle: f32,
cached_pan_cos: f32,
cached_pan_sin: f32,
pan_angle_is_valid: bool,
voice_state: VoiceState,
voice_length: usize,
min_voice_length: usize,
}
impl Voice {
pub(crate) fn new(settings: &SynthesizerSettings) -> Self {
Self {
vol_env: VolumeEnvelope::new(settings),
mod_env: ModulationEnvelope::new(settings),
vib_lfo: Lfo::new(settings),
mod_lfo: Lfo::new(settings),
oscillator: Oscillator::new(settings),
filter: BiQuadFilter::new(settings),
block: vec![0_f32; settings.block_size],
previous_mix_gain_left: 0_f32,
previous_mix_gain_right: 0_f32,
current_mix_gain_left: 0_f32,
current_mix_gain_right: 0_f32,
previous_reverb_send: 0_f32,
previous_chorus_send: 0_f32,
current_reverb_send: 0_f32,
current_chorus_send: 0_f32,
exclusive_class: 0,
channel: 0,
key: 0,
velocity: 0,
note_gain: 0_f32,
cutoff: 0_f32,
resonance: 0_f32,
vib_lfo_to_pitch: 0_f32,
mod_lfo_to_pitch: 0_f32,
mod_env_to_pitch: 0_f32,
mod_lfo_to_cutoff: 0,
mod_env_to_cutoff: 0,
dynamic_cutoff: false,
mod_lfo_to_volume: 0_f32,
dynamic_volume: false,
instrument_pan: 0_f32,
instrument_reverb: 0_f32,
instrument_chorus: 0_f32,
smoothed_cutoff: 0_f32,
cached_pan_angle: 0_f32,
cached_pan_cos: 0_f32,
cached_pan_sin: 0_f32,
pan_angle_is_valid: false,
voice_state: VoiceState::Playing,
voice_length: 0,
min_voice_length: (settings.sample_rate / 500) as usize,
}
}
pub(crate) fn start(&mut self, region: &RegionPair, channel: i32, key: i32, velocity: i32) {
self.exclusive_class = region.get_exclusive_class();
self.channel = channel;
self.key = key;
self.velocity = velocity;
if velocity > 0 {
let sample_attenuation = 0.4_f32 * region.get_initial_attenuation();
let filter_attenuation = 0.5_f32 * region.get_initial_filter_q();
let decibels = 2_f32 * SoundFontMath::linear_to_decibels(velocity as f32 / 127_f32)
- sample_attenuation
- filter_attenuation;
self.note_gain = SoundFontMath::decibels_to_linear(decibels.min(0_f32));
} else {
self.note_gain = 0_f32;
}
self.cutoff = region.get_initial_filter_cutoff_frequency();
self.resonance = SoundFontMath::decibels_to_linear(region.get_initial_filter_q());
self.vib_lfo_to_pitch = 0.01_f32 * region.get_vibrato_lfo_to_pitch() as f32;
self.mod_lfo_to_pitch = 0.01_f32 * region.get_modulation_lfo_to_pitch() as f32;
self.mod_env_to_pitch = 0.01_f32 * region.get_modulation_envelope_to_pitch() as f32;
self.mod_lfo_to_cutoff = region.get_modulation_lfo_to_filter_cutoff_frequency();
self.mod_env_to_cutoff = region.get_modulation_envelope_to_filter_cutoff_frequency();
self.dynamic_cutoff = self.mod_lfo_to_cutoff != 0 || self.mod_env_to_cutoff != 0;
self.mod_lfo_to_volume = region.get_modulation_lfo_to_volume();
self.dynamic_volume = self.mod_lfo_to_volume > 0.05_f32;
self.instrument_pan = SoundFontMath::clamp(region.get_pan(), -50_f32, 50_f32);
self.instrument_reverb = 0.01_f32 * region.get_reverb_effects_send();
self.instrument_chorus = 0.01_f32 * region.get_chorus_effects_send();
RegionEx::start_volume_envelope(&mut self.vol_env, region, key, velocity);
RegionEx::start_modulation_envelope(&mut self.mod_env, region, key, velocity);
RegionEx::start_vibrato(&mut self.vib_lfo, region, key, velocity);
RegionEx::start_modulation(&mut self.mod_lfo, region, key, velocity);
RegionEx::start_oscillator(&mut self.oscillator, region);
self.filter.clear_buffer();
self.filter.set_low_pass_filter(self.cutoff, self.resonance);
self.smoothed_cutoff = self.cutoff;
self.pan_angle_is_valid = false;
self.voice_state = VoiceState::Playing;
self.voice_length = 0;
}
pub(crate) fn end(&mut self) {
if self.voice_state == VoiceState::Playing {
self.voice_state = VoiceState::ReleaseRequested;
}
}
pub(crate) fn kill(&mut self) {
self.note_gain = 0_f32;
}
pub(crate) fn prepare(&mut self, data: &[i16], channels: &[Channel]) -> bool {
if self.note_gain < SoundFontMath::NON_AUDIBLE {
return false;
}
let channel_info = &channels[self.channel as usize];
self.release_if_necessary(channel_info);
if !self.vol_env.process(self.block.len()) {
return false;
}
self.mod_env.process(self.block.len());
self.vib_lfo.process();
self.mod_lfo.process();
let vib_pitch_change = (0.01_f32 * channel_info.get_modulation() + self.vib_lfo_to_pitch)
* self.vib_lfo.get_value();
let mod_pitch_change = self.mod_lfo_to_pitch * self.mod_lfo.get_value()
+ self.mod_env_to_pitch * self.mod_env.get_value();
let channel_pitch_change = channel_info.get_tune() + channel_info.get_pitch_bend();
let pitch = self.key as f32 + vib_pitch_change + mod_pitch_change + channel_pitch_change;
if !self.oscillator.process(data, &mut self.block[..], pitch) {
return false;
}
if self.dynamic_cutoff {
let cents = self.mod_lfo_to_cutoff as f32 * self.mod_lfo.get_value()
+ self.mod_env_to_cutoff as f32 * self.mod_env.get_value();
let factor = SoundFontMath::cents_to_multiplying_factor(cents);
let new_cutoff = factor * self.cutoff;
let lower_limit = 0.5_f32 * self.smoothed_cutoff;
let upper_limit = 2_f32 * self.smoothed_cutoff;
self.smoothed_cutoff = SoundFontMath::clamp(new_cutoff, lower_limit, upper_limit);
self.filter
.set_low_pass_filter(self.smoothed_cutoff, self.resonance);
}
true
}
pub(crate) fn process_filter(&mut self) {
self.filter.process(&mut self.block[..]);
}
pub(crate) fn process_filters(voices: [&mut Voice; 4]) {
let [v0, v1, v2, v3] = voices;
if v0.filter_is_active()
&& v1.filter_is_active()
&& v2.filter_is_active()
&& v3.filter_is_active()
{
BiQuadFilter::process_four(
(&mut v0.filter, v0.block.as_mut_slice()),
(&mut v1.filter, v1.block.as_mut_slice()),
(&mut v2.filter, v2.block.as_mut_slice()),
(&mut v3.filter, v3.block.as_mut_slice()),
);
} else {
v0.process_filter();
v1.process_filter();
v2.process_filter();
v3.process_filter();
}
}
pub(crate) fn finish(&mut self, channels: &[Channel]) {
self.previous_mix_gain_left = self.current_mix_gain_left;
self.previous_mix_gain_right = self.current_mix_gain_right;
self.previous_reverb_send = self.current_reverb_send;
self.previous_chorus_send = self.current_chorus_send;
let channel_info = &channels[self.channel as usize];
let ve = channel_info.get_volume() * channel_info.get_expression();
let channel_gain = ve * ve;
let mut mix_gain = self.note_gain * channel_gain * self.vol_env.get_value();
if self.dynamic_volume {
let decibels = (self.mod_lfo_to_volume * self.mod_lfo.get_value()).min(0_f32);
mix_gain *= SoundFontMath::decibels_to_linear(decibels);
}
let angle =
(consts::PI / 200_f32) * (channel_info.get_pan() + self.instrument_pan + 50_f32);
if angle <= 0_f32 {
self.current_mix_gain_left = mix_gain;
self.current_mix_gain_right = 0_f32;
} else if angle >= SoundFontMath::HALF_PI {
self.current_mix_gain_left = 0_f32;
self.current_mix_gain_right = mix_gain;
} else {
if !self.pan_angle_is_valid || angle != self.cached_pan_angle {
self.cached_pan_angle = angle;
self.cached_pan_cos = angle.cos();
self.cached_pan_sin = angle.sin();
self.pan_angle_is_valid = true;
}
self.current_mix_gain_left = mix_gain * self.cached_pan_cos;
self.current_mix_gain_right = mix_gain * self.cached_pan_sin;
}
self.current_reverb_send = SoundFontMath::clamp(
channel_info.get_reverb_send() + self.instrument_reverb,
0_f32,
1_f32,
);
self.current_chorus_send = SoundFontMath::clamp(
channel_info.get_chorus_send() + self.instrument_chorus,
0_f32,
1_f32,
);
if self.voice_length == 0 {
self.previous_mix_gain_left = self.current_mix_gain_left;
self.previous_mix_gain_right = self.current_mix_gain_right;
self.previous_reverb_send = self.current_reverb_send;
self.previous_chorus_send = self.current_chorus_send;
}
self.voice_length += self.block.len();
}
fn release_if_necessary(&mut self, channel_info: &Channel) {
if self.voice_length < self.min_voice_length {
return;
}
if self.voice_state == VoiceState::ReleaseRequested && !channel_info.get_hold_pedal() {
self.vol_env.release();
self.mod_env.release();
self.oscillator.release();
self.voice_state = VoiceState::Released;
}
}
pub(crate) fn filter_is_active(&self) -> bool {
self.filter.is_active()
}
pub(crate) fn block(&self) -> &Vec<f32> {
&self.block
}
pub(crate) fn voice_length(&self) -> usize {
self.voice_length
}
pub(crate) fn exclusive_class(&self) -> i32 {
self.exclusive_class
}
pub(crate) fn channel(&self) -> i32 {
self.channel
}
pub(crate) fn key(&self) -> i32 {
self.key
}
pub(crate) fn priority(&self) -> f32 {
if self.note_gain < SoundFontMath::NON_AUDIBLE {
0_f32
} else {
self.vol_env.get_priority()
}
}
}