From d3776887db90ac1ea15fd55d833ff6b3042e7caf Mon Sep 17 00:00:00 2001
From: Grant Shangreaux <grant@unabridgedsoftware.com>
Date: Fri, 12 Nov 2021 23:40:33 -0600
Subject: Add: Osc Iterator refactor

Now the MonoSynth has one main `osc` property, and it holds an Osc
struct which contains its own instance of a Phasor. The Phasor has
been mostly hidden away from the top level, though we still have our
Bender for bending phase. I think we can implement methods on Osc in
order to manipulate the Phasor of a particular instance of osc if need
be.

The outer `frames` function has been partially refactored. However, I
think we should/could wrap the Osc's `next` function in something like
`next_sample` which returns a Sample (vs an Option). we could also do
something like `osc.take(frames_count).map { |s| frames.push(s) }`
---
 klangfarbrs/src/lib.rs    | 92 ++++++++++++++++-------------------------------
 klangfarbrs/src/osc.rs    | 51 ++++++++++++++++++++++----
 klangfarbrs/src/phasor.rs | 28 ++++++++-------
 3 files changed, 90 insertions(+), 81 deletions(-)

(limited to 'klangfarbrs/src')

diff --git a/klangfarbrs/src/lib.rs b/klangfarbrs/src/lib.rs
index a118d38..6bc2e14 100644
--- a/klangfarbrs/src/lib.rs
+++ b/klangfarbrs/src/lib.rs
@@ -11,13 +11,12 @@
 
 use gdnative::prelude::*;
 use gdnative::core_types::TypedArray;
-use rand::Rng;
-use std::f32::consts::TAU;
 
 mod phasor;
+use phasor::{Phase};
 
 mod osc;
-use osc::Osc;
+use osc::{Osc, Waveform};
 
 pub mod adsr;
 use adsr::Envelope;
@@ -26,25 +25,13 @@ use adsr::Envelope;
 type Sample = f32;
 type SamplesPerSecond = f32;
 type Hz = f32;
-type Phase = f32;
 type Amplitude = f32;
 type Millisecond = u32;
 
