rodio/source/
signal_generator.rs
1use std::f32::consts::TAU;
15use std::time::Duration;
16
17use super::SeekError;
18use crate::Source;
19
20#[derive(Clone, Debug)]
22pub enum Function {
23 Sine,
25 Triangle,
27 Square,
29 Sawtooth,
31}
32
33impl Function {
34 #[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#[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 #[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}