1static SINE: [i16; 256] = sine_table();
6
7const fn sine_table() -> [i16; 256] {
8 let mut t = [0i16; 256];
9 let mut i = 0;
10 while i < 256 {
11 let mut x = (i as f64) * (2.0 * core::f64::consts::PI / 256.0);
14 if x > core::f64::consts::PI {
15 x -= 2.0 * core::f64::consts::PI;
16 }
17 let (mut term, mut sum, mut k) = (x, 0.0, 1);
18 while k < 23 {
19 sum += term;
20 term *= -x * x / (((k + 1) * (k + 2)) as f64);
21 k += 2;
22 }
23 let v = sum * 32767.0;
24 t[i] = (if v >= 0.0 { v + 0.5 } else { v - 0.5 }) as i16;
25 i += 1;
26 }
27 t
28}
29
30#[derive(Clone, Copy, Debug)]
32#[cfg_attr(feature = "defmt", derive(defmt::Format))]
33pub struct Sine {
34 phase: u32,
35 step: u32,
36 amplitude: i32,
37}
38
39impl Sine {
40 pub fn new(freq_hz: f32, sample_rate: u32, amplitude: f32) -> Self {
43 let step = (freq_hz as f64 / f64::from(sample_rate) * 4_294_967_296.0) as u32;
46 let amplitude = (amplitude.clamp(0.0, 1.0) * 32768.0) as i32;
47 Self {
48 phase: 0,
49 step,
50 amplitude,
51 }
52 }
53
54 pub fn set_frequency(&mut self, freq_hz: f32, sample_rate: u32) {
57 self.step = Self::new(freq_hz, sample_rate, 0.0).step;
58 }
59
60 pub fn next_sample(&mut self) -> i16 {
62 let s = i32::from(SINE[(self.phase >> 24) as usize]);
63 self.phase = self.phase.wrapping_add(self.step);
64 ((s * self.amplitude) >> 15) as i16
65 }
66
67 pub fn fill(&mut self, frames: &mut [i16], channels: usize) {
69 for frame in frames.chunks_exact_mut(channels.max(1)) {
70 frame.fill(self.next_sample());
71 }
72 }
73}
74
75#[cfg(test)]
76mod tests {
77 extern crate std;
78 use super::*;
79 use crate::level::Level;
80
81 #[test]
82 fn table_hits_the_cardinal_points() {
83 assert_eq!(SINE[0], 0);
84 assert_eq!(SINE[64], 32767);
85 assert_eq!(SINE[128], 0);
86 assert_eq!(SINE[192], -32767);
87 }
88
89 #[test]
90 fn full_scale_1khz_stereo() {
91 let mut osc = Sine::new(1000.0, 48_000, 1.0);
92 let mut buf = [0i16; 2 * 4800];
93 osc.fill(&mut buf, 2);
94 assert!(buf.chunks(2).all(|f| f[0] == f[1]), "channels differ");
95
96 let l = Level::measure(&buf, 0, 2);
97 assert!((l.rms_dbfs() + 3.01).abs() < 0.05, "rms {}", l.rms_dbfs());
98 assert!(l.dc.abs() < 1e-3, "dc {}", l.dc);
99
100 let left: std::vec::Vec<i16> = buf.iter().step_by(2).copied().collect();
102 let crossings = left.windows(2).filter(|w| (w[0] < 0) != (w[1] < 0)).count();
103 assert!((199..=201).contains(&crossings), "{crossings} crossings");
104 }
105
106 #[test]
107 fn frequency_changes_are_continuous() {
108 let mut osc = Sine::new(1000.0, 48_000, 1.0);
110 let mut prev = 0i32;
111 for i in 0..2000 {
112 if i == 1000 {
113 osc.set_frequency(2000.0, 48_000);
114 }
115 let s = i32::from(osc.next_sample());
116 assert!(
118 i == 0 || (s - prev).abs() < 9000,
119 "jump at {i}: {prev} -> {s}"
120 );
121 prev = s;
122 }
123 }
124}