1use std::f64::consts::PI;
5
6pub struct MdctNaive {
8 pub len: usize,
10 pub scale: f64,
12 plan: Option<FftPlan>,
13}
14
15impl MdctNaive {
16 pub fn new(len: usize, scale: f64) -> Self {
17 Self {
18 len,
19 scale,
20 plan: (len >= 2 && len.is_power_of_two()).then(|| FftPlan::new(len)),
21 }
22 }
23
24 pub fn imdct_half(&self, dst: &mut [f32], src: &[f32]) {
29 if let Some(plan) = self
30 .plan
31 .as_ref()
32 .filter(|plan| plan.phase.len() * 2 == self.len)
33 {
34 plan.imdct_half(dst, src, self.scale);
35 return;
36 }
37 self.imdct_half_naive(dst, src);
40 }
41
42 fn imdct_half_naive(&self, dst: &mut [f32], src: &[f32]) {
43 let len = self.len >> 1;
46 let len2 = len * 2;
47 let phase = PI / (4.0 * (len2 as f64));
48
49 for i in 0..len {
50 let mut sum_d: f64 = 0.0;
51 let mut sum_u: f64 = 0.0;
52
53 let i_d = phase * ((4 * len - 2 * i - 1) as f64);
54 let i_u = phase * ((3 * len2 + 2 * i + 1) as f64);
55
56 for j in 0..len2 {
57 let a = (2 * j + 1) as f64;
58 let a_d = (a * i_d).cos();
59 let a_u = (a * i_u).cos();
60 let val = src[j] as f64;
61 sum_d += a_d * val;
62 sum_u += a_u * val;
63 }
64
65 dst[i] = (sum_d * self.scale) as f32;
66 dst[i + len] = (-(sum_u * self.scale)) as f32;
67 }
68 }
69
70 pub fn imdct_full(&self, dst: &mut [f32], src: &[f32]) {
75 let len = self.len * 2;
77 let len2 = len / 2;
78 let len4 = len / 4;
79
80 self.imdct_half(&mut dst[len4..len4 + len2], src);
82
83 for i in 0..len4 {
84 dst[i] = -dst[len2 - i - 1];
85 dst[len - i - 1] = dst[len2 + i];
86 }
87 }
88}
89
90#[derive(Clone, Copy, Default)]
91struct Complex {
92 re: f64,
93 im: f64,
94}
95
96impl Complex {
97 fn rotation(angle: f64) -> Self {
98 let (im, re) = angle.sin_cos();
99 Self { re, im }
100 }
101
102 fn mul(self, other: Self) -> Self {
103 Self {
104 re: self.re * other.re - self.im * other.im,
105 im: self.re * other.im + self.im * other.re,
106 }
107 }
108}
109
110struct FftPlan {
111 phase: Vec<Complex>,
112 twiddles: Vec<Complex>,
113 bit_reversed: Vec<usize>,
114}
115
116impl FftPlan {
117 fn new(len: usize) -> Self {
118 let fft_len = len / 2;
119 let phase_step = -PI / len as f64;
120 Self {
121 phase: (0..fft_len)
124 .map(|i| Complex::rotation(phase_step * (i as f64 + 0.125)))
125 .collect(),
126 twiddles: (0..fft_len / 2)
127 .map(|i| Complex::rotation(-2.0 * PI * i as f64 / fft_len as f64))
128 .collect(),
129 bit_reversed: (0..fft_len)
130 .map(|i| {
131 if fft_len == 1 {
132 0
133 } else {
134 i.reverse_bits() >> (usize::BITS - fft_len.trailing_zeros())
135 }
136 })
137 .collect(),
138 }
139 }
140
141 fn imdct_half(&self, dst: &mut [f32], src: &[f32], scale: f64) {
142 let fft_len = self.phase.len();
143 let len = 2 * fft_len;
144 let mut work = vec![Complex::default(); fft_len];
145
146 for j in 0..fft_len {
154 work[self.bit_reversed[j]] = Complex {
155 re: src[2 * j] as f64,
156 im: src[len - 1 - 2 * j] as f64,
157 }
158 .mul(self.phase[j]);
159 }
160
161 let mut width = 2;
162 while width <= fft_len {
163 let half = width / 2;
164 let stride = fft_len / width;
165 for block in work.chunks_exact_mut(width) {
166 for j in 0..half {
167 let even = block[j];
168 let odd = block[j + half].mul(self.twiddles[j * stride]);
169 block[j] = Complex {
170 re: even.re + odd.re,
171 im: even.im + odd.im,
172 };
173 block[j + half] = Complex {
174 re: even.re - odd.re,
175 im: even.im - odd.im,
176 };
177 }
178 }
179 width *= 2;
180 }
181
182 for k in 0..fft_len {
183 let value = work[k].mul(self.phase[k]);
184 dst[len - 1 - 2 * k] = (value.re * scale) as f32;
185 dst[2 * k] = (-value.im * scale) as f32;
186 }
187 }
188}
189
190#[cfg(test)]
191mod tests {
192 use super::MdctNaive;
193
194 #[test]
195 fn packed_imdct_preserves_each_coefficient_and_scale() {
196 for len in [2, 4, 8, 16, 32] {
199 for coefficient in 0..len {
200 let mut input = vec![0.0; len];
201 input[coefficient] = 1.0;
202 for scale in [1.0, -0.25, 0.0] {
203 let transform = MdctNaive::new(len, scale);
204 let mut expected = vec![0.0; len];
205 transform.imdct_half_naive(&mut expected, &input);
206 let mut actual = vec![123.0; len + 3];
207 transform.imdct_half(&mut actual, &input);
208 for i in 0..len {
209 assert!(
210 (actual[i] - expected[i]).abs() < 1e-7,
211 "N={len} coefficient={coefficient} scale={scale} i={i}"
212 );
213 }
214 assert_eq!(&actual[len..], &[123.0; 3]);
215 }
216 }
217 }
218 }
219
220 #[test]
221 fn fft_matches_reference_half_and_full_imdct() {
222 for bits in 1..=13 {
224 let len = 1 << bits;
225 let mut seed = 1234567u32;
226 let noise: Vec<f32> = (0..len)
227 .map(|_| {
228 seed = seed.wrapping_mul(1664525).wrapping_add(1013904223);
229 (seed as i32) as f32 / i32::MAX as f32
230 })
231 .collect();
232 let mut impulse = vec![0.0; len];
233 impulse[len / 3] = 1.0;
234 for input in [noise, impulse, vec![0.0; len]] {
235 for scale in [1.0, 1.0 / 32768.0] {
236 let transform = MdctNaive::new(len, scale);
237 let mut expected = vec![0.0; len];
238 transform.imdct_half_naive(&mut expected, &input);
239 let mut half = vec![0.0; len];
240 transform.imdct_half(&mut half, &input);
241 let mut full = vec![0.0; 2 * len];
242 transform.imdct_full(&mut full, &input);
243 for (i, (&actual, &reference)) in half.iter().zip(&expected).enumerate() {
244 let tolerance = (2e-6 * reference.abs()).max(2e-7 * scale as f32);
245 assert!(
246 (actual - reference).abs() <= tolerance,
247 "N={len} i={i} scale={scale}: {actual} != {reference}"
248 );
249 assert_eq!(full[len / 2 + i], actual);
250 }
251 for i in 0..len / 2 {
252 assert_eq!(full[i], -half[len / 2 - i - 1]);
253 assert_eq!(full[2 * len - i - 1], half[len / 2 + i]);
254 }
255 }
256 }
257 }
258 }
259}