-/// The various waveforms the `MonoSynth` can generate.
-pub enum Waveform {
-    Sine,
-    Square,
-    Triangle,
-    Sawtooth,
-    WhiteNoise,
-    BrownNoise,
-}
-
 #[derive(NativeClass)]
 #[inherit(Node)]
 pub struct MonoSynth {
-    pub phasor: Phasor,
-    pub waveform: Waveform,
+    pub osc: Osc,
     pub sample_rate: SamplesPerSecond,
     pub frequency: Hz,
     pub apply_bend: bool,
@@ -60,24 +47,10 @@ pub struct MonoSynth {
     pub frequency_modulation: bool,
     pub fm_frequency: Hz,
     pub fm_depth: Amplitude,
-    fm_phasor: Phasor,
+    fm_osc: Osc,
     current_envelope_position: usize,
 }
 
-pub struct Phasor {
-    pub phase: Phase,
-}
-
-impl Phasor {
-    /// Phase stays between 0.0 and 1.0 and represents position on the axis of time
-    /// for a given wave form. Since audio signals are periodic, we can just calculate
-    /// the first cycle of a wave repeatedly. This also prevents pitch drift caused by
-    /// floating point errors over time.
-    pub fn next_phase(&self, frequency: Hz, sample_rate: SamplesPerSecond ) -> Phase {
-        (self.phase + (frequency / sample_rate)) % 1.0
-    }
-}
-
 pub struct Bender {}
 
 impl Bender {
@@ -107,22 +80,23 @@ impl MonoSynth {
     /// wave.square() # changes to a square wave
     /// ```
     pub fn new(_owner: &Node) -> Self {
+        let freq = 440.0;
+        let sprt = 48000.0;
+
         Self {
-            phasor: Phasor { phase: 0.0 },
-            waveform: Waveform::Sine,
-            sample_rate: 48000.0,
-            frequency: 440.0,
+            osc: Osc::new(freq, sprt),
+            sample_rate: sprt,
+            frequency: freq,
             apply_bend: false,
             phasor_bend: Vector2::new(0.0, 0.0),
             continuous: true,
             duration: 0,
-            envelope: Envelope::new(500, 1000, 0.5, 4000, 48000.0),
+            envelope: Envelope::new(500, 1000, 0.5, 4000, sprt),
             cutoff: 0.0,
             frequency_modulation: false,
             fm_frequency: 10.0,
             fm_depth: 0.1,
-    // Noise,
-            fm_phasor: Phasor { phase: 0.0 },
+            fm_osc: Osc::new(10.0, sprt),
             current_envelope_position: 0,
         }
     }
@@ -134,37 +108,37 @@ impl MonoSynth {
 
     #[export]
     fn sine(&mut self, _owner: &Node) {
-        self.waveform = Waveform::Sine
+        self.osc.waveform = Waveform::Sine
     }
 
     #[export]
     fn square(&mut self, _owner: &Node) {
-        self.waveform = Waveform::Square
+        self.osc.waveform = Waveform::Square
     }
 
     #[export]
     fn triangle(&mut self, _owner: &Node) {
-        self.waveform = Waveform::Triangle
+        self.osc.waveform = Waveform::Triangle
     }
 
     #[export]
     fn sawtooth(&mut self, _owner: &Node) {
-        self.waveform = Waveform::Sawtooth
+        self.osc.waveform = Waveform::Sawtooth
     }
 
     #[export]
     fn white_noise(&mut self, _owner: &Node) {
-        self.waveform = Waveform::WhiteNoise
+        self.osc.waveform = Waveform::WhiteNoise
     }
 
     #[export]
     fn brown_noise(&mut self, _owner: &Node) {
-        self.waveform = Waveform::BrownNoise
+        self.osc.waveform = Waveform::BrownNoise
     }
 
     #[export]
     fn frequency(&mut self, _owner: &Node, frequency: Hz) {
-        self.frequency = frequency
+        self.osc.set_frequency(frequency)
     }
 
     #[export]
@@ -194,7 +168,7 @@ impl MonoSynth {
 
     #[export]
     fn fm_frequency(&mut self, _owner: &Node, fm_frequency: f32) {
-        self.fm_frequency = fm_frequency
+        self.fm_osc.set_frequency(fm_frequency)
     }
 
     #[export]
@@ -213,27 +187,23 @@ impl MonoSynth {
     #[export]
     pub fn frames(&mut self, _owner: &Node, samples: i32) -> TypedArray<Vector2> {
         let mut frames = TypedArray::new();
-        let mut rng = rand::thread_rng();
-        let mut last_value = (rng.gen::<f32>() * TAU).sin();
 
         for _i in 0..samples {
-            let mut sample = Osc::generate_sample(&self.waveform, self.phasor.phase, last_value);
-            last_value = sample;
-            let next_phase : f32;
+            // let next_phase : f32;
 
             if self.frequency_modulation {
-                let modulation_value = Osc::generate_sample(&Waveform::Sine, self.fm_phasor.phase, last_value) * self.fm_depth;
-                self.fm_phasor.phase = self.fm_phasor.next_phase(self.fm_frequency, self.sample_rate);
-                next_phase = self.phasor.next_phase(self.frequency + modulation_value, self.sample_rate);
-            } else {
-                next_phase = self.phasor.next_phase(self.frequency, self.sample_rate);
+                let modulation_value =  self.fm_osc.next().unwrap() * self.fm_depth;
+                self.osc.set_frequency(self.osc.get_frequency() + modulation_value);
             }
 
-            if self.apply_bend {
-                self.phasor.phase = Bender::bend(next_phase, self.phasor_bend);
-            } else {
-                self.phasor.phase = next_phase;
-            }
+            let mut sample = self.osc.next().unwrap();
+            self.osc.last_value = sample;
+
+            // if self.apply_bend {
+            //     self.phasor.phase = Bender::bend(next_phase, self.phasor_bend);
+            // } else {
+            //     self.phasor.phase = next_phase;
+            // }
 
             if !self.continuous {
                 let pos = self.current_envelope_position;
diff --git a/klangfarbrs/src/osc.rs b/klangfarbrs/src/osc.rs
index fd78de1..8225eb1 100644
--- a/klangfarbrs/src/osc.rs
+++ b/klangfarbrs/src/osc.rs
@@ -1,15 +1,50 @@
 use std::f32::consts::TAU;
 use rand::Rng;
-use crate::{Waveform, Phase, Sample};
+use super::{Hz, Sample};
+use super::phasor::{Phasor};
 
-pub struct Osc {}
+/// The various waveforms the `MonoSynth` can generate.
+pub enum Waveform {
+    Sine,
+    Square,
+    Triangle,
+    Sawtooth,
+    WhiteNoise,
+    BrownNoise,
+}
+
+pub struct Osc {
+    pub phasor: Phasor,
+    pub waveform: Waveform,
+    pub last_value: Sample,
+}
 
 impl Osc {
-    pub fn generate_sample(waveform: &Waveform, phase: Phase, last_value: Sample) -> Sample {
-        let phase = phase;
+    pub fn new(frequency: Hz, sample_rate: f32) -> Self {
+        Self {
+            phasor: Phasor::new(frequency, sample_rate),
+            waveform: Waveform::Sine,
+            last_value: 0.1,
+        }
+    }
+
+    pub fn get_frequency(&self) -> Hz {
+        self.phasor.frequency
+    }
+
+    pub fn set_frequency(&mut self, frequency: Hz) {
+        self.phasor.frequency = frequency;
+    }
+}
+
+impl Iterator for Osc {
+    type Item = Sample;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let phase = self.phasor.next().unwrap();
         let mut rng = rand::thread_rng();
 
-        match waveform {
+        let sample = match self.waveform {
             Waveform::Sine => {
                 (TAU * phase).sin()
             },
@@ -39,9 +74,11 @@ impl Osc {
             },
 
             Waveform::BrownNoise => {
-                (last_value + (rng.gen::<f32>()) * 0.2 - 0.1).clamp(-1.0, 1.0)
+                (self.last_value + (rng.gen::<f32>()) * 0.2 - 0.1).clamp(-1.0, 1.0)
             },
-        }
+        };
+
+        Some(sample)
     }
 }
 
diff --git a/klangfarbrs/src/phasor.rs b/klangfarbrs/src/phasor.rs
index 00372c0..ace374a 100644
--- a/klangfarbrs/src/phasor.rs
+++ b/klangfarbrs/src/phasor.rs
@@ -1,26 +1,28 @@
 use super::{SamplesPerSecond, Hz};
 
-type Phase = f32; // Phase will always be between 0.0 and 1.0.
+pub type Phase = f32; // Phase will always be between 0.0 and 1.0.
 
-pub struct PhasorIter {
+/// Phase stays between 0.0 and 1.0 and represents position on the axis of time
+/// for a given wave form. Since audio signals are periodic, we can just calculate
+/// the first cycle of a wave repeatedly. This also prevents pitch drift caused by
+/// floating point errors over time.
+pub struct Phasor {
     pub phase: Phase,
     pub frequency: Hz,
-    sample_rate: SamplesPerSecond,
+    pub sample_rate: SamplesPerSecond,
 }
 
-impl PhasorIter {
-    fn new(frequency: Hz, sample_rate: SamplesPerSecond) -> Self {
+impl Phasor {
+    pub fn new(frequency: Hz, sample_rate: SamplesPerSecond) -> Self {
         Self { phase: 0.0, frequency, sample_rate }
     }
 }
 
-impl Iterator for PhasorIter {
+impl Iterator for Phasor {
     type Item = Phase;
 
     fn next(&mut self) -> Option<Self::Item> {
-        self.phase += (self.frequency / self.sample_rate);
-
-        if self.phase > 1.0 { self.phase -= 1.0; }
+        self.phase = (self.phase + self.frequency / self.sample_rate) % 1.0;
 
         Some(self.phase)
     }
@@ -32,15 +34,15 @@ mod tests {
 
     #[test]
     fn it_produces_expected_next_value() {
-        let mut phasor = PhasorIter::new(10.0, 100.0);
+        let mut phasor = Phasor::new(10.0, 100.0);
         assert_eq!(phasor.next(), Some(0.1))
     }
 
     #[test]
     fn it_produces_expected_next_values() {
-        let phasor = PhasorIter::new(10.0, 100.0);
-        let next  = phasor.take(11);
-        assert_eq!(0.1, next.last().unwrap())
+        let phasor = Phasor::new(10.0, 100.0);
+        let taken_iterator  = phasor.take(11);
+        assert_eq!(taken_iterator.last(), Some(0.1))
         // assert_eq!([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.1], next)
     }
 }
-- 
cgit v1.2.3