use std::time::Duration;
use fundsp::{
audionode::AudioNode,
buffer::{BufferMut, BufferRef},
prelude::An,
shared::Shared,
typenum::{U0, U1},
Frame,
};
use crate::utils::ahdsr::{AhdsrEnvelope, AhdsrParameters};
#[derive(Clone)]
pub struct SharedAhdsrNode {
params: AhdsrParameters,
envelope: AhdsrEnvelope,
gate_shared: Shared,
attack_shared: Shared,
hold_shared: Shared,
decay_shared: Shared,
sustain_shared: Shared,
release_shared: Shared,
last_gate: f32,
}
impl SharedAhdsrNode {
pub fn new(
gate: Shared,
attack: Shared,
hold: Shared,
decay: Shared,
sustain: Shared,
release: Shared,
) -> Self {
Self {
params: AhdsrParameters::default(),
envelope: AhdsrEnvelope::new(),
gate_shared: gate,
attack_shared: attack,
hold_shared: hold,
decay_shared: decay,
sustain_shared: sustain,
release_shared: release,
last_gate: 0.0,
}
}
}
impl AudioNode for SharedAhdsrNode {
const ID: u64 = 101; type Inputs = U0;
type Outputs = U1;
#[inline]
fn tick(&mut self, _input: &Frame<f32, Self::Inputs>) -> Frame<f32, Self::Outputs> {
let attack = self.attack_shared.value();
let hold = self.hold_shared.value();
let decay = self.decay_shared.value();
let sustain = self.sustain_shared.value();
let release = self.release_shared.value();
let _ = self.params.set_attack_time(Duration::from_secs_f32(attack));
let _ = self.params.set_hold_time(Duration::from_secs_f32(hold));
let _ = self.params.set_decay_time(Duration::from_secs_f32(decay));
let _ = self.params.set_sustain_level(sustain);
let _ = self
.params
.set_release_time(Duration::from_secs_f32(release));
let gate = self.gate_shared.value();
if gate > 0.0 && self.last_gate <= 0.0 {
self.envelope.note_on(&self.params, 1.0);
} else if gate <= 0.0 && self.last_gate > 0.0 {
self.envelope.note_off(&self.params);
}
self.last_gate = gate;
let output = self.envelope.run(&self.params);
[output].into()
}
fn process(&mut self, size: usize, _input: &BufferRef, output: &mut BufferMut) {
let attack = self.attack_shared.value();
let hold = self.hold_shared.value();
let decay = self.decay_shared.value();
let sustain = self.sustain_shared.value();
let release = self.release_shared.value();
let _ = self.params.set_attack_time(Duration::from_secs_f32(attack));
let _ = self.params.set_hold_time(Duration::from_secs_f32(hold));
let _ = self.params.set_decay_time(Duration::from_secs_f32(decay));
let _ = self.params.set_sustain_level(sustain);
let _ = self
.params
.set_release_time(Duration::from_secs_f32(release));
let gate = self.gate_shared.value();
if gate > 0.0 && self.last_gate <= 0.0 {
self.envelope.note_on(&self.params, 1.0);
} else if gate <= 0.0 && self.last_gate > 0.0 {
self.envelope.note_off(&self.params);
}
self.last_gate = gate;
debug_assert!(self.inputs() == 0);
debug_assert!(self.outputs() == 1);
let output_buffer = output.channel_f32_mut(0);
self.envelope
.process(&self.params, &mut output_buffer[0..size]);
}
fn reset(&mut self) {
self.envelope.reset();
self.last_gate = 0.0;
}
fn set_sample_rate(&mut self, sample_rate: f64) {
let _ = self.params.set_sample_rate(sample_rate as u32);
}
}
pub fn shared_ahdsr(
gate: Shared,
attack: Shared,
hold: Shared,
decay: Shared,
sustain: Shared,
release: Shared,
) -> An<SharedAhdsrNode> {
An(SharedAhdsrNode::new(
gate, attack, hold, decay, sustain, release,
))
}
#[cfg(test)]
mod tests {
use fundsp::prelude32::shared;
use super::*;
#[test]
fn test_ahdsr_node_creation() {
let gate = shared(0.0);
let attack = shared(0.01);
let hold = shared(0.0);
let decay = shared(0.1);
let sustain = shared(0.7);
let release = shared(0.5);
let mut node = SharedAhdsrNode::new(gate, attack, hold, decay, sustain, release);
node.set_sample_rate(44100.0);
let output = node.tick(&Frame::default());
assert_eq!(output[0], 0.0);
}
#[test]
fn test_ahdsr_node_gate_trigger() {
let gate = shared(0.0);
let attack = shared(0.01);
let hold = shared(0.0);
let decay = shared(0.1);
let sustain = shared(0.7);
let release = shared(0.5);
let mut node = SharedAhdsrNode::new(gate.clone(), attack, hold, decay, sustain, release);
node.set_sample_rate(44100.0);
gate.set_value(1.0);
let output = node.tick(&Frame::default());
assert!(output[0] > 0.0);
}
#[test]
fn test_ahdsr_node_reset() {
let gate = shared(1.0);
let attack = shared(0.0);
let hold = shared(0.01);
let decay = shared(0.1);
let sustain = shared(0.7);
let release = shared(0.5);
let mut node = SharedAhdsrNode::new(gate, attack, hold, decay, sustain, release);
node.set_sample_rate(44100.0);
for _ in 0..100 {
node.tick(&Frame::default());
}
node.reset();
let output = node.tick(&Frame::default());
assert_eq!(output[0], 1.0);
}
}