213 lines
8.4 KiB
Rust
213 lines
8.4 KiB
Rust
//! Unified wrapper for Mutable Instruments Plaits synthesis engines.
|
|
//!
|
|
//! This module provides a single enum that wraps all 13 synthesis engines from
|
|
//! the `mi_plaits_dsp` crate (a Rust port of the Mutable Instruments Plaits
|
|
//! Eurorack module). Each engine produces sound through a different synthesis
|
|
//! technique.
|
|
//!
|
|
//! # Engine Categories
|
|
//!
|
|
//! ## Pitched Engines
|
|
//! - [`Modal`](PlaitsEngine::Modal) - Physical modeling of resonant structures
|
|
//! - [`Va`](PlaitsEngine::Va) - Virtual analog (classic subtractive synthesis)
|
|
//! - [`Ws`](PlaitsEngine::Ws) - Waveshaping synthesis
|
|
//! - [`Fm`](PlaitsEngine::Fm) - 2-operator FM synthesis
|
|
//! - [`Grain`](PlaitsEngine::Grain) - Granular synthesis
|
|
//! - [`Additive`](PlaitsEngine::Additive) - Additive synthesis with harmonic control
|
|
//! - [`Wavetable`](PlaitsEngine::Wavetable) - Wavetable oscillator
|
|
//! - [`Chord`](PlaitsEngine::Chord) - Polyphonic chord generator
|
|
//! - [`Swarm`](PlaitsEngine::Swarm) - Swarm of detuned oscillators
|
|
//! - [`Noise`](PlaitsEngine::Noise) - Filtered noise with resonance
|
|
//!
|
|
//! ## Percussion Engines
|
|
//! - [`Bass`](PlaitsEngine::Bass) - Analog kick drum model
|
|
//! - [`Snare`](PlaitsEngine::Snare) - Analog snare drum model
|
|
//! - [`Hat`](PlaitsEngine::Hat) - Hi-hat synthesis
|
|
//!
|
|
//! # Control Parameters
|
|
//!
|
|
//! All engines share a common control interface via [`EngineParameters`]:
|
|
//! - `note` - MIDI note number (pitch)
|
|
//! - `harmonics` - Timbre brightness/harmonics control
|
|
//! - `timbre` - Primary timbre parameter
|
|
//! - `morph` - Secondary timbre/morph parameter
|
|
//! - `accent` - Velocity/accent amount
|
|
//! - `trigger` - Gate/trigger state
|
|
|
|
use crate::types::{Source, BLOCK_SIZE};
|
|
use mi_plaits_dsp::engine::additive_engine::AdditiveEngine;
|
|
use mi_plaits_dsp::engine::bass_drum_engine::BassDrumEngine;
|
|
use mi_plaits_dsp::engine::chord_engine::ChordEngine;
|
|
use mi_plaits_dsp::engine::fm_engine::FmEngine;
|
|
use mi_plaits_dsp::engine::grain_engine::GrainEngine;
|
|
use mi_plaits_dsp::engine::hihat_engine::HihatEngine;
|
|
use mi_plaits_dsp::engine::modal_engine::ModalEngine;
|
|
use mi_plaits_dsp::engine::noise_engine::NoiseEngine;
|
|
use mi_plaits_dsp::engine::snare_drum_engine::SnareDrumEngine;
|
|
use mi_plaits_dsp::engine::swarm_engine::SwarmEngine;
|
|
use mi_plaits_dsp::engine::virtual_analog_engine::VirtualAnalogEngine;
|
|
use mi_plaits_dsp::engine::waveshaping_engine::WaveshapingEngine;
|
|
use mi_plaits_dsp::engine::wavetable_engine::WavetableEngine;
|
|
use mi_plaits_dsp::engine::{Engine, EngineParameters};
|
|
|
|
/// Wrapper enum containing all Plaits synthesis engines.
|
|
///
|
|
/// Only one engine is active at a time. The engine is lazily initialized
|
|
/// when first needed and can be switched by creating a new instance with
|
|
/// [`PlaitsEngine::new`].
|
|
pub enum PlaitsEngine {
|
|
/// Physical modeling of resonant structures (strings, plates, tubes).
|
|
Modal(ModalEngine),
|
|
/// Classic virtual analog with saw, pulse, and sub oscillator.
|
|
Va(VirtualAnalogEngine),
|
|
/// Waveshaping synthesis for harsh, aggressive timbres.
|
|
Ws(WaveshapingEngine),
|
|
/// Two-operator FM synthesis.
|
|
Fm(FmEngine),
|
|
/// Granular synthesis with pitch-shifting grains.
|
|
Grain(GrainEngine),
|
|
/// Additive synthesis with individual harmonic control.
|
|
Additive(AdditiveEngine),
|
|
/// Wavetable oscillator with smooth morphing.
|
|
Wavetable(WavetableEngine<'static>),
|
|
/// Polyphonic chord generator (boxed due to size).
|
|
Chord(Box<ChordEngine<'static>>),
|
|
/// Swarm of detuned sawtooth oscillators.
|
|
Swarm(SwarmEngine),
|
|
/// Filtered noise with variable resonance.
|
|
Noise(NoiseEngine),
|
|
/// Analog bass drum synthesis.
|
|
Bass(BassDrumEngine),
|
|
/// Analog snare drum synthesis.
|
|
Snare(SnareDrumEngine),
|
|
/// Metallic hi-hat synthesis.
|
|
Hat(HihatEngine),
|
|
}
|
|
|
|
impl PlaitsEngine {
|
|
/// Creates and initializes a new engine based on the given source type.
|
|
///
|
|
/// # Panics
|
|
/// Panics if `source` is not a Plaits source variant (e.g., `Source::Tri`).
|
|
pub fn new(source: Source, sample_rate: f32) -> Self {
|
|
match source {
|
|
Source::PlModal => {
|
|
let mut e = ModalEngine::new(BLOCK_SIZE);
|
|
e.init(sample_rate);
|
|
Self::Modal(e)
|
|
}
|
|
Source::PlVa => {
|
|
let mut e = VirtualAnalogEngine::new(BLOCK_SIZE);
|
|
e.init(sample_rate);
|
|
Self::Va(e)
|
|
}
|
|
Source::PlWs => {
|
|
let mut e = WaveshapingEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Ws(e)
|
|
}
|
|
Source::PlFm => {
|
|
let mut e = FmEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Fm(e)
|
|
}
|
|
Source::PlGrain => {
|
|
let mut e = GrainEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Grain(e)
|
|
}
|
|
Source::PlAdd => {
|
|
let mut e = AdditiveEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Additive(e)
|
|
}
|
|
Source::PlWt => {
|
|
let mut e = WavetableEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Wavetable(e)
|
|
}
|
|
Source::PlChord => {
|
|
let mut e = ChordEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Chord(Box::new(e))
|
|
}
|
|
Source::PlSwarm => {
|
|
let mut e = SwarmEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Swarm(e)
|
|
}
|
|
Source::PlNoise => {
|
|
let mut e = NoiseEngine::new(BLOCK_SIZE);
|
|
e.init(sample_rate);
|
|
Self::Noise(e)
|
|
}
|
|
Source::PlBass => {
|
|
let mut e = BassDrumEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Bass(e)
|
|
}
|
|
Source::PlSnare => {
|
|
let mut e = SnareDrumEngine::new();
|
|
e.init(sample_rate);
|
|
Self::Snare(e)
|
|
}
|
|
Source::PlHat => {
|
|
let mut e = HihatEngine::new(BLOCK_SIZE);
|
|
e.init(sample_rate);
|
|
Self::Hat(e)
|
|
}
|
|
_ => unreachable!(),
|
|
}
|
|
}
|
|
|
|
/// Renders a block of audio samples.
|
|
///
|
|
/// # Arguments
|
|
/// - `params` - Engine parameters (pitch, timbre, morph, etc.)
|
|
/// - `out` - Output buffer for main signal (length must be `BLOCK_SIZE`)
|
|
/// - `aux` - Output buffer for auxiliary signal (length must be `BLOCK_SIZE`)
|
|
/// - `already_enveloped` - Set to true by percussion engines that apply their own envelope
|
|
pub fn render(
|
|
&mut self,
|
|
params: &EngineParameters,
|
|
out: &mut [f32],
|
|
aux: &mut [f32],
|
|
already_enveloped: &mut bool,
|
|
) {
|
|
match self {
|
|
Self::Modal(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Va(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Ws(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Fm(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Grain(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Additive(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Wavetable(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Chord(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Swarm(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Noise(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Bass(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Snare(e) => e.render(params, out, aux, already_enveloped),
|
|
Self::Hat(e) => e.render(params, out, aux, already_enveloped),
|
|
}
|
|
}
|
|
|
|
/// Returns the [`Source`] variant corresponding to the current engine.
|
|
pub fn source(&self) -> Source {
|
|
match self {
|
|
Self::Modal(_) => Source::PlModal,
|
|
Self::Va(_) => Source::PlVa,
|
|
Self::Ws(_) => Source::PlWs,
|
|
Self::Fm(_) => Source::PlFm,
|
|
Self::Grain(_) => Source::PlGrain,
|
|
Self::Additive(_) => Source::PlAdd,
|
|
Self::Wavetable(_) => Source::PlWt,
|
|
Self::Chord(_) => Source::PlChord,
|
|
Self::Swarm(_) => Source::PlSwarm,
|
|
Self::Noise(_) => Source::PlNoise,
|
|
Self::Bass(_) => Source::PlBass,
|
|
Self::Snare(_) => Source::PlSnare,
|
|
Self::Hat(_) => Source::PlHat,
|
|
}
|
|
}
|
|
}
|