summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJacob Lee <jacob@unabridgedsoftware.com>2021-11-24 23:32:31 -0500
committerJacob Lee <jacob@unabridgedsoftware.com>2021-11-24 23:32:31 -0500
commitaf484658565920005a502553422530493a5d91de (patch)
tree15852bf7ee4b261e39f10a9a590cacfbb2762d15
parent1a44b91562d41343da19e7521ab48b144881e2d0 (diff)
Refactor envelope to use line iterator
-rw-r--r--klangfarb/Main.tscn2
-rw-r--r--klangfarb/main.gd4
-rw-r--r--klangfarbrs/src/envelope.rs88
-rw-r--r--klangfarbrs/src/lib.rs35
-rw-r--r--klangfarbrs/src/line.rs68
5 files changed, 122 insertions, 75 deletions
diff --git a/klangfarb/Main.tscn b/klangfarb/Main.tscn
index 0c2f046..7b547cd 100644
--- a/klangfarb/Main.tscn
+++ b/klangfarb/Main.tscn
@@ -8,5 +8,5 @@
stream = SubResource( 1 )
volume_db = -13.216
script = ExtResource( 2 )
-frequency_modulation = true
+continuous = false
fm_index = 100.0
diff --git a/klangfarb/main.gd b/klangfarb/main.gd
index 751527a..86ebc87 100644
--- a/klangfarb/main.gd
+++ b/klangfarb/main.gd
@@ -64,6 +64,10 @@ func _process(_delta):
# synth.fm_frequency(fm_multiplier * freq)
synth.fm_depth(fm_index)
synth.continuous(continuous)
+ synth.set_attack(attack)
+ synth.set_decay(decay)
+ synth.set_sustain(sustain)
+ synth.set_release(release)
_check_waveform()
_fill_buffer()
diff --git a/klangfarbrs/src/envelope.rs b/klangfarbrs/src/envelope.rs
index 3da7977..ce3071d 100644
--- a/klangfarbrs/src/envelope.rs
+++ b/klangfarbrs/src/envelope.rs
@@ -1,25 +1,20 @@
-use super::{Millisecond, Amplitude, SamplesPerSecond};
+use super::{Millisecond, Amplitude, SamplesPerSecond, Line};
pub struct Envelope {
- pub attack: Vec<Amplitude>,
- pub decay: Vec<Amplitude>,
- pub release: Vec<Amplitude>,
- pub index: usize,
+ pub attack: Line,
+ pub decay: Line,
+ pub release: Line,
}
impl Envelope {
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));
+ let attack = Line::new(0.0, 1.0, attack, sample_rate);
+ let decay = Line::new(1.0, sustain, decay, sample_rate);
+ let release = Line::new(sustain, 0.0, release, sample_rate);
- Self { attack, decay, release, index: 0 }
- }
-
- pub fn len(&self) -> usize {
- self.attack.len() + self.decay.len() + self.release.len()
+ Self { attack, decay, release }
}
}
@@ -27,67 +22,16 @@ impl Iterator for Envelope {
type Item = Amplitude;
fn next(&mut self) -> Option<Self::Item> {
- let idx = self.index;
- let atk = self.attack.len();
- let atkdcy = atk + self.decay.len();
-
- self.index += 1;
-
- if idx < self.len() {
- let val = if idx < atk {
- self.attack[idx]
- } else if idx >= atk && idx < atkdcy {
- self.decay[idx - atk]
+ let mut val = self.attack.next();
+ if val.is_none() {
+ val = self.decay.next();
+ if val.is_none() {
+ self.release.next()
} else {
- self.release[idx - atkdcy]
- };
-
- Some(val)
- } else {
- None
- }
- }
-}
-
-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)
+ val
+ }
} else {
- amps.push(current_val);
- current_val += step_size;
+ val
}
}
-
- 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 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 fc5e6f9..e64246c 100644
--- a/klangfarbrs/src/lib.rs
+++ b/klangfarbrs/src/lib.rs
@@ -13,6 +13,8 @@ use gdnative::prelude::*;
use gdnative::core_types::TypedArray;
mod phasor;
+mod line;
+use line::Line;
mod osc;
use osc::{Osc, Waveform};
@@ -46,6 +48,10 @@ pub struct MonoSynth {
pub frequency_modulation: bool,
pub fm_frequency: Hz,
pub fm_depth: Amplitude,
+ pub attack: Millisecond,
+ pub decay: Millisecond,
+ pub sustain: Amplitude,
+ pub release: Millisecond,
fm_osc: Osc,
}
@@ -77,6 +83,10 @@ impl MonoSynth {
fm_frequency: 10.0,
fm_depth: 0.1,
fm_osc: Osc::new(freq * 2.0, sprt),
+ attack: 10,
+ decay: 100,
+ sustain: 0.5,
+ release: 500,
}
}
@@ -157,6 +167,26 @@ impl MonoSynth {
}
#[export]
+ fn set_attack(&mut self, _owner: &Node, attack: Millisecond) {
+ self.attack = attack
+ }
+
+ #[export]
+ fn set_decay(&mut self, _owner: &Node, decay: Millisecond) {
+ self.decay = decay
+ }
+
+ #[export]
+ fn set_sustain(&mut self, _owner: &Node, sustain: Amplitude) {
+ self.sustain = sustain
+ }
+
+ #[export]
+ fn set_release(&mut self, _owner: &Node, release: Millisecond) {
+ self.release = release
+ }
+
+ #[export]
fn envelope(
&mut self, _owner: &Node,
attack: Millisecond, decay: Millisecond, sustain: Amplitude, release: Millisecond
@@ -165,8 +195,9 @@ impl MonoSynth {
}
#[export]
- fn trigger(&mut self, _owner: &Node) {
- self.envelope.index = 0;
+ fn trigger(&mut self, _owner: &Node,
+ ) {
+ self.envelope = Envelope::new(self.attack, self.decay, self.sustain, self.release, self.sample_rate);
}
#[export]
diff --git a/klangfarbrs/src/line.rs b/klangfarbrs/src/line.rs
new file mode 100644
index 0000000..f57b103
--- /dev/null
+++ b/klangfarbrs/src/line.rs
@@ -0,0 +1,68 @@
+use super::{Millisecond, Amplitude, SamplesPerSecond};
+
+pub struct Line {
+ pub start: Amplitude,
+ pub end: Amplitude,
+ pub duration: Millisecond,
+ pub index: u32,
+ slope: f32,
+ samples: u32
+}
+
+impl Line {
+ pub fn new(
+ start: Amplitude, end: Amplitude, duration: Millisecond, sample_rate: SamplesPerSecond
+ ) -> Self {
+ Self { start, end, duration, index: 0, slope: slope(start, end, ms_to_samples(duration, sample_rate)), samples: ms_to_samples(duration, sample_rate) }
+ }
+}
+
+impl Iterator for Line {
+ type Item = Amplitude;
+
+ fn next(&mut self) -> Option<Self::Item> {
+ let idx = self.index;
+ let val = self.slope * idx as f32 + self.start;
+ self.index += 1;
+
+ if idx <= self.samples {
+ Some(val)
+ } else {
+ None
+ }
+
+ }
+}
+
+fn slope(start: Amplitude, end: Amplitude, duration: Millisecond) -> f32 {
+ return (end - start) / duration as f32 ;
+}
+
+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_calculates_a_slope() {
+ let expected = 0.5;
+ let slope = slope(0.0, 0.5, 1);
+ assert_eq! (expected, slope)
+ }
+
+ #[test]
+ fn it_calculates_the_next_values() {
+ let mut line = Line::new(0.0, 0.5, 1, 5000.0);
+ assert_eq!(0.0, line.next().unwrap());
+ assert_eq!(0.1, line.next().unwrap());
+ assert_eq!(0.2, line.next().unwrap());
+ assert_eq!(0.3, line.next().unwrap());
+ assert_eq!(0.4, line.next().unwrap());
+ assert_eq!(0.5, line.next().unwrap());
+ assert_eq!(None, line.next());
+ }
+}