1use super::basic::{BasicOscillator, Waveform};
10use crate::algorithm::{Algorithm, AlgorithmCategory, AlgorithmMetadata};
11use crate::generators::{Generator, ModulatableGenerator};
12use crate::vector::prelude::*;
13use rill_core::traits::{ActionContext, ProcessResult};
14use rill_core::Transcendental;
15
16#[derive(Clone, Copy)]
52pub struct SimpleFmSynth<T: Transcendental> {
53 carrier: BasicOscillator<T>,
55 modulator: BasicOscillator<T>,
57 modulation_index: ScalarVector1<T>,
59 ratio: f32,
61}
62
63impl<T: Transcendental> SimpleFmSynth<T> {
64 pub fn new(carrier_freq: f32, modulator_ratio: f32, modulation_index: T) -> Self {
71 let one = T::from_f32(1.0);
72 Self {
73 carrier: BasicOscillator::new(Waveform::Sine, carrier_freq, one),
74 modulator: BasicOscillator::new(Waveform::Sine, carrier_freq * modulator_ratio, one),
75 modulation_index: ScalarVector1::splat(modulation_index),
76 ratio: modulator_ratio,
77 }
78 }
79
80 pub fn with_carrier_waveform(mut self, waveform: Waveform) -> Self {
84 let freq = self.carrier.frequency();
85 self.carrier = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
86 self
87 }
88
89 pub fn with_modulator_waveform(mut self, waveform: Waveform) -> Self {
93 let freq = self.modulator.frequency();
94 self.modulator = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
95 self
96 }
97
98 pub fn set_carrier_frequency(&mut self, freq: f32) {
100 self.carrier.set_frequency(freq);
101 self.modulator.set_frequency(freq * self.ratio);
102 }
103
104 pub fn set_modulation_index(&mut self, index: T) {
109 self.modulation_index = ScalarVector1::splat(index);
110 self.carrier.set_modulation_index(index);
111 }
112
113 pub fn set_ratio(&mut self, ratio: f32) {
118 self.ratio = ratio;
119 self.modulator
120 .set_frequency(self.carrier.frequency() * ratio);
121 }
122
123 pub fn modulation_index(&self) -> T {
125 self.modulation_index.extract(0)
126 }
127
128 pub fn ratio(&self) -> f32 {
130 self.ratio
131 }
132}
133
134impl<T: Transcendental> Algorithm<T> for SimpleFmSynth<T> {
135 fn init(&mut self, sample_rate: f32) {
136 self.carrier.init(sample_rate);
137 self.modulator.init(sample_rate);
138 }
139
140 fn reset(&mut self) {
141 self.carrier.reset();
142 self.modulator.reset();
143 }
144
145 fn process(
146 &mut self,
147 input: Option<&[T]>,
148 output: &mut [T],
149 _ctx: &ActionContext,
150 ) -> ProcessResult<()> {
151 let input = input.unwrap_or(&[]);
152 for out in output.iter_mut() {
153 let mod_signal = self.modulator.generate().extract(0);
155
156 self.carrier
158 .modulate_frequency(mod_signal * self.modulation_index.extract(0));
159
160 *out = self.carrier.generate().extract(0);
162 }
163 Ok(())
164 }
165
166 fn metadata(&self) -> AlgorithmMetadata {
167 AlgorithmMetadata {
168 name: "Simple FM Synth",
169 category: AlgorithmCategory::Generator,
170 description: "2-operator FM synthesizer",
171 author: "Rill",
172 version: env!("CARGO_PKG_VERSION"),
173 }
174 }
175}
176
177impl<T: Transcendental> Generator<T> for SimpleFmSynth<T> {
180 fn phase(&self) -> T {
181 self.carrier.phase()
182 }
183
184 fn set_phase(&mut self, phase: T) {
185 self.carrier.set_phase(phase);
186 self.modulator.set_phase(phase);
187 }
188
189 fn frequency(&self) -> f32 {
190 self.carrier.frequency()
191 }
192
193 fn set_frequency(&mut self, freq: f32) {
194 self.set_carrier_frequency(freq);
195 }
196
197 fn amplitude(&self) -> T {
198 self.carrier.amplitude()
199 }
200
201 fn set_amplitude(&mut self, amp: T) {
202 self.carrier.set_amplitude(amp);
203 self.modulator.set_amplitude(amp);
204 }
205}
206
207impl<T: Transcendental> ModulatableGenerator<T> for SimpleFmSynth<T> {
210 fn modulate_frequency(&mut self, amount: T) {
211 self.carrier.modulate_frequency(amount);
212 self.modulation_index = ScalarVector1::splat(amount);
214 }
215
216 fn modulation_index(&self) -> T {
217 SimpleFmSynth::modulation_index(self)
218 }
219
220 fn set_modulation_index(&mut self, index: T) {
221 SimpleFmSynth::set_modulation_index(self, index);
222 }
223}
224
225pub struct FmSynth<T: Transcendental, const N: usize> {
257 operators: [BasicOscillator<T>; N],
259 algorithm: [[bool; N]; N],
262 modulation_indices: [ScalarVector1<T>; N],
264}
265
266impl<T: Transcendental, const N: usize> FmSynth<T, N> {
267 pub fn new(frequencies: [f32; N], algorithm: [[bool; N]; N]) -> Self {
273 let one = T::from_f32(1.0);
274 let mut operators = [BasicOscillator::new(Waveform::Sine, 440.0, one); N];
275 for i in 0..N {
276 operators[i].set_frequency(frequencies[i]);
277 }
278
279 Self {
280 operators,
281 algorithm,
282 modulation_indices: [ScalarVector1::splat(T::ZERO); N],
283 }
284 }
285
286 pub fn new_with_freq(frequency: f32, algorithm: [[bool; N]; N]) -> Self {
288 let one = T::from_f32(1.0);
289 let operators = [BasicOscillator::new(Waveform::Sine, frequency, one); N];
290
291 Self {
292 operators,
293 algorithm,
294 modulation_indices: [ScalarVector1::splat(T::ZERO); N],
295 }
296 }
297
298 pub fn set_waveform(&mut self, index: usize, waveform: Waveform) {
300 if index < N {
301 let freq = self.operators[index].frequency();
302 self.operators[index] = BasicOscillator::new(waveform, freq, T::from_f32(1.0));
303 }
304 }
305
306 pub fn set_frequency(&mut self, index: usize, freq: f32) {
308 if index < N {
309 self.operators[index].set_frequency(freq);
310 }
311 }
312
313 pub fn set_modulation_index(&mut self, index: usize, idx: T) {
315 if index < N {
316 self.modulation_indices[index] = ScalarVector1::splat(idx);
317 }
318 }
319
320 pub fn peek_operator(&self, index: usize) -> T {
322 if index < N {
323 self.operators[index].phase()
324 } else {
325 T::ZERO
326 }
327 }
328
329 pub fn reset_all(&mut self) {
331 for op in &mut self.operators {
332 op.reset();
333 }
334 }
335}
336
337impl<T: Transcendental, const N: usize> Algorithm<T> for FmSynth<T, N> {
338 fn init(&mut self, sample_rate: f32) {
339 for op in &mut self.operators {
340 op.init(sample_rate);
341 }
342 }
343
344 fn reset(&mut self) {
345 self.reset_all();
346 }
347
348 fn process(
349 &mut self,
350 input: Option<&[T]>,
351 output: &mut [T],
352 _ctx: &ActionContext,
353 ) -> ProcessResult<()> {
354 let input = input.unwrap_or(&[]);
355 for out in output.iter_mut() {
356 let mut values = [T::ZERO; N];
358 for i in 0..N {
359 values[i] = self.operators[i].generate().extract(0);
360 }
361
362 for i in 0..N {
364 let mut mod_sum = T::ZERO;
365
366 for j in 0..N {
368 if self.algorithm[i][j] {
369 mod_sum = mod_sum + values[j] * self.modulation_indices[j].extract(0);
370 }
371 }
372
373 if mod_sum != T::ZERO {
375 self.operators[i].modulate_frequency(mod_sum);
376 }
377 }
378
379 *out = values[N - 1];
382 }
383 Ok(())
384 }
385
386 fn metadata(&self) -> AlgorithmMetadata {
387 match N {
389 2 => AlgorithmMetadata {
390 name: "2-operator FM Synth",
391 category: AlgorithmCategory::Generator,
392 description: "2-operator FM synthesizer",
393 author: "Rill",
394 version: env!("CARGO_PKG_VERSION"),
395 },
396 3 => AlgorithmMetadata {
397 name: "3-operator FM Synth",
398 category: AlgorithmCategory::Generator,
399 description: "3-operator FM synthesizer",
400 author: "Rill",
401 version: env!("CARGO_PKG_VERSION"),
402 },
403 4 => AlgorithmMetadata {
404 name: "4-operator FM Synth",
405 category: AlgorithmCategory::Generator,
406 description: "4-operator FM synthesizer (DX7 style)",
407 author: "Rill",
408 version: env!("CARGO_PKG_VERSION"),
409 },
410 5 => AlgorithmMetadata {
411 name: "5-operator FM Synth",
412 category: AlgorithmCategory::Generator,
413 description: "5-operator FM synthesizer",
414 author: "Rill",
415 version: env!("CARGO_PKG_VERSION"),
416 },
417 6 => AlgorithmMetadata {
418 name: "6-operator FM Synth",
419 category: AlgorithmCategory::Generator,
420 description: "6-operator FM synthesizer (DX7 style)",
421 author: "Rill",
422 version: env!("CARGO_PKG_VERSION"),
423 },
424 _ => AlgorithmMetadata {
425 name: "FM Synth",
426 category: AlgorithmCategory::Generator,
427 description: "Multi-operator FM synthesizer",
428 author: "Rill",
429 version: env!("CARGO_PKG_VERSION"),
430 },
431 }
432 }
433}
434
435pub mod algorithms_4op {
441 pub const ALGORITHM_1: [[bool; 4]; 4] = [
443 [false, true, false, false],
444 [false, false, true, false],
445 [false, false, false, true],
446 [false, false, false, false],
447 ];
448
449 pub const ALGORITHM_2: [[bool; 4]; 4] = [
451 [false, true, false, false],
452 [false, false, false, false],
453 [false, false, false, true],
454 [false, false, false, false],
455 ];
456
457 pub const ALGORITHM_3: [[bool; 4]; 4] = [
459 [false, false, false, false],
460 [false, false, false, false],
461 [true, true, false, false],
462 [false, false, false, false],
463 ];
464}
465
466pub mod algorithms_6op {
468 pub const ALGORITHM_1: [[bool; 6]; 6] = [
470 [false, true, false, false, false, false],
471 [false, false, true, false, false, false],
472 [false, false, false, true, false, false],
473 [false, false, false, false, true, false],
474 [false, false, false, false, false, true],
475 [false, false, false, false, false, false],
476 ];
477
478 pub const ALGORITHM_2: [[bool; 6]; 6] = [
480 [false, true, false, false, false, false],
481 [false, false, true, false, false, false],
482 [false, false, false, false, false, false],
483 [false, false, false, false, true, false],
484 [false, false, false, false, false, true],
485 [false, false, false, false, false, false],
486 ];
487
488 pub const ALGORITHM_3: [[bool; 6]; 6] = [
490 [false, true, false, false, false, false],
491 [true, false, true, false, false, false],
492 [false, false, false, true, false, false],
493 [false, false, false, false, true, false],
494 [false, false, false, false, false, true],
495 [false, false, false, false, false, false],
496 ];
497}
498
499#[cfg(test)]
504mod tests {
505 use super::*;
506 use rill_core::time::ClockTick;
507 use rill_core::traits::ActionContext;
508
509 #[test]
510 fn test_simple_fm_synth() {
511 let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
512 fm.init(44100.0);
513
514 let mut output = [0.0f32; 1];
515 let tick = ClockTick::default();
516 let ctx = ActionContext::new(&tick);
517 fm.process(None, &mut output, &ctx).unwrap();
518 let sample = output[0];
519 assert!(sample >= -1.0 && sample <= 1.0);
520 }
521
522 #[test]
523 fn test_simple_fm_with_different_waveforms() {
524 let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5)
525 .with_carrier_waveform(Waveform::Saw)
526 .with_modulator_waveform(Waveform::Square);
527 fm.init(44100.0);
528
529 let mut output = [0.0f32; 1];
530 let tick = ClockTick::default();
531 let ctx = ActionContext::new(&tick);
532 fm.process(None, &mut output, &ctx).unwrap();
533 let sample = output[0];
534 assert!(sample >= -1.0 && sample <= 1.0);
535 }
536
537 #[test]
538 fn test_simple_fm_parameters() {
539 let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
540 fm.init(44100.0);
541
542 assert_eq!(fm.frequency(), 440.0);
543 assert_eq!(fm.ratio(), 2.0);
544 assert_eq!(fm.modulation_index(), 1.5);
545
546 fm.set_carrier_frequency(880.0);
547 assert_eq!(fm.frequency(), 880.0);
548
549 fm.set_ratio(3.0);
550 assert_eq!(fm.ratio(), 3.0);
551
552 fm.set_modulation_index(2.0);
553 assert_eq!(fm.modulation_index(), 2.0);
554 }
555
556 #[test]
557 fn test_fm_synth_4op() {
558 let frequencies = [440.0, 880.0, 1320.0, 1760.0];
559 let mut fm = FmSynth::<f32, 4>::new(frequencies, algorithms_4op::ALGORITHM_1);
560 fm.init(44100.0);
561
562 let mut output = [0.0f32; 1];
563 let tick = ClockTick::default();
564 let ctx = ActionContext::new(&tick);
565 fm.process(None, &mut output, &ctx).unwrap();
566 let sample = output[0];
567 assert!(sample >= -1.0 && sample <= 1.0);
568 }
569
570 #[test]
571 fn test_fm_synth_6op() {
572 let frequencies = [440.0, 880.0, 1320.0, 1760.0, 2200.0, 2640.0];
573 let mut fm = FmSynth::<f32, 6>::new(frequencies, algorithms_6op::ALGORITHM_1);
574 fm.init(44100.0);
575
576 let mut output = [0.0f32; 1];
577 let tick = ClockTick::default();
578 let ctx = ActionContext::new(&tick);
579 fm.process(None, &mut output, &ctx).unwrap();
580 let sample = output[0];
581 assert!(sample >= -1.0 && sample <= 1.0);
582 }
583
584 #[test]
585 fn test_fm_synth_set_waveform() {
586 let frequencies = [440.0, 880.0];
587 let algorithm = [[false, true], [false, false]];
588
589 let mut fm = FmSynth::<f32, 2>::new(frequencies, algorithm);
590 fm.init(44100.0);
591
592 fm.set_waveform(0, Waveform::Saw);
593 fm.set_waveform(1, Waveform::Square);
594
595 let mut output = [0.0f32; 1];
596 let tick = ClockTick::default();
597 let ctx = ActionContext::new(&tick);
598 fm.process(None, &mut output, &ctx).unwrap();
599 let sample = output[0];
600 assert!(sample >= -1.0 && sample <= 1.0);
601 }
602
603 #[test]
604 fn test_generator_trait() {
605 use crate::generators::Generator;
606
607 let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
608 fm.init(44100.0);
609
610 assert_eq!(fm.frequency(), 440.0);
611 fm.set_frequency(880.0);
612 assert_eq!(fm.frequency(), 880.0);
613
614 fm.set_amplitude(0.5);
615 assert_eq!(fm.amplitude(), 0.5);
616
617 let phase = fm.phase();
618 assert!(phase >= 0.0 && phase <= 1.0);
619 }
620
621 #[test]
622 fn test_modulatable_trait() {
623 use crate::generators::ModulatableGenerator;
624
625 let mut fm = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
626 fm.init(44100.0);
627
628 assert_eq!(fm.modulation_index(), 1.5);
630
631 fm.modulate_frequency(0.3);
633 assert_eq!(
634 fm.modulation_index(),
635 0.3,
636 "modulation_index should be updated to 0.3"
637 );
638
639 fm.set_modulation_index(0.8);
641 assert_eq!(
642 fm.modulation_index(),
643 0.8,
644 "modulation_index should be updated to 0.8"
645 );
646 }
647
648 #[test]
649 fn test_clone_copy() {
650 let fm1 = SimpleFmSynth::<f32>::new(440.0, 2.0, 1.5);
651 let fm2 = fm1; let fm3 = fm1.clone(); assert_eq!(fm1.frequency(), fm2.frequency());
655 assert_eq!(fm1.frequency(), fm3.frequency());
656 assert_eq!(fm1.ratio(), fm2.ratio());
657 }
658}