use four_cc::FourCC;
use crate::{
modulation::{
matrix::ModulationMatrix,
processor::MODULATION_PROCESSOR_BLOCK_SIZE,
state::{ModulationSlotType, ModulationState},
ModulationConfig, ModulationSource, ModulationTarget,
},
utils::dsp::lfo::LfoWaveform,
Error,
};
use super::{granular::GranularParameterModulation, voice::SamplerVoice};
#[derive(Debug)]
pub(crate) struct SamplerModulationState {
inner: ModulationState,
}
impl SamplerModulationState {
pub fn new(config: ModulationConfig) -> Self {
let inner = ModulationState::new(config);
Self { inner }
}
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(
&mut self,
id: FourCC,
rate: Option<f32>,
waveform: Option<LfoWaveform>,
voices: &mut [SamplerVoice],
) -> Result<(), Error> {
for source_config in self.inner.config().sources.iter() {
match source_config {
ModulationSource::Lfo {
rate_param,
waveform_param,
..
} => {
let source_id = source_config.id();
let lfo_index = if let Some(ModulationSlotType::Lfo(index)) =
self.inner.source_slot_map().get(&source_id)
{
*index
} else {
continue;
};
if id == rate_param.id() {
if let Some(rate) = rate {
for voice in voices {
voice
.modulation_matrix_mut()
.expect("Should have a valid modulation matrix when modulation is enabled")
.update_lfo_rate(lfo_index, rate as f64);
}
}
return Ok(());
} else if id == waveform_param.id() {
if let Some(waveform) = waveform {
for voice in voices {
voice
.modulation_matrix_mut()
.expect("Should have a valid modulation matrix when modulation is enabled")
.update_lfo_waveform(lfo_index, waveform);
}
}
return Ok(());
}
}
ModulationSource::Envelope { .. } => {
panic!("Not expecting envelope modulation source for a sampler");
}
ModulationSource::Velocity { .. } | ModulationSource::Keytracking { .. } => {
}
}
}
Err(Error::ParameterError(format!(
"Invalid/unknown modulation parameter {id}"
)))
}
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 SamplerVoiceModulationState {
matrix: ModulationMatrix,
size: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
density: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
variation: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
spray: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
pan_spread: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
position: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
speed: [f32; MODULATION_PROCESSOR_BLOCK_SIZE],
}
impl SamplerVoiceModulationState {
pub fn new(matrix: ModulationMatrix) -> Self {
Self {
matrix,
size: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
density: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
variation: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
spray: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
pan_spread: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
position: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
speed: [0.0; MODULATION_PROCESSOR_BLOCK_SIZE],
}
}
#[inline]
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) {
use super::Sampler;
debug_assert!(
chunk_size <= MODULATION_PROCESSOR_BLOCK_SIZE,
"Frames exceeds maximum block size"
);
self.matrix.process(chunk_size);
self.matrix.output(
Sampler::GRAIN_SIZE.id(), &mut self.size[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_DENSITY.id(), &mut self.density[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_VARIATION.id(),
&mut self.variation[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_SPRAY.id(), &mut self.spray[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_PAN_SPREAD.id(),
&mut self.pan_spread[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_POSITION.id(),
&mut self.position[..chunk_size],
);
self.matrix.output(
Sampler::GRAIN_STEP.id(), &mut self.speed[..chunk_size],
);
}
pub fn output<'a>(&'a self, frame_count: usize) -> GranularParameterModulation<'a> {
GranularParameterModulation {
size: &self.size[..frame_count],
density: &self.density[..frame_count],
variation: &self.variation[..frame_count],
spray: &self.spray[..frame_count],
pan_spread: &self.pan_spread[..frame_count],
position: &self.position[..frame_count],
speed: &self.speed[..frame_count],
}
}
}