use std::collections::HashMap;
use four_cc::FourCC;
use crate::{
modulation::{
matrix::ModulationMatrix,
processor::MODULATION_PROCESSOR_BLOCK_SIZE,
state::{ModulationSlotType, ModulationState},
ModulationConfig, ModulationSource, ModulationTarget,
},
utils::{dsp::lfo::LfoWaveform, fundsp::SharedBuffer},
};
use super::parameter::SharedParameterValue;
pub(crate) struct FunDspModulationState {
inner: ModulationState,
}
impl FunDspModulationState {
pub fn new(config: ModulationConfig) -> Self {
Self {
inner: ModulationState::new(config),
}
}
pub fn create_matrix(&self, sample_rate: u32) -> ModulationMatrix {
self.inner.create_matrix(sample_rate)
}
pub fn is_source_parameter(&self, id: FourCC) -> bool {
self.inner.is_source_parameter(id)
}
pub fn sources(&self) -> Vec<ModulationSource> {
self.inner.sources()
}
pub fn targets(&self) -> Vec<ModulationTarget> {
self.inner.targets()
}
pub fn apply_parameter_update(
&self,
matrix: &mut ModulationMatrix,
param_id: FourCC,
shared_params: &HashMap<FourCC, SharedParameterValue>,
) {
for source_config in self.inner.config().sources.iter() {
match source_config {
ModulationSource::Lfo {
rate_param,
waveform_param,
..
} => {
if let Some(ModulationSlotType::Lfo(lfo_index)) =
self.inner.source_slot_map().get(&source_config.id())
{
if param_id == rate_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
let rate = param.shared().value();
matrix.update_lfo_rate(*lfo_index, rate as f64);
}
} else if param_id == waveform_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
let waveform_index = param.shared().value().round() as usize;
let waveform =
<LfoWaveform as strum::VariantArray>::VARIANTS[waveform_index];
matrix.update_lfo_waveform(*lfo_index, waveform);
}
}
}
}
ModulationSource::Envelope {
attack_param,
hold_param,
decay_param,
sustain_param,
release_param,
..
} => {
if let Some(ModulationSlotType::Envelope(env_index)) =
self.inner.source_slot_map().get(&source_config.id())
{
if param_id == attack_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
matrix.update_envelope_attack(*env_index, param.shared().value());
}
} else if param_id == hold_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
matrix.update_envelope_hold(*env_index, param.shared().value());
}
} else if param_id == decay_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
matrix.update_envelope_decay(*env_index, param.shared().value());
}
} else if param_id == sustain_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
matrix.update_envelope_sustain(*env_index, param.shared().value());
}
} else if param_id == release_param.id() {
if let Some(param) = shared_params.get(¶m_id) {
matrix.update_envelope_release(*env_index, param.shared().value());
}
}
}
}
ModulationSource::Velocity { .. } | ModulationSource::Keytracking { .. } => {
}
}
}
}
pub fn set_modulation(
&self,
matrix: &mut ModulationMatrix,
source: FourCC,
target: FourCC,
amount: f32,
bipolar: bool,
) -> Result<(), crate::Error> {
self.inner
.set_modulation(matrix, source, target, amount, bipolar)
}
pub fn clear_modulation(
&self,
matrix: &mut ModulationMatrix,
source: FourCC,
target: FourCC,
) -> Result<(), crate::Error> {
self.inner.clear_modulation(matrix, source, target)
}
}
pub(crate) struct FunDSpModulationVoiceState {
matrix: ModulationMatrix,
shared_buffers: HashMap<FourCC, SharedBuffer>,
temp_buffer: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
}
impl FunDSpModulationVoiceState {
pub fn new(matrix: ModulationMatrix, shared_buffers: HashMap<FourCC, SharedBuffer>) -> Self {
let temp_buffer = [0.0; MODULATION_PROCESSOR_BLOCK_SIZE];
Self {
matrix,
shared_buffers,
temp_buffer,
}
}
#[inline]
#[allow(unused)]
pub fn matrix(&self) -> &ModulationMatrix {
&self.matrix
}
#[inline]
pub fn matrix_mut(&mut self) -> &mut ModulationMatrix {
&mut self.matrix
}
pub fn start(&mut self, note: u8, volume: f32) {
self.matrix.note_on(note, volume);
}
pub fn stop(&mut self) {
self.matrix.note_off();
}
pub fn process(&mut self, chunk_size: usize) {
debug_assert!(
chunk_size <= MODULATION_PROCESSOR_BLOCK_SIZE,
"Frames exceeds maximum block size"
);
self.matrix.process(chunk_size);
for (param_id, shared_buffer) in &mut self.shared_buffers {
self.matrix
.output(*param_id, &mut self.temp_buffer[..chunk_size]);
shared_buffer.write(&self.temp_buffer[..chunk_size]);
}
}
pub fn clear(&mut self) {
for buffer in self.shared_buffers.values_mut() {
buffer.clear();
}
}
}