#![allow(clippy::excessive_precision)] /// The number of samples in one full turn. pub const N: usize = 256; /// One full turn of sine, sampled N times. pub const SINE: [f32; 256] = [ 0.0f32, 0.024541229009628296f32, 0.049067676067352295f32, 0.0735645666718483f32, 0.0980171412229538f32, 0.12241067737340927f32, 0.1467304676771164f32, 0.1709618866443634f32, 0.19509032368659973f32, 0.21910123527050018f32, 0.24298018217086792f32, 0.2667127549648285f32, 0.290284663438797f32, 0.3136817514896393f32, 0.3368898630142212f32, 0.3598950505256653f32, 0.3826834261417389f32, 0.40524131059646606f32, 0.4275550842285156f32, 0.4496113359928131f32, 0.4713967442512512f32, 0.49289819598197937f32, 0.5141027569770813f32, 0.5349976420402527f32, 0.5555702447891235f32, 0.5758081674575806f32, 0.5956993103027344f32, 0.6152315735816956f32, 0.6343932747840881f32, 0.6531728506088257f32, 0.6715589761734009f32, 0.6895405650138855f32, 0.7071067690849304f32, 0.7242470979690552f32, 0.7409511208534241f32, 0.7572088241577148f32, 0.7730104327201843f32, 0.7883464097976685f32, 0.803207516670227f32, 0.8175848126411438f32, 0.8314695954322815f32, 0.8448535799980164f32, 0.8577286005020142f32, 0.8700869679450989f32, 0.8819212913513184f32, 0.89322429895401f32, 0.903989315032959f32, 0.91420978307724f32, 0.9238795042037964f32, 0.9329928159713745f32, 0.9415440559387207f32, 0.949528157711029f32, 0.9569403529167175f32, 0.9637760519981384f32, 0.9700312614440918f32, 0.9757021069526672f32, 0.9807852506637573f32, 0.9852776527404785f32, 0.9891765117645264f32, 0.9924795627593994f32, 0.9951847195625305f32, 0.9972904324531555f32, 0.9987954497337341f32, 0.99969881772995f32, 1.0f32, 0.99969881772995f32, 0.9987954497337341f32, 0.9972904324531555f32, 0.9951847195625305f32, 0.9924795627593994f32, 0.9891765117645264f32, 0.9852776527404785f32, 0.9807852506637573f32, 0.9757021069526672f32, 0.9700312614440918f32, 0.9637760519981384f32, 0.9569403529167175f32, 0.949528157711029f32, 0.9415440559387207f32, 0.9329928159713745f32, 0.9238795042037964f32, 0.91420978307724f32, 0.903989315032959f32, 0.89322429895401f32, 0.8819212913513184f32, 0.8700869679450989f32, 0.8577286005020142f32, 0.8448535799980164f32, 0.8314695954322815f32, 0.8175848126411438f32, 0.803207516670227f32, 0.7883464097976685f32, 0.7730104327201843f32, 0.7572088241577148f32, 0.7409511208534241f32, 0.7242470979690552f32, 0.7071067690849304f32, 0.6895405650138855f32, 0.6715589761734009f32, 0.6531728506088257f32, 0.6343932747840881f32, 0.6152315735816956f32, 0.5956993103027344f32, 0.5758081674575806f32, 0.5555702447891235f32, 0.5349976420402527f32, 0.5141027569770813f32, 0.49289819598197937f32, 0.4713967442512512f32, 0.4496113359928131f32, 0.4275550842285156f32, 0.40524131059646606f32, 0.3826834261417389f32, 0.3598950505256653f32, 0.3368898630142212f32, 0.3136817514896393f32, 0.290284663438797f32, 0.2667127549648285f32, 0.24298018217086792f32, 0.21910123527050018f32, 0.19509032368659973f32, 0.1709618866443634f32, 0.1467304676771164f32, 0.12241067737340927f32, 0.0980171412229538f32, 0.0735645666718483f32, 0.049067676067352295f32, 0.024541229009628296f32, 1.2246468525851679e-16f32, -0.024541229009628296f32, -0.049067676067352295f32, -0.0735645666718483f32, -0.0980171412229538f32, -0.12241067737340927f32, -0.1467304676771164f32, -0.1709618866443634f32, -0.19509032368659973f32, -0.21910123527050018f32, -0.24298018217086792f32, -0.2667127549648285f32, -0.290284663438797f32, -0.3136817514896393f32, -0.3368898630142212f32, -0.3598950505256653f32, -0.3826834261417389f32, -0.40524131059646606f32, -0.4275550842285156f32, -0.4496113359928131f32, -0.4713967442512512f32, -0.49289819598197937f32, -0.5141027569770813f32, -0.5349976420402527f32, -0.5555702447891235f32, -0.5758081674575806f32, -0.5956993103027344f32, -0.6152315735816956f32, -0.6343932747840881f32, -0.6531728506088257f32, -0.6715589761734009f32, -0.6895405650138855f32, -0.7071067690849304f32, -0.7242470979690552f32, -0.7409511208534241f32, -0.7572088241577148f32, -0.7730104327201843f32, -0.7883464097976685f32, -0.803207516670227f32, -0.8175848126411438f32, -0.8314695954322815f32, -0.8448535799980164f32, -0.8577286005020142f32, -0.8700869679450989f32, -0.8819212913513184f32, -0.89322429895401f32, -0.903989315032959f32, -0.91420978307724f32, -0.9238795042037964f32, -0.9329928159713745f32, -0.9415440559387207f32, -0.949528157711029f32, -0.9569403529167175f32, -0.9637760519981384f32, -0.9700312614440918f32, -0.9757021069526672f32, -0.9807852506637573f32, -0.9852776527404785f32, -0.9891765117645264f32, -0.9924795627593994f32, -0.9951847195625305f32, -0.9972904324531555f32, -0.9987954497337341f32, -0.99969881772995f32, -1.0f32, -0.99969881772995f32, -0.9987954497337341f32, -0.9972904324531555f32, -0.9951847195625305f32, -0.9924795627593994f32, -0.9891765117645264f32, -0.9852776527404785f32, -0.9807852506637573f32, -0.9757021069526672f32, -0.9700312614440918f32, -0.9637760519981384f32, -0.9569403529167175f32, -0.949528157711029f32, -0.9415440559387207f32, -0.9329928159713745f32, -0.9238795042037964f32, -0.91420978307724f32, -0.903989315032959f32, -0.89322429895401f32, -0.8819212913513184f32, -0.8700869679450989f32, -0.8577286005020142f32, -0.8448535799980164f32, -0.8314695954322815f32, -0.8175848126411438f32, -0.803207516670227f32, -0.7883464097976685f32, -0.7730104327201843f32, -0.7572088241577148f32, -0.7409511208534241f32, -0.7242470979690552f32, -0.7071067690849304f32, -0.6895405650138855f32, -0.6715589761734009f32, -0.6531728506088257f32, -0.6343932747840881f32, -0.6152315735816956f32, -0.5956993103027344f32, -0.5758081674575806f32, -0.5555702447891235f32, -0.5349976420402527f32, -0.5141027569770813f32, -0.49289819598197937f32, -0.4713967442512512f32, -0.4496113359928131f32, -0.4275550842285156f32, -0.40524131059646606f32, -0.3826834261417389f32, -0.3598950505256653f32, -0.3368898630142212f32, -0.3136817514896393f32, -0.290284663438797f32, -0.2667127549648285f32, -0.24298018217086792f32, -0.21910123527050018f32, -0.19509032368659973f32, -0.1709618866443634f32, -0.1467304676771164f32, -0.12241067737340927f32, -0.0980171412229538f32, -0.0735645666718483f32, -0.049067676067352295f32, -0.024541229009628296f32, ]; /// Returns the sine at a sample index, wrapping past a full turn. #[inline] pub fn sin_idx(i: usize) -> f32 { SINE[i % N] } /// Returns the cosine at a sample index by a quarter-turn shift of the sine table. #[inline] pub fn cos_idx(i: usize) -> f32 { SINE[(i + N / 4) % N] } /// Returns the unit vector of cosine and sine at a sample index. #[inline] pub fn unit(i: usize) -> (f32, f32) { (cos_idx(i), sin_idx(i)) } /// A fractional sample index that wraps around the turn. #[derive(Clone, Copy, Debug, PartialEq)] pub struct FracIndex { /// The fractional index within one turn. pub value: f32, } impl FracIndex { /// Builds a fractional index wrapped into the turn. #[inline] pub fn new(value: f32) -> FracIndex { FracIndex { value }.wrapped() } /// Advances the index by a delta and wraps it back into the turn. #[inline] pub fn advance(&mut self, delta: f32) { self.value += delta; *self = self.wrapped(); } /// Rounds to the nearest whole sample index. #[inline] pub fn index(&self) -> usize { let r = (self.value + 0.5).floor(); (r as usize) % N } /// Returns the unit vector at the rounded index. #[inline] pub fn unit(&self) -> (f32, f32) { unit(self.index()) } #[inline] fn wrapped(self) -> FracIndex { let n = N as f32; let v = self.value - (self.value / n).floor() * n; FracIndex { value: v } } } /// Scales a count of turns into a fractional sample index. #[inline] pub fn index_from_turns(turns: f32) -> f32 { turns * N as f32 } #[cfg(test)] mod tests { use super::*; #[test] fn quarter_turns_are_exact() { assert_eq!(unit(0), (1.0, 0.0)); assert_eq!(sin_idx(N / 4), 1.0); assert!(cos_idx(N / 4).abs() < 1e-6); assert!(sin_idx(N / 2).abs() < 1e-6); assert_eq!(sin_idx(3 * N / 4), -1.0); } #[test] fn unit_vectors_are_unit_length() { for i in 0..N { let (c, s) = unit(i); let mag = c * c + s * s; assert!((mag - 1.0).abs() < 1e-5, "index {i} mag {mag}"); } } #[test] fn stride_divides_every_count() { for &count in &[1usize, 2, 4, 8, 16, 32, 64, 128] { assert_eq!(N % count, 0, "N must divide count {count}"); } } #[test] fn frac_index_wraps_with_floor_only() { let mut f = FracIndex::new(0.0); f.advance(N as f32 + 3.0); assert!(f.value >= 0.0 && f.value < N as f32); assert_eq!(f.index(), 3); } #[test] fn frac_index_negative_wraps() { let f = FracIndex::new(-1.0); assert!(f.value >= 0.0 && f.value < N as f32); assert_eq!(f.index(), N - 1); } }