second_music_system/
fader.rs

1use crate::PosFloat;
2
3/// Specifies what kind of curve to use in a fade.
4///
5/// Logarithmic fades will have (roughly) the same perceived volume change per
6/// unit time. Linear fades will seem to speed up or slow down over the course
7/// of the fade, and should be used when "intermixing" related tracks.
8/// Exponential fades will have the variable-speed "problem" even worse, but
9/// may sound the best of the three.
10#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
11pub enum FadeType {
12    /// Fades between the given volumes on a logarithmic curve, such that any
13    /// given timespan within the fade will have the same perceived volume
14    /// change as any other.
15    Logarithmic,
16    /// Fades linearly between the given amplification factors. You only want
17    /// this when you're crossfading between partly correlated samples.
18    Linear,
19    /// Fades between the given volumes on an exponential curve, resulting in
20    /// a fade that "hangs out" at the louder side. Arguably more
21    /// aesthetically pleasing than a logarithmic fade.
22    #[default]
23    Exponential,
24}
25
26#[derive(Debug, Clone, Copy)]
27pub(crate) enum FadeCurve {
28    Logarithmic { pos: f32, step: f32 },
29    Exponential { pos: f32, step: f32 },
30    Linear { pos: f32, step: f32 },
31}
32
33/// Natural logarithm of the quietest amplitude we consider audible.
34/// This value is equivalent to a volume level of about -96.3dB, and also the
35/// ratio of the smallest non-zero voltage to the largest non-zero voltage that
36/// a 16-bit DAC will output.
37const SILENT_LOG: f32 = -11.1;
38
39/// Natural exponent of the quietest amplitude we consider audible.
40/// This value is equivalent to a volume level of about -96.3dB, and also the
41/// ratio of the smallest non-zero voltage to the largest non-zero voltage that
42/// a 16-bit DAC will output.
43const SILENT_EXP: f32 = 1.0000152;
44
45impl FadeCurve {
46    pub fn from(
47        typ: FadeType,
48        from: PosFloat,
49        to: PosFloat,
50        length: PosFloat,
51    ) -> FadeCurve {
52        match typ {
53            FadeType::Exponential => {
54                let from = from.exp().max(SILENT_EXP);
55                let to = to.exp().max(SILENT_EXP);
56                let step = (to - from) / (*length + 1.0);
57                FadeCurve::Exponential { pos: from, step }
58            }
59            FadeType::Logarithmic => {
60                let from = from.ln().max(SILENT_LOG);
61                let to = to.ln().max(SILENT_LOG);
62                let step = (to - from) / (*length + 1.0);
63                FadeCurve::Logarithmic { pos: from, step }
64            }
65            FadeType::Linear => {
66                let step = (*to - *from) / (*length + 1.0);
67                FadeCurve::Linear { pos: *from, step }
68            }
69        }
70    }
71    /// Evaluate the current state of the fader.
72    fn evaluate(&self) -> PosFloat {
73        PosFloat::new_clamped(match self {
74            Self::Exponential { pos, .. } => pos.ln(),
75            Self::Logarithmic { pos, .. } => pos.exp(),
76            Self::Linear { pos, .. } => *pos,
77        })
78    }
79    /// Evaluate the state of the fader t steps into the future.
80    fn evaluate_t(&self, t: PosFloat) -> PosFloat {
81        PosFloat::new_clamped(match self {
82            Self::Exponential { pos, step } => (pos + step * *t).ln(),
83            Self::Logarithmic { pos, step } => (pos + step * *t).exp(),
84            Self::Linear { pos, step } => *pos + step * *t,
85        })
86    }
87    /// Step by a single sample frame
88    fn step_by_one(&mut self) {
89        match self {
90            Self::Logarithmic { pos, step }
91            | Self::Exponential { pos, step }
92            | Self::Linear { pos, step } => *pos += *step,
93        }
94    }
95    /// Step by a given number of sample frames
96    fn step_by(&mut self, count: PosFloat) {
97        match self {
98            Self::Logarithmic { pos, step }
99            | Self::Exponential { pos, step }
100            | Self::Linear { pos, step } => *pos += *step * *count,
101        }
102    }
103}
104
105/// Represents a fade, in or out, currently in progress.
106#[derive(Debug, Clone)]
107pub struct Fader {
108    curve: FadeCurve,
109    to: PosFloat,
110    length: PosFloat, // given in SAMPLE FRAMES
111    pos: PosFloat,    // given in SAMPLE FRAMES
112}
113
114impl Fader {
115    /// Blank fader. Just has volume at the given level.
116    pub fn new(volume: PosFloat) -> Fader {
117        Fader {
118            curve: FadeCurve::Linear {
119                pos: *volume,
120                step: 0.0,
121            },
122            to: volume,
123            length: PosFloat::ZERO,
124            pos: PosFloat::ONE,
125        }
126    }
127    /// Start a fade.
128    /// - `type`: A [`FadeType`](enum.FadeType.html) denoting which curve to
129    ///   use.
130    /// - `from`: The starting volume of the fade.
131    /// - `to`: The ending volume of the fade.
132    /// - `length`: How long, in **sample frames**, the fade should take to
133    ///   complete.
134    pub fn start(
135        typ: FadeType,
136        from: PosFloat,
137        to: PosFloat,
138        length: PosFloat,
139    ) -> Fader {
140        Fader {
141            curve: FadeCurve::from(typ, from, to, length),
142            to,
143            length,
144            pos: PosFloat::ZERO,
145        }
146    }
147    /// As `start`, but returns `None` if the given length is infinite, zero,
148    /// or negative.
149    pub fn maybe_start(
150        typ: FadeType,
151        from: PosFloat,
152        to: PosFloat,
153        length: PosFloat,
154    ) -> Option<Fader> {
155        if length > PosFloat::ZERO {
156            Some(Fader::start(typ, from, to, length))
157        } else {
158            None
159        }
160    }
161    /// Returns true if the fade has run its course.
162    pub fn complete(&self) -> bool {
163        self.pos >= self.length
164    }
165    /// Evaluate the current volume.
166    pub fn evaluate(&self) -> PosFloat {
167        if self.complete() {
168            self.to
169        } else {
170            self.curve.evaluate()
171        }
172    }
173    /// Evaluate the volume `t` steps into the future.
174    pub fn evaluate_t(&self, t: PosFloat) -> PosFloat {
175        let new_pos = self.pos + t;
176        if new_pos >= self.length {
177            self.to
178        } else {
179            self.curve.evaluate_t(t)
180        }
181    }
182    /// Step by a single sample frame
183    pub fn step_by_one(&mut self) {
184        if !self.complete() {
185            self.curve.step_by_one();
186            self.pos = self.pos + PosFloat::ONE;
187        }
188    }
189    /// Step by a give number of sample frames
190    pub fn step_by(&mut self, count: PosFloat) {
191        if !self.complete() {
192            self.curve.step_by(count);
193            self.pos = self.pos + count;
194        }
195    }
196}
197
198impl Iterator for Fader {
199    type Item = PosFloat;
200    fn next(&mut self) -> Option<PosFloat> {
201        if self.complete() {
202            None
203        } else {
204            let ret = self.evaluate();
205            self.step_by_one();
206            Some(ret)
207        }
208    }
209    fn size_hint(&self) -> (usize, Option<usize>) {
210        let size = (self.length.ceil() - *self.pos) as usize;
211        (size, Some(size))
212    }
213    fn count(self) -> usize {
214        (self.length.ceil() - *self.pos) as usize
215    }
216}