1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
//! Attack-Decay-Sustain-Release Envelope
//!
//! This envelope is built upon the
//! [`envelope2()`](https://docs.rs/fundsp/0.9.0/fundsp/prelude/fn.envelope2.html) function to
//! control volume over time.
//!
//! When a sound begins, its volume increases from zero to one in a time interval called the
//! "Attack". It then decreases from 1.0 to the "Sustain" volume in a time interval called the
//! "Decay". It remains at the "Sustain" level until an external input indicates that the note
//! is finished, after which it decreases from the
//! "Sustain" level to 0.0 in a time interval called the "Release".
//!
//! The example [`live_adsr.rs`](https://github.com/SamiPerttu/fundsp/blob/master/examples/live_adsr.rs)
//! is a fully functional demonstration of `adsr_live()`. It will listen to messages from the first
//! connected MIDI input device it finds, and play the corresponding pitches with the volume moderated by
//! an `adsr_live()` envelope.
use super::Real;
use super::prelude::{An, EnvelopeIn, Frame, U1, clamp01, delerp, envelope2, lerp, shared, var};
pub fn adsr_live(
attack: f32,
decay: f32,
sustain: f32,
release: f32,
) -> An<EnvelopeIn<f32, impl FnMut(f32, &Frame<f32, U1>) -> f32 + Clone, U1, f32>> {
let neg1 = -1.0;
let zero = 0.0;
let mut attacked = false;
let a = shared(zero);
let b = shared(neg1);
let attack_start = var(&a);
let release_start = var(&b);
envelope2(move |time, control| {
if release_start.value() >= zero && control > zero {
attacked = true;
attack_start.set_value(time);
release_start.set_value(neg1);
} else if release_start.value() < zero && control <= zero {
release_start.set_value(time);
}
if !attacked {
return 0.0;
}
let ads_value = ads(attack, decay, sustain, time - attack_start.value());
if release_start.value() < zero {
ads_value
} else {
ads_value
* clamp01(delerp(
release_start.value() + release,
release_start.value(),
time,
))
}
})
}
fn ads<F: Real>(attack: F, decay: F, sustain: F, time: F) -> F {
if time < attack {
lerp(F::from_f64(0.0), F::from_f64(1.0), time / attack)
} else {
let decay_time = time - attack;
if decay_time < decay {
lerp(F::from_f64(1.0), sustain, decay_time / decay)
} else {
sustain
}
}
}