Skip to main content

rodio/source/
signal_generator.rs

1//! Generator sources for various periodic test waveforms.
2//!
3//! This module provides several periodic, deterministic waveforms for testing other sources and
4//! for simple additive sound synthesis. Every source is monoaural and in the codomain [-1.0, 1.0].
5//!
6//! # Example
7//!
8//! ```
9//! use rodio::source::{SignalGenerator,Function};
10//! use core::num::NonZero;
11//!
12//! let tone = SignalGenerator::new(NonZero::new(48000).unwrap(), 440.0, Function::Sine);
13//! ```
14use super::SeekError;
15use crate::common::{ChannelCount, SampleRate};
16use crate::math::{duration_to_float, nz, TAU};
17use crate::{Float, Sample, Source};
18use std::time::Duration;
19
20/// Generator function.
21///
22/// A generator function is the core of a signal generator, the `SignalGenerator` type uses these
23/// function to create periodic waveforms.
24///
25/// # Arguments
26///  *  A `Float` representing a time in the signal to generate. The scale of this variable is
27///     normalized to the period of the signal, such that "0.0" is time zero, "1.0" is one period of
28///     the signal, "2.0" is two periods and so on. This function should be written to accept any
29///     float in the range (`Float::MIN`, `Float::MAX`) but `SignalGenerator` will only pass values in
30///     (0.0, 1.0) to mitigate floating point error.
31///
32/// # Returns
33///
34/// A `Sample` (Float) representing the signal level at the passed time. This value should be normalized
35/// in the range [-1.0,1.0].
36pub type GeneratorFunction = fn(Float) -> Sample;
37
38/// Waveform functions.
39#[derive(Clone, Debug)]
40pub enum Function {
41    /// A sinusoidal waveform.
42    Sine,
43    /// A triangle waveform.
44    Triangle,
45    /// A square wave, rising edge at t=0.
46    Square,
47    /// A rising sawtooth wave.
48    Sawtooth,
49}
50
51fn sine_signal(phase: Float) -> Sample {
52    (TAU * phase).sin()
53}
54
55fn triangle_signal(phase: Float) -> Sample {
56    4.0 * (phase - (phase + 0.5).floor()).abs() - 1.0
57}
58
59fn square_signal(phase: Float) -> Sample {
60    if phase % 1.0 < 0.5 {
61        1.0
62    } else {
63        -1.0
64    }
65}
66
67fn sawtooth_signal(phase: Float) -> Sample {
68    2.0 * (phase - (phase + 0.5).floor())
69}
70
71/// An infinite source that produces one of a selection of test waveforms.
72#[derive(Clone, Debug)]
73pub struct SignalGenerator {
74    sample_rate: SampleRate,
75    function: GeneratorFunction,
76    phase_step: Float,
77    phase: Float,
78    period: Float,
79}
80
81impl SignalGenerator {
82    /// Create a new `SignalGenerator` object that generates an endless waveform
83    /// `f`.
84    ///
85    /// # Panics
86    ///
87    /// Will panic if `frequency` is equal to zero.
88    #[inline]
89    pub fn new(sample_rate: SampleRate, frequency: f32, f: Function) -> Self {
90        let function: GeneratorFunction = match f {
91            Function::Sine => sine_signal,
92            Function::Triangle => triangle_signal,
93            Function::Square => square_signal,
94            Function::Sawtooth => sawtooth_signal,
95        };
96
97        Self::with_function(sample_rate, frequency, function)
98    }
99
100    /// Create a new `SignalGenerator` object that generates an endless waveform
101    /// from the [generator function](crate::source::signal_generator::GeneratorFunction) `generator_function`.
102    ///
103    /// # Panics
104    ///
105    /// Will panic if `frequency` is equal to zero.
106    #[inline]
107    pub fn with_function(
108        sample_rate: SampleRate,
109        frequency: f32,
110        generator_function: GeneratorFunction,
111    ) -> Self {
112        assert!(frequency > 0.0, "frequency must be greater than zero");
113        let period = sample_rate.get() as Float / frequency as Float;
114        let phase_step = 1.0 / period;
115
116        SignalGenerator {
117            sample_rate,
118            function: generator_function,
119            phase_step,
120            phase: 0.0,
121            period,
122        }
123    }
124}
125
126impl Iterator for SignalGenerator {
127    type Item = Sample;
128
129    #[inline]
130    fn next(&mut self) -> Option<Sample> {
131        let f = self.function;
132        let val = Some(f(self.phase));
133        self.phase = (self.phase + self.phase_step).rem_euclid(1.0);
134        val
135    }
136}
137
138impl Source for SignalGenerator {
139    #[inline]
140    fn current_span_len(&self) -> Option<usize> {
141        None
142    }
143
144    #[inline]
145    fn channels(&self) -> ChannelCount {
146        nz!(1)
147    }
148
149    #[inline]
150    fn sample_rate(&self) -> SampleRate {
151        self.sample_rate
152    }
153
154    #[inline]
155    fn total_duration(&self) -> Option<Duration> {
156        None
157    }
158
159    #[inline]
160    fn try_seek(&mut self, duration: Duration) -> Result<(), SeekError> {
161        let seek = duration_to_float(duration) * (self.sample_rate.get() as Float) / self.period;
162        self.phase = seek.rem_euclid(1.0);
163        Ok(())
164    }
165}
166
167#[cfg(test)]
168mod tests {
169    use crate::math::nz;
170    use crate::source::{Function, SignalGenerator};
171    use crate::Sample;
172    use approx::assert_abs_diff_eq;
173
174    const TEST_EPSILON: Sample = 0.0001;
175
176    #[test]
177    fn square() {
178        let mut wf = SignalGenerator::new(nz!(2000), 500.0, Function::Square);
179        assert_eq!(wf.next(), Some(1.0));
180        assert_eq!(wf.next(), Some(1.0));
181        assert_eq!(wf.next(), Some(-1.0));
182        assert_eq!(wf.next(), Some(-1.0));
183        assert_eq!(wf.next(), Some(1.0));
184        assert_eq!(wf.next(), Some(1.0));
185        assert_eq!(wf.next(), Some(-1.0));
186        assert_eq!(wf.next(), Some(-1.0));
187    }
188
189    #[test]
190    fn triangle() {
191        let mut wf = SignalGenerator::new(nz!(8000), 1000.0, Function::Triangle);
192        assert_eq!(wf.next(), Some(-1.0));
193        assert_eq!(wf.next(), Some(-0.5));
194        assert_eq!(wf.next(), Some(0.0));
195        assert_eq!(wf.next(), Some(0.5));
196        assert_eq!(wf.next(), Some(1.0));
197        assert_eq!(wf.next(), Some(0.5));
198        assert_eq!(wf.next(), Some(0.0));
199        assert_eq!(wf.next(), Some(-0.5));
200        assert_eq!(wf.next(), Some(-1.0));
201        assert_eq!(wf.next(), Some(-0.5));
202        assert_eq!(wf.next(), Some(0.0));
203        assert_eq!(wf.next(), Some(0.5));
204        assert_eq!(wf.next(), Some(1.0));
205        assert_eq!(wf.next(), Some(0.5));
206        assert_eq!(wf.next(), Some(0.0));
207        assert_eq!(wf.next(), Some(-0.5));
208    }
209
210    #[test]
211    fn saw() {
212        let mut wf = SignalGenerator::new(nz!(200), 50.0, Function::Sawtooth);
213        assert_eq!(wf.next(), Some(0.0));
214        assert_eq!(wf.next(), Some(0.5));
215        assert_eq!(wf.next(), Some(-1.0));
216        assert_eq!(wf.next(), Some(-0.5));
217        assert_eq!(wf.next(), Some(0.0));
218        assert_eq!(wf.next(), Some(0.5));
219        assert_eq!(wf.next(), Some(-1.0));
220    }
221
222    #[test]
223    fn sine() {
224        let mut wf = SignalGenerator::new(nz!(1000), 100f32, Function::Sine);
225
226        assert_abs_diff_eq!(wf.next().unwrap(), 0.0, epsilon = TEST_EPSILON);
227        assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525, epsilon = TEST_EPSILON);
228        assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652, epsilon = TEST_EPSILON);
229        assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652, epsilon = TEST_EPSILON);
230        assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525, epsilon = TEST_EPSILON);
231        assert_abs_diff_eq!(wf.next().unwrap(), 0.0, epsilon = TEST_EPSILON);
232        assert_abs_diff_eq!(wf.next().unwrap(), -0.58778554, epsilon = TEST_EPSILON);
233    }
234}