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.0f32,
5//! 1.0f32].
6//!
7//! # Example
8//!
9//! ```
10//! use rodio::source::{SignalGenerator,Function};
11//!
12//! let tone = SignalGenerator::new(cpal::SampleRate(48000), 440.0, Function::Sine);
13//! ```
14use std::f32::consts::TAU;
15use std::time::Duration;
16
17use super::SeekError;
18use crate::Source;
19
20/// Waveform functions.
21#[derive(Clone, Debug)]
22pub enum Function {
23    /// A sinusoidal waveform.
24    Sine,
25    /// A triangle waveform.
26    Triangle,
27    /// A square wave, rising edge at t=0.
28    Square,
29    /// A rising sawtooth wave.
30    Sawtooth,
31}
32
33impl Function {
34    /// Create a single sample for the given waveform
35    #[inline]
36    fn render(&self, i: u64, period: f32) -> f32 {
37        let cycle_pos: f32 = i as f32 / period;
38
39        match self {
40            Self::Sine => (TAU * cycle_pos).sin(),
41            Self::Triangle => 4.0f32 * (cycle_pos - (cycle_pos + 0.5f32).floor()).abs() - 1f32,
42            Self::Square => {
43                if cycle_pos % 1.0f32 < 0.5f32 {
44                    1.0f32
45                } else {
46                    -1.0f32
47                }
48            }
49            Self::Sawtooth => 2.0f32 * (cycle_pos - (cycle_pos + 0.5f32).floor()),
50        }
51    }
52}
53
54/// An infinite source that produces one of a selection of test waveforms.
55#[derive(Clone, Debug)]
56pub struct SignalGenerator {
57    sample_rate: cpal::SampleRate,
58    period: f32,
59    function: Function,
60    i: u64,
61}
62
63impl SignalGenerator {
64    /// Create a new `TestWaveform` object that generates an endless waveform
65    /// `f`.
66    ///
67    /// # Panics
68    ///
69    /// Will panic if `frequency` is equal to zero.
70    #[inline]
71    pub fn new(sample_rate: cpal::SampleRate, frequency: f32, f: Function) -> SignalGenerator {
72        assert!(frequency != 0.0, "frequency must be greater than zero");
73        let period = sample_rate.0 as f32 / frequency;
74        SignalGenerator {
75            sample_rate,
76            period,
77            function: f,
78            i: 0,
79        }
80    }
81}
82
83impl Iterator for SignalGenerator {
84    type Item = f32;
85
86    #[inline]
87    fn next(&mut self) -> Option<f32> {
88        let val = Some(self.function.render(self.i, self.period));
89        self.i += 1;
90        val
91    }
92}
93
94impl Source for SignalGenerator {
95    #[inline]
96    fn current_frame_len(&self) -> Option<usize> {
97        None
98    }
99
100    #[inline]
101    fn channels(&self) -> u16 {
102        1
103    }
104
105    #[inline]
106    fn sample_rate(&self) -> u32 {
107        self.sample_rate.0
108    }
109
110    #[inline]
111    fn total_duration(&self) -> Option<Duration> {
112        None
113    }
114
115    #[inline]
116    fn try_seek(&mut self, duration: Duration) -> Result<(), SeekError> {
117        self.i = (self.sample_rate.0 as f32 * duration.as_secs_f32()) as u64;
118        Ok(())
119    }
120}
121
122#[cfg(test)]
123mod tests {
124    use crate::source::{Function, SignalGenerator};
125    use approx::assert_abs_diff_eq;
126
127    #[test]
128    fn square() {
129        let mut wf = SignalGenerator::new(cpal::SampleRate(2000), 500.0f32, Function::Square);
130        assert_eq!(wf.next(), Some(1.0f32));
131        assert_eq!(wf.next(), Some(1.0f32));
132        assert_eq!(wf.next(), Some(-1.0f32));
133        assert_eq!(wf.next(), Some(-1.0f32));
134        assert_eq!(wf.next(), Some(1.0f32));
135        assert_eq!(wf.next(), Some(1.0f32));
136        assert_eq!(wf.next(), Some(-1.0f32));
137        assert_eq!(wf.next(), Some(-1.0f32));
138    }
139
140    #[test]
141    fn triangle() {
142        let mut wf = SignalGenerator::new(cpal::SampleRate(8000), 1000.0f32, Function::Triangle);
143        assert_eq!(wf.next(), Some(-1.0f32));
144        assert_eq!(wf.next(), Some(-0.5f32));
145        assert_eq!(wf.next(), Some(0.0f32));
146        assert_eq!(wf.next(), Some(0.5f32));
147        assert_eq!(wf.next(), Some(1.0f32));
148        assert_eq!(wf.next(), Some(0.5f32));
149        assert_eq!(wf.next(), Some(0.0f32));
150        assert_eq!(wf.next(), Some(-0.5f32));
151        assert_eq!(wf.next(), Some(-1.0f32));
152        assert_eq!(wf.next(), Some(-0.5f32));
153        assert_eq!(wf.next(), Some(0.0f32));
154        assert_eq!(wf.next(), Some(0.5f32));
155        assert_eq!(wf.next(), Some(1.0f32));
156        assert_eq!(wf.next(), Some(0.5f32));
157        assert_eq!(wf.next(), Some(0.0f32));
158        assert_eq!(wf.next(), Some(-0.5f32));
159    }
160
161    #[test]
162    fn saw() {
163        let mut wf = SignalGenerator::new(cpal::SampleRate(200), 50.0f32, Function::Sawtooth);
164        assert_eq!(wf.next(), Some(0.0f32));
165        assert_eq!(wf.next(), Some(0.5f32));
166        assert_eq!(wf.next(), Some(-1.0f32));
167        assert_eq!(wf.next(), Some(-0.5f32));
168        assert_eq!(wf.next(), Some(0.0f32));
169        assert_eq!(wf.next(), Some(0.5f32));
170        assert_eq!(wf.next(), Some(-1.0f32));
171    }
172
173    #[test]
174    fn sine() {
175        let mut wf = SignalGenerator::new(cpal::SampleRate(1000), 100f32, Function::Sine);
176
177        assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32);
178        assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32);
179        assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652f32);
180        assert_abs_diff_eq!(wf.next().unwrap(), 0.95105652f32);
181        assert_abs_diff_eq!(wf.next().unwrap(), 0.58778525f32);
182        assert_abs_diff_eq!(wf.next().unwrap(), 0.0f32);
183        assert_abs_diff_eq!(wf.next().unwrap(), -0.58778554f32);
184    }
185}