summaryrefslogtreecommitdiff
path: root/klangfarbrs/src
diff options
context:
space:
mode:
authorGrant Shangreaux <grant@unabridgedsoftware.com>2021-11-11 10:20:59 -0600
committerGrant Shangreaux <grant@unabridgedsoftware.com>2021-11-11 10:20:59 -0600
commita17829f5412ee0757574b34e34ce78b2975df1fe (patch)
treefb7e09aa7ee101bf48c824634f1e79428c0529ed /klangfarbrs/src
parent2b22b39b08c499798efcb874db6e6268eef43a52 (diff)
Add: poorly implemented but working amplitude envelope
Diffstat (limited to 'klangfarbrs/src')
-rw-r--r--klangfarbrs/src/adsr.rs54
-rw-r--r--klangfarbrs/src/lib.rs48
2 files changed, 79 insertions, 23 deletions
diff --git a/klangfarbrs/src/adsr.rs b/klangfarbrs/src/adsr.rs
index 682cc8a..9d51ba3 100644
--- a/klangfarbrs/src/adsr.rs
+++ b/klangfarbrs/src/adsr.rs
@@ -1,16 +1,18 @@
-use crate::{Millisecond, Amplitude};
+use super::{Millisecond, Amplitude, SamplesPerSecond};
pub struct Envelope {
- attack: Vec<Amplitude>,
- decay: Vec<Amplitude>,
- release: Vec<Amplitude>,
+ pub attack: Vec<Amplitude>,
+ pub decay: Vec<Amplitude>,
+ pub release: Vec<Amplitude>,
}
impl Envelope {
- pub fn new(attack: Millisecond, decay: Millisecond, sustain: Amplitude, release: Millisecond) -> Self {
- let attack = vec![sustain; attack as usize];
- let decay = vec![sustain; decay as usize];
- let release = vec![sustain; release as usize];
+ pub fn new(
+ attack: Millisecond, decay: Millisecond, sustain: Amplitude, release: Millisecond, sample_rate: SamplesPerSecond
+ ) -> Self {
+ let attack = interpolate(0.0, 1.0, ms_to_samples(attack, sample_rate));
+ let decay = interpolate(1.0, sustain, ms_to_samples(decay, sample_rate));
+ let release = interpolate(sustain, 0.0, ms_to_samples(release, sample_rate));
Self{attack, decay, release}
}
@@ -20,13 +22,45 @@ impl Envelope {
}
}
+fn interpolate(start: Amplitude, end: Amplitude, milliseconds: Millisecond) -> Vec<Amplitude> {
+ let step_size = (end - start) / milliseconds as f32;
+ let mut amps = vec!();
+ let mut current_val = start + step_size;
+
+ for i in 0..=milliseconds {
+ if i == 0 {
+ amps.push(start)
+ } else if i == milliseconds {
+ amps.push(end)
+ } else {
+ amps.push(current_val);
+ current_val += step_size;
+ }
+ }
+
+ amps
+}
+
+fn ms_to_samples(ms: Millisecond, sample_rate: SamplesPerSecond) -> u32 {
+ let multiplier = sample_rate as u32 / 1000;
+ multiplier * ms
+}
+
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn it_has_expected_total_length() {
- let total = Envelope::new(10, 10, 1.0, 10).len();
- assert_eq! (30, total)
+ let expected = 3 + ms_to_samples(30, 48000.0) as usize;
+ let total = Envelope::new(10, 10, 1.0, 10, 48000.0).len();
+ assert_eq! (expected, total)
+ }
+
+ #[test]
+ fn interpolate_works() {
+ let expected = vec![0.0, 0.2, 0.4, 0.6, 0.8, 1.0];
+ let result = interpolate(0.0, 1.0, 5);
+ assert_eq!(expected, result)
}
}
diff --git a/klangfarbrs/src/lib.rs b/klangfarbrs/src/lib.rs
index 0fc7936..ef9417c 100644
--- a/klangfarbrs/src/lib.rs
+++ b/klangfarbrs/src/lib.rs
@@ -47,10 +47,7 @@ pub struct MonoSynth {
pub continuous: bool,
pub duration: Millisecond,
// ADSR amplifier
- pub attack: Millisecond,
- pub decay: Millisecond,
- pub sustain: Amplitude,
- pub release: Millisecond,
+ envelope: Envelope,
// filter
pub cutoff: Hz,
// pub resonance:
@@ -59,6 +56,7 @@ pub struct MonoSynth {
pub fm_frequency: Hz,
pub fm_depth: Amplitude,
fm_phasor: Phasor,
+ current_envelope_position: usize,
}
pub struct Phasor {
@@ -113,15 +111,13 @@ impl MonoSynth {
phasor_bend: Vector2::new(0.0, 0.0),
continuous: true,
duration: 0,
- attack: 0,
- decay: 0,
- sustain: 0.0,
- release: 0,
+ envelope: Envelope::new(500, 1000, 0.5, 4000, 48000.0),
cutoff: 0.0,
frequency_modulation: false,
fm_frequency: 10.0,
fm_depth: 0.1,
- fm_phasor: Phasor { phase: 0.0 }
+ fm_phasor: Phasor { phase: 0.0 },
+ current_envelope_position: 0,
}
}
@@ -156,6 +152,11 @@ impl MonoSynth {
}
#[export]
+ fn continuous(&mut self, _owner: &Node, state: bool) {
+ self.continuous = state;
+ }
+
+ #[export]
fn phasor_bend(&mut self, _owner: &Node, phasor_bend: Vector2) {
self.phasor_bend = phasor_bend
}
@@ -185,11 +186,12 @@ impl MonoSynth {
self.fm_depth = fm_depth
}
+ #[export]
fn envelope(
- &self, _owner: &Node,
+ &mut self, _owner: &Node,
attack: Millisecond, decay: Millisecond, sustain: Amplitude, release: Millisecond
- ) -> Envelope {
- Envelope::new(attack, decay, sustain, release)
+ ) {
+ self.envelope = Envelope::new(attack, decay, sustain, release, self.sample_rate);
}
#[export]
@@ -197,7 +199,7 @@ impl MonoSynth {
let mut frames = TypedArray::new();
for _i in 0..samples {
- let sample = Osc::generate_sample(&self.waveform, self.phasor.phase);
+ let mut sample = Osc::generate_sample(&self.waveform, self.phasor.phase);
let next_phase : f32;
if self.frequency_modulation {
@@ -214,6 +216,26 @@ impl MonoSynth {
self.phasor.phase = next_phase;
}
+ if !self.continuous {
+ let pos = self.current_envelope_position;
+ let atk = self.envelope.attack.len();
+ let atkdcy = atk + self.envelope.decay.len();
+
+ if pos < atk {
+ sample = sample * self.envelope.attack[pos]
+ } else if pos >= atk && pos < atkdcy {
+ sample = sample * self.envelope.decay[pos - atk]
+ } else if pos < self.envelope.len() {
+ sample = sample * self.envelope.release[pos - atkdcy]
+ }
+
+ self.current_envelope_position += 1;
+
+ if self.current_envelope_position >= self.envelope.len() {
+ self.current_envelope_position = 0;
+ }
+ }
+
frames.push(Vector2::new(sample, sample));
}