use super::basic::{BasicOscillator, Waveform};
use crate::generators::{Generator, ModulatableGenerator};
use crate::vector::prelude::*;
use rill_core::traits::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
use rill_core::traits::ProcessResult;
use rill_core::Transcendental;
#[derive(Clone, Copy)]
pub struct SimpleFmSynth<T: Transcendental> {
carrier: BasicOscillator<T>,
modulator: BasicOscillator<T>,
modulation_index: ScalarVector1<T>,
ratio: f32,
}
impl<T: Transcendental> SimpleFmSynth<T> {
pub fn new(carrier_freq: f32, modulator_ratio: f32, modulation_index: T) -> Self {
let one = T::from_f32(1.0);
Self {
carrier: BasicOscillator::new(Waveform::Sine, carrier_freq, one),
modulator: BasicOscillator::new(Waveform::Sine, carrier_freq * modulator_ratio, one),
modulation_index: ScalarVector1::splat(modulation_index),
ratio: modulator_ratio,
}
}
pub fn with_carrier_waveform(mut self, waveform: Waveform) -> Self {
let freq = self.carrier.frequency();
self.carrier = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
self
}
pub fn with_modulator_waveform(mut self, waveform: Waveform) -> Self {
let freq = self.modulator.frequency();
self.modulator = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
self
}
pub fn set_carrier_frequency(&mut self, freq: f32) {
self.carrier.set_frequency(freq);
self.modulator.set_frequency(freq * self.ratio);
}
pub fn set_modulation_index(&mut self, index: T) {
self.modulation_index = ScalarVector1::splat(index);
self.carrier.set_modulation_index(index);
}
pub fn set_ratio(&mut self, ratio: f32) {
self.ratio = ratio;
self.modulator
.set_frequency(self.carrier.frequency() * ratio);
}
pub fn modulation_index(&self) -> T {
self.modulation_index.extract(0)
}
pub fn ratio(&self) -> f32 {
self.ratio
}
}
impl<T: Transcendental> Algorithm<T> for SimpleFmSynth<T> {
fn init(&mut self, sample_rate: f32) {
self.carrier.init(sample_rate);
self.modulator.init(sample_rate);
}
fn reset(&mut self) {
self.carrier.reset();
self.modulator.reset();
}
fn process(&mut self, _input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
for out in output.iter_mut() {
let mod_signal = self.modulator.generate().extract(0);
self.carrier
.modulate_frequency(mod_signal * self.modulation_index.extract(0));
*out = self.carrier.generate().extract(0);
}
Ok(())
}
fn metadata(&self) -> AlgorithmMetadata {
AlgorithmMetadata {
name: "Simple FM Synth",
category: AlgorithmCategory::Generator,
description: "2-operator FM synthesizer",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
}
}
}
impl<T: Transcendental> Generator<T> for SimpleFmSynth<T> {
fn phase(&self) -> T {
self.carrier.phase()
}
fn set_phase(&mut self, phase: T) {
self.carrier.set_phase(phase);
self.modulator.set_phase(phase);
}
fn frequency(&self) -> f32 {
self.carrier.frequency()
}
fn set_frequency(&mut self, freq: f32) {
self.set_carrier_frequency(freq);
}
fn amplitude(&self) -> T {
self.carrier.amplitude()
}
fn set_amplitude(&mut self, amp: T) {
self.carrier.set_amplitude(amp);
self.modulator.set_amplitude(amp);
}
}
impl<T: Transcendental> ModulatableGenerator<T> for SimpleFmSynth<T> {
fn modulate_frequency(&mut self, amount: T) {
self.carrier.modulate_frequency(amount);
self.modulation_index = ScalarVector1::splat(amount);
}
fn modulation_index(&self) -> T {
SimpleFmSynth::modulation_index(self)
}
fn set_modulation_index(&mut self, index: T) {
SimpleFmSynth::set_modulation_index(self, index);
}
}
pub struct FmSynth<T: Transcendental, const N: usize> {
operators: [BasicOscillator<T>; N],
algorithm: [[bool; N]; N],
modulation_indices: [ScalarVector1<T>; N],
}
impl<T: Transcendental, const N: usize> FmSynth<T, N> {
pub fn new(frequencies: [f32; N], algorithm: [[bool; N]; N]) -> Self {
let one = T::from_f32(1.0);
let mut operators = [BasicOscillator::new(Waveform::Sine, 440.0, one); N];
for i in 0..N {
operators[i].set_frequency(frequencies[i]);
}
Self {
operators,
algorithm,
modulation_indices: [ScalarVector1::splat(T::ZERO); N],
}
}
pub fn new_with_freq(frequency: f32, algorithm: [[bool; N]; N]) -> Self {
let one = T::from_f32(1.0);
let operators = [BasicOscillator::new(Waveform::Sine, frequency, one); N];
Self {
operators,
algorithm,
modulation_indices: [ScalarVector1::splat(T::ZERO); N],
}
}
pub fn set_waveform(&mut self, index: usize, waveform: Waveform) {
if index < N {
let freq = self.operators[index].frequency();
self.operators[index] = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
}
}
pub fn set_frequency(&mut self, index: usize, freq: f32) {
if index < N {
self.operators[index].set_frequency(freq);
}
}
pub fn set_modulation_index(&mut self, index: usize, idx: T) {
if index < N {
self.modulation_indices[index] = ScalarVector1::splat(idx);
}
}
pub fn peek_operator(&self, index: usize) -> T {
if index < N {
self.operators[index].phase()
} else {
T::ZERO
}
}
pub fn reset_all(&mut self) {
for op in &mut self.operators {
op.reset();
}
}
}
impl<T: Transcendental, const N: usize> Algorithm<T> for FmSynth<T, N> {
fn init(&mut self, sample_rate: f32) {
for op in &mut self.operators {
op.init(sample_rate);
}
}
fn reset(&mut self) {
self.reset_all();
}
fn process(&mut self, _input: Option<&[T]>, output: &mut [T]) -> ProcessResult<()> {
for out in output.iter_mut() {
let values: [_; N] = core::array::from_fn(|i| self.operators[i].generate().extract(0));
for (i, op) in self.operators.iter_mut().enumerate() {
let mut mod_sum = T::ZERO;
for (j, &is_mod) in self.algorithm[i].iter().enumerate() {
if is_mod {
mod_sum += values[j] * self.modulation_indices[j].extract(0);
}
}
if mod_sum != T::ZERO {
op.modulate_frequency(mod_sum);
}
}
*out = values[N - 1];
}
Ok(())
}
fn metadata(&self) -> AlgorithmMetadata {
match N {
2 => AlgorithmMetadata {
name: "2-operator FM Synth",
category: AlgorithmCategory::Generator,
description: "2-operator FM synthesizer",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
3 => AlgorithmMetadata {
name: "3-operator FM Synth",
category: AlgorithmCategory::Generator,
description: "3-operator FM synthesizer",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
4 => AlgorithmMetadata {
name: "4-operator FM Synth",
category: AlgorithmCategory::Generator,
description: "4-operator FM synthesizer (DX7 style)",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
5 => AlgorithmMetadata {
name: "5-operator FM Synth",
category: AlgorithmCategory::Generator,
description: "5-operator FM synthesizer",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
6 => AlgorithmMetadata {
name: "6-operator FM Synth",
category: AlgorithmCategory::Generator,
description: "6-operator FM synthesizer (DX7 style)",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
_ => AlgorithmMetadata {
name: "FM Synth",
category: AlgorithmCategory::Generator,
description: "Multi-operator FM synthesizer",
author: "Rill",
version: env!("CARGO_PKG_VERSION"),
},
}
}
}
pub mod algorithms_4op {
pub const ALGORITHM_1: [[bool; 4]; 4] = [
[false, true, false, false],
[false, false, true, false],
[false, false, false, true],
[false, false, false, false],
];
pub const ALGORITHM_2: [[bool; 4]; 4] = [
[false, true, false, false],
[false, false, false, false],
[false, false, false, true],
[false, false, false, false],
];
pub const ALGORITHM_3: [[bool; 4]; 4] = [
[false, false, false, false],
[false, false, false, false],
[true, true, false, false],
[false, false, false, false],
];
}
pub mod algorithms_6op {
pub const ALGORITHM_1: [[bool; 6]; 6] = [
[false, true, false, false, false, false],
[false, false, true, false, false, false],
[false, false, false, true, false, false],
[false, false, false, false, true, false],
[false, false, false, false, false, true],
[false, false, false, false, false, false],
];
pub const ALGORITHM_2: [[bool; 6]; 6] = [
[false, true, false, false, false, false],
[false, false, true, false, false, false],
[false, false, false, false, false, false],
[false, false, false, false, true, false],
[false, false, false, false, false, true],
[false, false, false, false, false, false],
];
pub const ALGORITHM_3: [[bool; 6]; 6] = [
[false, true, false, false, false, false],
[true, false, true, false, false, false],
[false, false, false, true, false, false],
[false, false, false, false, true, false],
[false, false, false, false, false, true],
[false, false, false, false, false, false],
];
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_fm_synth() {
let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
fm.init(44100.0);
let mut output = [0.0f32; 1];
fm.process(None, &mut output).unwrap();
let sample = output[0];
assert!((-1.0..=1.0).contains(&sample));
}
#[test]
fn test_simple_fm_with_different_waveforms() {
let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5)
.with_carrier_waveform(Waveform::Saw)
.with_modulator_waveform(Waveform::Square);
fm.init(44100.0);
let mut output = [0.0f32; 1];
fm.process(None, &mut output).unwrap();
let sample = output[0];
assert!((-1.0..=1.0).contains(&sample));
}
#[test]
fn test_simple_fm_parameters() {
let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
fm.init(44100.0);
assert_eq!(fm.frequency(), 440.0);
assert_eq!(fm.ratio(), 2.0);
assert_eq!(fm.modulation_index(), 1.5);
fm.set_carrier_frequency(880.0);
assert_eq!(fm.frequency(), 880.0);
fm.set_ratio(3.0);
assert_eq!(fm.ratio(), 3.0);
fm.set_modulation_index(2.0);
assert_eq!(fm.modulation_index(), 2.0);
}
#[test]
fn test_fm_synth_4op() {
let frequencies = [440.0, 880.0, 1320.0, 1760.0];
let mut fm = FmSynth::<f32, 4>::new(frequencies, algorithms_4op::ALGORITHM_1);
fm.init(44100.0);
let mut output = [0.0f32; 1];
fm.process(None, &mut output).unwrap();
let sample = output[0];
assert!((-1.0..=1.0).contains(&sample));
}
#[test]
fn test_fm_synth_6op() {
let frequencies = [440.0, 880.0, 1320.0, 1760.0, 2200.0, 2640.0];
let mut fm = FmSynth::<f32, 6>::new(frequencies, algorithms_6op::ALGORITHM_1);
fm.init(44100.0);
let mut output = [0.0f32; 1];
fm.process(None, &mut output).unwrap();
let sample = output[0];
assert!((-1.0..=1.0).contains(&sample));
}
#[test]
fn test_fm_synth_set_waveform() {
let frequencies = [440.0, 880.0];
let algorithm = [[false, true], [false, false]];
let mut fm = FmSynth::<f32, 2>::new(frequencies, algorithm);
fm.init(44100.0);
fm.set_waveform(0, Waveform::Saw);
fm.set_waveform(1, Waveform::Square);
let mut output = [0.0f32; 1];
fm.process(None, &mut output).unwrap();
let sample = output[0];
assert!((-1.0..=1.0).contains(&sample));
}
#[test]
fn test_generator_trait() {
use crate::generators::Generator;
let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
fm.init(44100.0);
assert_eq!(fm.frequency(), 440.0);
fm.set_frequency(880.0);
assert_eq!(fm.frequency(), 880.0);
fm.set_amplitude(0.5);
assert_eq!(fm.amplitude(), 0.5);
let phase = fm.phase();
assert!((0.0..=1.0).contains(&phase));
}
#[test]
fn test_modulatable_trait() {
use crate::generators::ModulatableGenerator;
let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
fm.init(44100.0);
assert_eq!(fm.modulation_index(), 1.5);
fm.modulate_frequency(0.3);
assert_eq!(
fm.modulation_index(),
0.3,
"modulation_index should be updated to 0.3"
);
fm.set_modulation_index(0.8);
assert_eq!(
fm.modulation_index(),
0.8,
"modulation_index should be updated to 0.8"
);
}
#[test]
fn test_clone_copy() {
let fm1 = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
let fm2 = fm1; let fm3 = Clone::clone(&fm1);
assert_eq!(fm1.frequency(), fm2.frequency());
assert_eq!(fm1.frequency(), fm3.frequency());
assert_eq!(fm1.ratio(), fm2.ratio());
}
}