use crate::{
blocks::{
effectors::{
ambisonic_decoder::AmbisonicDecoderBlock, binaural_decoder::BinauralDecoderBlock,
channel_merger::ChannelMergerBlock, channel_router::ChannelRouterBlock,
channel_splitter::ChannelSplitterBlock, dc_blocker::DcBlockerBlock, gain::GainBlock,
low_pass_filter::LowPassFilterBlock, matrix_mixer::MatrixMixerBlock, mixer::MixerBlock,
overdrive::OverdriveBlock, panner::PannerBlock, vca::VcaBlock,
},
generators::oscillator::OscillatorBlock,
io::{file_input::FileInputBlock, file_output::FileOutputBlock, output::OutputBlock},
modulators::{envelope::EnvelopeBlock, lfo::LfoBlock},
},
channel::ChannelConfig,
context::DspContext,
parameter::{ModulationOutput, Parameter},
sample::Sample,
};
pub(crate) const DEFAULT_EFFECTOR_INPUT_COUNT: usize = 1;
pub(crate) const DEFAULT_EFFECTOR_OUTPUT_COUNT: usize = 1;
pub(crate) const DEFAULT_GENERATOR_INPUT_COUNT: usize = 0;
pub(crate) const DEFAULT_GENERATOR_OUTPUT_COUNT: usize = 1;
pub(crate) const DEFAULT_MODULATOR_INPUT_COUNT: usize = 0;
pub(crate) const DEFAULT_MODULATOR_OUTPUT_COUNT: usize = 1;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct BlockId(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum BlockCategory {
Generator,
Effector,
Modulator,
IO,
}
pub trait Block<S: Sample> {
fn process(&mut self, inputs: &[&[S]], outputs: &mut [&mut [S]], modulation_values: &[S], context: &DspContext);
fn input_count(&self) -> usize;
fn output_count(&self) -> usize;
fn modulation_outputs(&self) -> &[ModulationOutput];
fn channel_config(&self) -> ChannelConfig {
ChannelConfig::Parallel
}
fn set_smoothing(&mut self, _sample_rate: f64, _ramp_time_ms: f64) {}
}
pub enum BlockType<S: Sample> {
FileInput(FileInputBlock<S>),
FileOutput(FileOutputBlock<S>),
Output(OutputBlock<S>),
Oscillator(OscillatorBlock<S>),
AmbisonicDecoder(AmbisonicDecoderBlock<S>),
BinauralDecoder(BinauralDecoderBlock<S>),
ChannelMerger(ChannelMergerBlock<S>),
ChannelRouter(ChannelRouterBlock<S>),
ChannelSplitter(ChannelSplitterBlock<S>),
DcBlocker(DcBlockerBlock<S>),
Gain(GainBlock<S>),
LowPassFilter(LowPassFilterBlock<S>),
MatrixMixer(MatrixMixerBlock<S>),
Mixer(MixerBlock<S>),
Overdrive(OverdriveBlock<S>),
Panner(PannerBlock<S>),
Vca(VcaBlock<S>),
Envelope(EnvelopeBlock<S>),
Lfo(LfoBlock<S>),
}
impl<S: Sample> BlockType<S> {
#[inline]
pub fn process(
&mut self,
inputs: &[&[S]],
outputs: &mut [&mut [S]],
modulation_values: &[S],
context: &DspContext,
) {
match self {
BlockType::FileInput(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::FileOutput(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Output(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Oscillator(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::AmbisonicDecoder(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::BinauralDecoder(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::ChannelMerger(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::ChannelRouter(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::ChannelSplitter(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::DcBlocker(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Gain(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::LowPassFilter(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::MatrixMixer(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Mixer(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Overdrive(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Panner(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Vca(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Envelope(block) => block.process(inputs, outputs, modulation_values, context),
BlockType::Lfo(block) => block.process(inputs, outputs, modulation_values, context),
}
}
#[inline]
pub fn input_count(&self) -> usize {
match self {
BlockType::FileInput(block) => block.input_count(),
BlockType::FileOutput(block) => block.input_count(),
BlockType::Output(block) => block.input_count(),
BlockType::Oscillator(block) => block.input_count(),
BlockType::AmbisonicDecoder(block) => block.input_count(),
BlockType::BinauralDecoder(block) => block.input_count(),
BlockType::ChannelMerger(block) => block.input_count(),
BlockType::ChannelRouter(block) => block.input_count(),
BlockType::ChannelSplitter(block) => block.input_count(),
BlockType::DcBlocker(block) => block.input_count(),
BlockType::Gain(block) => block.input_count(),
BlockType::LowPassFilter(block) => block.input_count(),
BlockType::MatrixMixer(block) => block.input_count(),
BlockType::Mixer(block) => block.input_count(),
BlockType::Overdrive(block) => block.input_count(),
BlockType::Panner(block) => block.input_count(),
BlockType::Vca(block) => block.input_count(),
BlockType::Envelope(block) => block.input_count(),
BlockType::Lfo(block) => block.input_count(),
}
}
#[inline]
pub fn output_count(&self) -> usize {
match self {
BlockType::FileInput(block) => block.output_count(),
BlockType::FileOutput(block) => block.output_count(),
BlockType::Output(block) => block.output_count(),
BlockType::Oscillator(block) => block.output_count(),
BlockType::AmbisonicDecoder(block) => block.output_count(),
BlockType::BinauralDecoder(block) => block.output_count(),
BlockType::ChannelMerger(block) => block.output_count(),
BlockType::ChannelRouter(block) => block.output_count(),
BlockType::ChannelSplitter(block) => block.output_count(),
BlockType::DcBlocker(block) => block.output_count(),
BlockType::Gain(block) => block.output_count(),
BlockType::LowPassFilter(block) => block.output_count(),
BlockType::MatrixMixer(block) => block.output_count(),
BlockType::Mixer(block) => block.output_count(),
BlockType::Overdrive(block) => block.output_count(),
BlockType::Panner(block) => block.output_count(),
BlockType::Vca(block) => block.output_count(),
BlockType::Envelope(block) => block.output_count(),
BlockType::Lfo(block) => block.output_count(),
}
}
#[inline]
pub fn modulation_outputs(&self) -> &[ModulationOutput] {
match self {
BlockType::FileInput(block) => block.modulation_outputs(),
BlockType::FileOutput(block) => block.modulation_outputs(),
BlockType::Output(block) => block.modulation_outputs(),
BlockType::Oscillator(block) => block.modulation_outputs(),
BlockType::AmbisonicDecoder(block) => block.modulation_outputs(),
BlockType::BinauralDecoder(block) => block.modulation_outputs(),
BlockType::ChannelMerger(block) => block.modulation_outputs(),
BlockType::ChannelRouter(block) => block.modulation_outputs(),
BlockType::ChannelSplitter(block) => block.modulation_outputs(),
BlockType::DcBlocker(block) => block.modulation_outputs(),
BlockType::Gain(block) => block.modulation_outputs(),
BlockType::LowPassFilter(block) => block.modulation_outputs(),
BlockType::MatrixMixer(block) => block.modulation_outputs(),
BlockType::Mixer(block) => block.modulation_outputs(),
BlockType::Overdrive(block) => block.modulation_outputs(),
BlockType::Panner(block) => block.modulation_outputs(),
BlockType::Vca(block) => block.modulation_outputs(),
BlockType::Envelope(block) => block.modulation_outputs(),
BlockType::Lfo(block) => block.modulation_outputs(),
}
}
#[inline]
pub fn channel_config(&self) -> ChannelConfig {
match self {
BlockType::FileInput(block) => block.channel_config(),
BlockType::FileOutput(block) => block.channel_config(),
BlockType::Output(block) => block.channel_config(),
BlockType::Oscillator(block) => block.channel_config(),
BlockType::AmbisonicDecoder(block) => block.channel_config(),
BlockType::BinauralDecoder(block) => block.channel_config(),
BlockType::ChannelMerger(block) => block.channel_config(),
BlockType::ChannelRouter(block) => block.channel_config(),
BlockType::ChannelSplitter(block) => block.channel_config(),
BlockType::DcBlocker(block) => block.channel_config(),
BlockType::Gain(block) => block.channel_config(),
BlockType::LowPassFilter(block) => block.channel_config(),
BlockType::MatrixMixer(block) => block.channel_config(),
BlockType::Mixer(block) => block.channel_config(),
BlockType::Overdrive(block) => block.channel_config(),
BlockType::Panner(block) => block.channel_config(),
BlockType::Vca(block) => block.channel_config(),
BlockType::Envelope(block) => block.channel_config(),
BlockType::Lfo(block) => block.channel_config(),
}
}
pub fn set_smoothing(&mut self, sample_rate: f64, ramp_time_ms: f64) {
match self {
BlockType::Panner(block) => block.set_smoothing(sample_rate, ramp_time_ms),
BlockType::Gain(block) => block.set_smoothing(sample_rate, ramp_time_ms),
BlockType::Overdrive(block) => block.set_smoothing(sample_rate, ramp_time_ms),
_ => {} }
}
pub fn set_parameter(&mut self, parameter_name: &str, parameter: Parameter<S>) -> Result<(), String> {
match self {
BlockType::FileInput(_) => Err("File input blocks have no modulated parameters".to_string()),
BlockType::FileOutput(_) => Err("File output blocks have no modulated parameters".to_string()),
BlockType::Output(_) => Err("Output blocks have no modulated parameters".to_string()),
BlockType::Oscillator(block) => match parameter_name.to_lowercase().as_str() {
"frequency" => {
block.frequency = parameter;
Ok(())
}
"pitch_offset" => {
block.pitch_offset = parameter;
Ok(())
}
_ => Err(format!("Unknown oscillator parameter: {parameter_name}")),
},
BlockType::AmbisonicDecoder(_) => Err("Ambisonic decoder has no modulated parameters".to_string()),
BlockType::BinauralDecoder(_) => Err("Binaural decoder has no modulated parameters".to_string()),
BlockType::ChannelMerger(_) => Err("Channel merger has no modulated parameters".to_string()),
BlockType::ChannelRouter(_) => Err("Channel router uses direct field access, not Parameter<S>".to_string()),
BlockType::ChannelSplitter(_) => Err("Channel splitter has no modulated parameters".to_string()),
BlockType::DcBlocker(_) => Err("DC blocker uses direct field access, not Parameter<S>".to_string()),
BlockType::Gain(block) => match parameter_name.to_lowercase().as_str() {
"level" | "level_db" => {
block.level_db = parameter;
Ok(())
}
_ => Err(format!("Unknown gain parameter: {parameter_name}")),
},
BlockType::LowPassFilter(block) => match parameter_name.to_lowercase().as_str() {
"cutoff" | "frequency" => {
block.cutoff = parameter;
Ok(())
}
"resonance" | "q" => {
block.resonance = parameter;
Ok(())
}
_ => Err(format!("Unknown low-pass filter parameter: {parameter_name}")),
},
BlockType::MatrixMixer(_) => Err("Matrix mixer uses set_gain method, not Parameter<S>".to_string()),
BlockType::Mixer(_) => Err("Mixer has no modulated parameters".to_string()),
BlockType::Overdrive(block) => match parameter_name.to_lowercase().as_str() {
"drive" => {
block.drive = parameter;
Ok(())
}
"level" => {
block.level = parameter;
Ok(())
}
_ => Err(format!("Unknown overdrive parameter: {parameter_name}")),
},
BlockType::Panner(block) => match parameter_name.to_lowercase().as_str() {
"position" | "pan" => {
block.position = parameter;
Ok(())
}
"azimuth" => {
block.azimuth = parameter;
Ok(())
}
"elevation" => {
block.elevation = parameter;
Ok(())
}
_ => Err(format!("Unknown panner parameter: {parameter_name}")),
},
BlockType::Vca(_) => Err("VCA has no modulated parameters".to_string()),
BlockType::Envelope(block) => match parameter_name.to_lowercase().as_str() {
"attack" => {
block.attack = parameter;
Ok(())
}
"decay" => {
block.decay = parameter;
Ok(())
}
"sustain" => {
block.sustain = parameter;
Ok(())
}
"release" => {
block.release = parameter;
Ok(())
}
_ => Err(format!("Unknown envelope parameter: {parameter_name}")),
},
BlockType::Lfo(block) => match parameter_name.to_lowercase().as_str() {
"frequency" => {
block.frequency = parameter;
Ok(())
}
"depth" => {
block.depth = parameter;
Ok(())
}
_ => Err(format!("Unknown LFO parameter: {parameter_name}")),
},
}
}
#[inline]
pub fn is_modulator(&self) -> bool {
matches!(self, BlockType::Envelope(_) | BlockType::Lfo(_))
}
#[inline]
pub fn is_output(&self) -> bool {
matches!(self, BlockType::Output(_) | BlockType::FileOutput(_))
}
#[inline]
pub fn category(&self) -> BlockCategory {
match self {
BlockType::FileInput(_) | BlockType::FileOutput(_) | BlockType::Output(_) => BlockCategory::IO,
BlockType::Oscillator(_) => BlockCategory::Generator,
BlockType::AmbisonicDecoder(_)
| BlockType::BinauralDecoder(_)
| BlockType::ChannelMerger(_)
| BlockType::ChannelRouter(_)
| BlockType::ChannelSplitter(_)
| BlockType::DcBlocker(_)
| BlockType::Gain(_)
| BlockType::LowPassFilter(_)
| BlockType::MatrixMixer(_)
| BlockType::Mixer(_)
| BlockType::Overdrive(_)
| BlockType::Panner(_)
| BlockType::Vca(_) => BlockCategory::Effector,
BlockType::Envelope(_) | BlockType::Lfo(_) => BlockCategory::Modulator,
}
}
#[inline]
pub fn name(&self) -> &'static str {
match self {
BlockType::FileInput(_) => "File Input",
BlockType::FileOutput(_) => "File Output",
BlockType::Output(_) => "Output",
BlockType::Oscillator(_) => "Oscillator",
BlockType::AmbisonicDecoder(_) => "Ambisonic Decoder",
BlockType::BinauralDecoder(_) => "Binaural Decoder",
BlockType::ChannelMerger(_) => "Channel Merger",
BlockType::ChannelRouter(_) => "Channel Router",
BlockType::ChannelSplitter(_) => "Channel Splitter",
BlockType::DcBlocker(_) => "DC Blocker",
BlockType::Gain(_) => "Gain",
BlockType::LowPassFilter(_) => "Low Pass Filter",
BlockType::MatrixMixer(_) => "Matrix Mixer",
BlockType::Mixer(_) => "Mixer",
BlockType::Overdrive(_) => "Overdrive",
BlockType::Panner(_) => "Panner",
BlockType::Vca(_) => "VCA",
BlockType::Envelope(_) => "Envelope",
BlockType::Lfo(_) => "LFO",
}
}
pub fn get_modulated_parameters(&self) -> Vec<(&'static str, BlockId)> {
let mut result = Vec::new();
match self {
BlockType::FileInput(_) | BlockType::FileOutput(_) | BlockType::Output(_) => {}
BlockType::Oscillator(block) => {
if let Parameter::Modulated(id) = &block.frequency {
result.push(("frequency", *id));
}
if let Parameter::Modulated(id) = &block.pitch_offset {
result.push(("pitch_offset", *id));
}
}
BlockType::AmbisonicDecoder(_)
| BlockType::BinauralDecoder(_)
| BlockType::ChannelMerger(_)
| BlockType::ChannelRouter(_)
| BlockType::ChannelSplitter(_)
| BlockType::DcBlocker(_)
| BlockType::MatrixMixer(_)
| BlockType::Mixer(_)
| BlockType::Vca(_) => {}
BlockType::Gain(block) => {
if let Parameter::Modulated(id) = &block.level_db {
result.push(("level", *id));
}
}
BlockType::LowPassFilter(block) => {
if let Parameter::Modulated(id) = &block.cutoff {
result.push(("cutoff", *id));
}
if let Parameter::Modulated(id) = &block.resonance {
result.push(("resonance", *id));
}
}
BlockType::Overdrive(block) => {
if let Parameter::Modulated(id) = &block.drive {
result.push(("drive", *id));
}
if let Parameter::Modulated(id) = &block.level {
result.push(("level", *id));
}
}
BlockType::Panner(block) => {
if let Parameter::Modulated(id) = &block.position {
result.push(("position", *id));
}
if let Parameter::Modulated(id) = &block.azimuth {
result.push(("azimuth", *id));
}
if let Parameter::Modulated(id) = &block.elevation {
result.push(("elevation", *id));
}
}
BlockType::Envelope(block) => {
if let Parameter::Modulated(id) = &block.attack {
result.push(("attack", *id));
}
if let Parameter::Modulated(id) = &block.decay {
result.push(("decay", *id));
}
if let Parameter::Modulated(id) = &block.sustain {
result.push(("sustain", *id));
}
if let Parameter::Modulated(id) = &block.release {
result.push(("release", *id));
}
}
BlockType::Lfo(block) => {
if let Parameter::Modulated(id) = &block.frequency {
result.push(("frequency", *id));
}
if let Parameter::Modulated(id) = &block.depth {
result.push(("depth", *id));
}
}
}
result
}
}
impl<S: Sample> From<FileInputBlock<S>> for BlockType<S> {
fn from(block: FileInputBlock<S>) -> Self {
BlockType::FileInput(block)
}
}
impl<S: Sample> From<FileOutputBlock<S>> for BlockType<S> {
fn from(block: FileOutputBlock<S>) -> Self {
BlockType::FileOutput(block)
}
}
impl<S: Sample> From<OutputBlock<S>> for BlockType<S> {
fn from(block: OutputBlock<S>) -> Self {
BlockType::Output(block)
}
}
impl<S: Sample> From<OscillatorBlock<S>> for BlockType<S> {
fn from(block: OscillatorBlock<S>) -> Self {
BlockType::Oscillator(block)
}
}
impl<S: Sample> From<AmbisonicDecoderBlock<S>> for BlockType<S> {
fn from(block: AmbisonicDecoderBlock<S>) -> Self {
BlockType::AmbisonicDecoder(block)
}
}
impl<S: Sample> From<BinauralDecoderBlock<S>> for BlockType<S> {
fn from(block: BinauralDecoderBlock<S>) -> Self {
BlockType::BinauralDecoder(block)
}
}
impl<S: Sample> From<ChannelMergerBlock<S>> for BlockType<S> {
fn from(block: ChannelMergerBlock<S>) -> Self {
BlockType::ChannelMerger(block)
}
}
impl<S: Sample> From<ChannelRouterBlock<S>> for BlockType<S> {
fn from(block: ChannelRouterBlock<S>) -> Self {
BlockType::ChannelRouter(block)
}
}
impl<S: Sample> From<ChannelSplitterBlock<S>> for BlockType<S> {
fn from(block: ChannelSplitterBlock<S>) -> Self {
BlockType::ChannelSplitter(block)
}
}
impl<S: Sample> From<DcBlockerBlock<S>> for BlockType<S> {
fn from(block: DcBlockerBlock<S>) -> Self {
BlockType::DcBlocker(block)
}
}
impl<S: Sample> From<GainBlock<S>> for BlockType<S> {
fn from(block: GainBlock<S>) -> Self {
BlockType::Gain(block)
}
}
impl<S: Sample> From<LowPassFilterBlock<S>> for BlockType<S> {
fn from(block: LowPassFilterBlock<S>) -> Self {
BlockType::LowPassFilter(block)
}
}
impl<S: Sample> From<MatrixMixerBlock<S>> for BlockType<S> {
fn from(block: MatrixMixerBlock<S>) -> Self {
BlockType::MatrixMixer(block)
}
}
impl<S: Sample> From<MixerBlock<S>> for BlockType<S> {
fn from(block: MixerBlock<S>) -> Self {
BlockType::Mixer(block)
}
}
impl<S: Sample> From<OverdriveBlock<S>> for BlockType<S> {
fn from(block: OverdriveBlock<S>) -> Self {
BlockType::Overdrive(block)
}
}
impl<S: Sample> From<PannerBlock<S>> for BlockType<S> {
fn from(block: PannerBlock<S>) -> Self {
BlockType::Panner(block)
}
}
impl<S: Sample> From<VcaBlock<S>> for BlockType<S> {
fn from(block: VcaBlock<S>) -> Self {
BlockType::Vca(block)
}
}
impl<S: Sample> From<EnvelopeBlock<S>> for BlockType<S> {
fn from(block: EnvelopeBlock<S>) -> Self {
BlockType::Envelope(block)
}
}
impl<S: Sample> From<LfoBlock<S>> for BlockType<S> {
fn from(block: LfoBlock<S>) -> Self {
BlockType::Lfo(block)
}
}