From d3776887db90ac1ea15fd55d833ff6b3042e7caf Mon Sep 17 00:00:00 2001 From: Grant Shangreaux 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') 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 { let mut frames = TypedArray::new(); - let mut rng = rand::thread_rng(); - let mut last_value = (rng.gen::() * 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 { + 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::()) * 0.2 - 0.1).clamp(-1.0, 1.0) + (self.last_value + (rng.gen::()) * 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.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