From af6f09e21e46fa6fd3990862a69efb52813c378a Mon Sep 17 00:00:00 2001 From: Grant Shangreaux Date: Fri, 3 Dec 2021 22:24:26 -0600 Subject: Add: Partial implementation to refactor Instrument --- docs/design.org | 30 ++++++++++++++++++++++++++++++ klangfarbrs/src/instrument.rs | 33 +++++++++++++++++++++------------ klangfarbrs/src/lib.rs | 8 ++++++-- 3 files changed, 57 insertions(+), 14 deletions(-) diff --git a/docs/design.org b/docs/design.org index d52ce05..c111716 100644 --- a/docs/design.org +++ b/docs/design.org @@ -89,4 +89,34 @@ An Instrument is N Sine(?) waves with an Envelope applied to it. it implements Iterator so that ~next()~ sums the oscillators, scales them down by 1/N, and multiplies by the Envelope value. +** Partial + + + The four arguments to each invocation of the partial abstraction specify: + +amplitude. +The amplitude of the partial at its peak, at the end of the +attack and the beginning of the decay of the note. + +relative duration. +This is multiplied by the overall note duration (controlled +in the main patch) to determine the duration of the decay portion of the +sinusoid. Individual partials may thus have different decay times, so that +some partials die out faster than others, under the main patch's overall +control. + +relative frequency. +As with the relative duration, this controls each partial's +frequency as a multiple of the overall frequency controlled in the main +patch. + +detune. +A frequency in Hertz to be added to the product of the global frequency and the relative frequency. + +Inside the partial abstraction, the amplitude is simply taken directly +from the ``$1" argument (multiplying by 0.1 to adjust for the high individual +amplitudes); the duration is calculated from the r duration object, multiplying +it by the ``$2" argument. The frequency is computed as $fp+d$ where $f$ +is the global frequency (from the r frequency object), $p$ is the relative +frequency of the partial, and $d$ is the detune frequency. diff --git a/klangfarbrs/src/instrument.rs b/klangfarbrs/src/instrument.rs index 3a381dc..bf08fe4 100644 --- a/klangfarbrs/src/instrument.rs +++ b/klangfarbrs/src/instrument.rs @@ -1,11 +1,18 @@ -use super::{ Osc, Envelope, Sample }; +use super::{ Partial, Sample, Hz, SamplesPerSecond }; pub struct Instrument { - pub osc_bank: Vec, - pub envelope: Envelope, + pub partials: Vec, } impl Instrument { + pub fn new(base_freq: Hz, partial_multipliers: Vec, sample_rate: SamplesPerSecond) -> Self { + Self { + partials: partial_multipliers.iter() + .map(|&p| Partial::new(1.0, 1.0, p, 0.0, sample_rate, 2000, base_freq)) + .collect() + } + } + pub fn sample(&mut self) -> Sample { match self.next() { Some(s) => { s }, @@ -18,12 +25,16 @@ impl Iterator for Instrument { type Item = Sample; fn next(&mut self) -> Option { - let goo : f32 = self.osc_bank.iter_mut().map(|o| o.sample()).sum(); - let scaled = goo / self.osc_bank.len() as f32; + let partial_samps : Vec> = self.partials.iter_mut() + .map(|o| o.next()).collect(); + + let filtered : Vec = partial_samps.iter().filter(|opt| opt.is_some()) + .map(|i| i.unwrap()).collect(); - match self.envelope.next() { - Some(a) => Some(scaled * a), - None => None + if filtered.is_empty() { + None + } else { + Some(filtered.iter().sum()) } } } @@ -35,11 +46,9 @@ mod tests { #[test] fn test_name() { let sr = 44800.0; - let mut inst = Instrument { - osc_bank: vec![Osc::new(220.0, sr), Osc::new(440.0, sr), Osc::new(880.0, sr)], - envelope: Envelope::new(10, 200, 0.7, 1000, sr), - }; + let mut inst = Instrument::new(220.0, vec![1.0, 2.0, 4.0], sr); assert_eq!(inst.next(), Some(0.0)); + assert_eq!(inst.last(), Some(0.0)); } } diff --git a/klangfarbrs/src/lib.rs b/klangfarbrs/src/lib.rs index 7cdb214..b0b9dc1 100644 --- a/klangfarbrs/src/lib.rs +++ b/klangfarbrs/src/lib.rs @@ -22,9 +22,13 @@ use osc::{Osc, Waveform}; pub mod envelope; use envelope::Envelope; +mod partial; +use partial::Partial; + mod instrument; use instrument::Instrument; + mod utils; /// Aliasing some types to distinguish various audio properties. @@ -75,7 +79,7 @@ impl MonoSynth { let sprt = 48000.0; Self { - instrument: Instrument{osc_bank: vec![Osc::new(freq, sprt), Osc::new(400.0, sprt)], envelope: Envelope::new(30, 500, 0.5, 1000, sprt)}, + instrument: Instrument::new(freq, vec![1.0, 0.909], sprt), sample_rate: sprt, frequency: freq, apply_bend: false, @@ -202,7 +206,7 @@ impl MonoSynth { #[export] fn trigger(&mut self, _owner: &Node, ) { - self.instrument.envelope = Envelope::new(self.attack, self.decay, self.sustain, self.release, self.sample_rate); + () } #[export] -- cgit v1.2.3