/src/symphonia/symphonia-bundle-mp3/src/layer3/hybrid_synthesis.rs
Line | Count | Source |
1 | | // Symphonia |
2 | | // Copyright (c) 2019-2026 The Project Symphonia Developers. |
3 | | // |
4 | | // This Source Code Form is subject to the terms of the Mozilla Public |
5 | | // License, v. 2.0. If a copy of the MPL was not distributed with this |
6 | | // file, You can obtain one at https://mozilla.org/MPL/2.0/. |
7 | | |
8 | | // Justification: Some loops are better expressed without a range loop. |
9 | | #![allow(clippy::needless_range_loop)] |
10 | | |
11 | | use crate::common::FrameHeader; |
12 | | |
13 | | use super::{GranuleChannel, common::*}; |
14 | | |
15 | | use std::{convert::TryInto, f64}; |
16 | | |
17 | | use lazy_static::lazy_static; |
18 | | |
19 | | lazy_static! { |
20 | | /// Hybrid synthesesis IMDCT window coefficients for: Long, Start, Short, and End block, in that |
21 | | /// order. |
22 | | /// |
23 | | /// For long blocks: |
24 | | /// |
25 | | /// ```text |
26 | | /// W[ 0..36] = sin(PI/36.0 * (i + 0.5)) |
27 | | /// ``` |
28 | | /// |
29 | | /// For start blocks: |
30 | | /// |
31 | | /// ```text |
32 | | /// W[ 0..18] = sin(PI/36.0 * (i + 0.5)) |
33 | | /// W[18..24] = 1.0 |
34 | | /// W[24..30] = sin(PI/12.0 * ((i - 18) - 0.5)) |
35 | | /// W[30..36] = 0.0 |
36 | | /// ``` |
37 | | /// |
38 | | /// For short blocks (to be applied to each 12 sample window): |
39 | | /// |
40 | | /// ```text |
41 | | /// W[ 0..12] = sin(PI/12.0 * (i + 0.5)) |
42 | | /// W[12..36] = 0.0 |
43 | | /// ``` |
44 | | /// |
45 | | /// For end blocks: |
46 | | /// |
47 | | /// ```text |
48 | | /// W[ 0..6 ] = 0.0 |
49 | | /// W[ 6..12] = sin(PI/12.0 * ((i - 6) + 0.5)) |
50 | | /// W[12..18] = 1.0 |
51 | | /// W[18..36] = sin(PI/36.0 * (i + 0.5)) |
52 | | /// ``` |
53 | | static ref IMDCT_WINDOWS: [[f32; 36]; 4] = { |
54 | | const PI_36: f64 = f64::consts::PI / 36.0; |
55 | | const PI_12: f64 = f64::consts::PI / 12.0; |
56 | | |
57 | | let mut windows = [[0f32; 36]; 4]; |
58 | | |
59 | | // Window for Long blocks. |
60 | | for i in 0..36 { |
61 | | windows[0][i] = (PI_36 * (i as f64 + 0.5)).sin() as f32; |
62 | | } |
63 | | |
64 | | // Window for Start blocks (indicies 30..36 implictly 0.0). |
65 | | for i in 0..18 { |
66 | | windows[1][i] = (PI_36 * (i as f64 + 0.5)).sin() as f32; |
67 | | } |
68 | | for i in 18..24 { |
69 | | windows[1][i] = 1.0; |
70 | | } |
71 | | for i in 24..30 { |
72 | | windows[1][i] = (PI_12 * ((i - 18) as f64 + 0.5)).sin() as f32; |
73 | | } |
74 | | |
75 | | // Window for Short blocks. |
76 | | for i in 0..12 { |
77 | | windows[2][i] = (PI_12 * (i as f64 + 0.5)).sin() as f32; |
78 | | } |
79 | | |
80 | | // Window for End blocks (indicies 0..6 implicitly 0.0). |
81 | | for i in 6..12 { |
82 | | windows[3][i] = (PI_12 * ((i - 6) as f64 + 0.5)).sin() as f32; |
83 | | } |
84 | | for i in 12..18 { |
85 | | windows[3][i] = 1.0; |
86 | | } |
87 | | for i in 18..36 { |
88 | | windows[3][i] = (PI_36 * (i as f64 + 0.5)).sin() as f32; |
89 | | } |
90 | | |
91 | | windows |
92 | | }; |
93 | | } |
94 | | |
95 | | lazy_static! { |
96 | | /// Lookup table of cosine coefficients for half of a 12-point IMDCT. |
97 | | /// |
98 | | /// This table is derived from the general expression: |
99 | | /// |
100 | | /// ```text |
101 | | /// cos12[i][k] = cos(PI/24.0 * (2*i + 1 + N/2) * (2*k + 1)) |
102 | | /// ``` |
103 | | /// where: |
104 | | /// `N=12`, `i=N/4..3N/4`, and `k=0..N/2`. |
105 | | static ref IMDCT_HALF_COS_12: [[f32; 6]; 6] = { |
106 | | const PI_24: f64 = f64::consts::PI / 24.0; |
107 | | |
108 | | let mut cos = [[0f32; 6]; 6]; |
109 | | |
110 | | for (i, cos_i) in cos.iter_mut().enumerate() { |
111 | | for (k, cos_ik) in cos_i.iter_mut().enumerate() { |
112 | | // Only compute the middle half of the cosine lookup table (i offset by 3). |
113 | | let n = (2 * (i + 3) + (12 / 2) + 1) * (2 * k + 1); |
114 | | *cos_ik = (PI_24 * n as f64).cos() as f32; |
115 | | } |
116 | | } |
117 | | |
118 | | cos |
119 | | }; |
120 | | } |
121 | | |
122 | | lazy_static! { |
123 | | /// Pair of lookup tables, CS and CA, for alias reduction. |
124 | | /// |
125 | | /// As per ISO/IEC 11172-3, CS and CA are calculated as follows: |
126 | | /// |
127 | | /// ```text |
128 | | /// cs[i] = 1.0 / sqrt(1.0 + c[i]^2) |
129 | | /// ca[i] = c[i] / sqrt(1.0 + c[i]^2) |
130 | | /// ``` |
131 | | /// |
132 | | /// where: |
133 | | /// ```text |
134 | | /// c[i] = [ -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 ] |
135 | | /// ``` |
136 | | static ref ANTIALIAS_CS_CA: ([f32; 8], [f32; 8]) = { |
137 | | const C: [f64; 8] = [ -0.6, -0.535, -0.33, -0.185, -0.095, -0.041, -0.0142, -0.0037 ]; |
138 | | |
139 | | let mut cs = [0f32; 8]; |
140 | | let mut ca = [0f32; 8]; |
141 | | |
142 | | for i in 0..8 { |
143 | | let sqrt = f64::sqrt(1.0 + (C[i] * C[i])); |
144 | | cs[i] = (1.0 / sqrt) as f32; |
145 | | ca[i] = (C[i] / sqrt) as f32; |
146 | | } |
147 | | |
148 | | (cs, ca) |
149 | | }; |
150 | | } |
151 | | |
152 | | /// Reorder samples that are part of short blocks into sub-band order. |
153 | 43.2k | pub(super) fn reorder(header: &FrameHeader, channel: &mut GranuleChannel, buf: &mut [f32; 576]) { |
154 | | // Only short blocks are reordered. |
155 | 43.2k | if let BlockType::Short { is_mixed } = channel.block_type { |
156 | | // Every short block is split into 3 equally sized windows as illustrated below (e.g. for |
157 | | // a short scale factor band with win_len=4): |
158 | | // |
159 | | // <- Window #1 -> <- Window #2 -> <- Window #3 -> |
160 | | // [ 0 | 1 | 2 | 3 ][ 4 | 5 | 6 | 7 ][ 8 | 9 | a | b ] |
161 | | // <----- 3 * Short Scale Factor Band Width -----> |
162 | | // |
163 | | // Reordering interleaves the samples of each window as follows: |
164 | | // |
165 | | // [ 0 | 4 | 8 | 1 | 5 | 9 | 2 | 6 | a | 3 | 7 | b ] |
166 | | // <---- 3 * Short Scale Factor Band Width ----> |
167 | | // |
168 | | // Basically, reordering interleaves the 3 windows the same way that 3 planar audio buffers |
169 | | // would be interleaved. |
170 | 23.3k | debug_assert!(channel.rzero <= 576); |
171 | | |
172 | | // In mixed blocks, only the short bands can be re-ordered. Determine the applicable bands. |
173 | 23.3k | let bands = if is_mixed { |
174 | 7.49k | let switch = SFB_MIXED_SWITCH_POINT[header.sample_rate_idx]; |
175 | 7.49k | &SFB_MIXED_BANDS[header.sample_rate_idx][switch..] |
176 | | } |
177 | | else { |
178 | 15.8k | &SFB_SHORT_BANDS[header.sample_rate_idx] |
179 | | }; |
180 | | |
181 | 23.3k | let mut reorder_buf = [0f32; 576]; |
182 | | |
183 | 23.3k | let start = bands[0]; |
184 | 23.3k | let mut i = start; |
185 | | |
186 | 133k | for (((s0, s1), s2), s3) in |
187 | 23.3k | bands.iter().zip(&bands[1..]).zip(&bands[2..]).zip(&bands[3..]).step_by(3) |
188 | | { |
189 | | // Do not reorder short blocks that begin after the rzero partition boundary since |
190 | | // they're zeroed. |
191 | 133k | if *s0 >= channel.rzero { |
192 | 20.0k | break; |
193 | 113k | } |
194 | | |
195 | | // The three short sample windows. |
196 | 113k | let win0 = &buf[*s0..*s1]; |
197 | 113k | let win1 = &buf[*s1..*s2]; |
198 | 113k | let win2 = &buf[*s2..*s3]; |
199 | | |
200 | | // Interleave the three short sample windows. |
201 | 1.45M | for ((w0, w1), w2) in win0.iter().zip(win1).zip(win2) { |
202 | 1.45M | reorder_buf[i + 0] = *w0; |
203 | 1.45M | reorder_buf[i + 1] = *w1; |
204 | 1.45M | reorder_buf[i + 2] = *w2; |
205 | 1.45M | i += 3; |
206 | 1.45M | } |
207 | | } |
208 | | |
209 | | // Copy reordered samples from the reorder buffer to the actual sample buffer. |
210 | 23.3k | buf[start..i].copy_from_slice(&reorder_buf[start..i]); |
211 | | |
212 | | // After reordering, the start of the rzero partition may no longer be valid. Update it. |
213 | 23.3k | channel.rzero = channel.rzero.max(i); |
214 | 19.8k | } |
215 | 43.2k | } |
216 | | |
217 | | /// Applies the anti-aliasing filter to sub-bands that are not part of short blocks. |
218 | 43.2k | pub(super) fn antialias(channel: &mut GranuleChannel, samples: &mut [f32; 576]) { |
219 | | // The maximum number of sub-bands to anti-alias depends on block type. |
220 | 43.2k | let sb_limit = match channel.block_type { |
221 | | // Short blocks are never anti-aliased. |
222 | 15.8k | BlockType::Short { is_mixed: false } => return, |
223 | | // Mixed blocks have a long block span the first 36 samples (2 sub-bands). Therefore, only |
224 | | // anti-alias these two sub-bands. |
225 | 7.49k | BlockType::Short { is_mixed: true } => 2, |
226 | | // All other block types require all 32 sub-bands to be anti-aliased. |
227 | 19.8k | _ => 32, |
228 | | }; |
229 | | |
230 | | // Amortize the lazy_static fetch over the entire anti-aliasing operation. |
231 | 27.3k | let (cs, ca): &([f32; 8], [f32; 8]) = &ANTIALIAS_CS_CA; |
232 | | |
233 | | // The sub-band that intersects the start of the rzero partition. All sub-bands after this one |
234 | | // are zeroed and do-not need anti-aliasing. |
235 | 27.3k | let sb_rzero = channel.rzero / 18; |
236 | | |
237 | | // The anti-aliasing filter must be applied up-to the last non-zero sub-band. After |
238 | | // anti-aliasing, the first zeroed sub-band may have non-zero values "smeared" into it. |
239 | | // Therefore, the rzero must be updated. |
240 | 27.3k | channel.rzero = 18 * sb_limit.min(sb_rzero + 2).min(32); |
241 | | |
242 | | // Anti-aliasing is performed using 8 butterfly calculations at the boundaries of ADJACENT |
243 | | // sub-bands. For each calculation, there are two samples: lower and upper. For each iteration, |
244 | | // the lower sample index advances backwards from the boundary, while the upper sample index |
245 | | // advances forward from the boundary. |
246 | | // |
247 | | // For example, let B(li, ui) represent the butterfly calculation where li and ui are the |
248 | | // indicies of the lower and upper samples respectively. If j is the index of the first sample |
249 | | // of a sub-band, then the iterations are as follows: |
250 | | // |
251 | | // B(j-1,j), B(j-2,j+1), B(j-3,j+2), B(j-4,j+3), B(j-5,j+4), B(j-6,j+5), B(j-7,j+6), B(j-8,j+7) |
252 | | // |
253 | | // The butterfly calculation itself can be illustrated as follows: |
254 | | // |
255 | | // * cs[i] |
256 | | // l0 -------o------(-)------> l1 |
257 | | // \ / l1 = l0 * cs[i] - u0 * ca[i] |
258 | | // \ / * ca[i] u1 = u0 * cs[i] + l0 * ca[i] |
259 | | // \ |
260 | | // / \ * ca[i] where: |
261 | | // / \ cs[i], ca[i] are constant values for iteration i, |
262 | | // u0 ------o------(+)-------> u1 derived from table B.9 of ISO/IEC 11172-3. |
263 | | // * cs[i] |
264 | | // |
265 | | // Note that all butterfly calculations only involve two samples, and all iterations are |
266 | | // independant of each other. This lends itself well for SIMD processing. |
267 | 253k | for sb in (18..channel.rzero).step_by(18) { |
268 | 2.27M | for i in 0..8 { |
269 | 2.02M | let li = sb - 1 - i; |
270 | 2.02M | let ui = sb + i; |
271 | 2.02M | let lower = samples[li]; |
272 | 2.02M | let upper = samples[ui]; |
273 | 2.02M | samples[li] = lower * cs[i] - upper * ca[i]; |
274 | 2.02M | samples[ui] = upper * cs[i] + lower * ca[i]; |
275 | 2.02M | } |
276 | | } |
277 | 43.2k | } |
278 | | |
279 | | /// Performs hybrid synthesis (IMDCT and windowing). |
280 | 43.2k | pub(super) fn hybrid_synthesis( |
281 | 43.2k | channel: &GranuleChannel, |
282 | 43.2k | overlap: &mut [[f32; 18]; 32], |
283 | 43.2k | samples: &mut [f32; 576], |
284 | 43.2k | ) { |
285 | | // The first sub-band after the rzero partition boundary is the sub-band limit. All sub-bands |
286 | | // past this are zeroed. |
287 | 43.2k | let sb_limit = channel.rzero.div_ceil(18); |
288 | | |
289 | | // Determine the split point of long and short blocks in terms of a sub-band index. |
290 | | // |
291 | | // Short blocks process 0 sub-bands as long blocks, mixed blocks process the first 2 sub-bands |
292 | | // as long blocks, and all other block types (long, start, end) process all 32 sub-bands as long |
293 | | // blocks. |
294 | 43.2k | let sb_split = match channel.block_type { |
295 | 15.8k | BlockType::Short { is_mixed: false } => 0, |
296 | 7.49k | BlockType::Short { is_mixed: true } => 2, |
297 | 19.8k | _ => 32, |
298 | | }; |
299 | | |
300 | | // If the split point is not 0, then some sub-bands need to be processed as long blocks using |
301 | | // the 36-point IMDCT. |
302 | 43.2k | if sb_split > 0 { |
303 | | // Select the appropriate window given the block type. |
304 | 27.3k | let window: &[f32; 36] = match channel.block_type { |
305 | 70 | BlockType::Start => &IMDCT_WINDOWS[1], |
306 | 11.4k | BlockType::End => &IMDCT_WINDOWS[3], |
307 | 15.9k | _ => &IMDCT_WINDOWS[0], |
308 | | }; |
309 | | |
310 | 27.3k | let sb_long_end = sb_split.min(sb_limit); |
311 | | |
312 | | // For each of the sub-bands (18 samples each) in the long block... |
313 | 280k | for sb in 0..sb_long_end { |
314 | 280k | let start = 18 * sb; |
315 | 280k | |
316 | 280k | // Casting to a slice of a known-size lets the compiler elide bounds checks. |
317 | 280k | let sub_band: &mut [f32; 18] = (&mut samples[start..(start + 18)]) |
318 | 280k | .try_into() |
319 | 280k | .expect("slice is exactly 18 elements"); |
320 | 280k | |
321 | 280k | // Perform the 36-point on the entire sub-band. |
322 | 280k | imdct36::imdct36(sub_band, window, &mut overlap[sb]); |
323 | 280k | } |
324 | 15.8k | } |
325 | | |
326 | | // If the split point is less-than 32, then some sub-bands need to be processed as short blocks |
327 | | // using the 12-point IMDCT on each of the three windows. |
328 | 43.2k | if sb_split < 32 { |
329 | | // Select the short block window. |
330 | 23.3k | let window: &[f32; 36] = &IMDCT_WINDOWS[2]; |
331 | | |
332 | 23.3k | let sb_short_begin = sb_split.min(sb_limit); |
333 | | |
334 | | // For each of the sub-bands (18 samples each) in the short block... |
335 | 143k | for sb in sb_short_begin..sb_limit { |
336 | 143k | let start = 18 * sb; |
337 | 143k | |
338 | 143k | // Casting to a slice of a known-size lets the compiler elide bounds checks. |
339 | 143k | let sub_band: &mut [f32; 18] = (&mut samples[start..(start + 18)]) |
340 | 143k | .try_into() |
341 | 143k | .expect("slice is exactly 18 elements"); |
342 | 143k | |
343 | 143k | // Perform the 12-point IMDCT on each of the 3 short windows within the sub-band (6 |
344 | 143k | // samples each). |
345 | 143k | imdct12_win(sub_band, window, &mut overlap[sb]); |
346 | 143k | } |
347 | 19.8k | } |
348 | | |
349 | | // Every sub-band after the the sub-band limit are zeroed, however, the overlap for that |
350 | | // sub-band may be non-zero. Therefore, copy it over. |
351 | 959k | for sb in sb_limit..32 { |
352 | 959k | let start = 18 * sb; |
353 | 959k | let sub_band: &mut [f32; 18] = |
354 | 959k | (&mut samples[start..(start + 18)]).try_into().expect("slice is exactly 18 elements"); |
355 | 959k | |
356 | 959k | sub_band.copy_from_slice(&overlap[sb]); |
357 | 959k | overlap[sb].fill(0.0); |
358 | 959k | } |
359 | 43.2k | } |
360 | | |
361 | | /// Performs the 12-point IMDCT, and windowing for each of the 3 short windows of a short block, and |
362 | | /// then overlap-adds the result. |
363 | 143k | fn imdct12_win(x: &mut [f32; 18], window: &[f32; 36], overlap: &mut [f32; 18]) { |
364 | 143k | let cos12: &[[f32; 6]; 6] = &IMDCT_HALF_COS_12; |
365 | | |
366 | 143k | let mut tmp = [0.0; 36]; |
367 | | |
368 | 574k | for w in 0..3 { |
369 | 1.72M | for i in 0..3 { |
370 | 1.29M | // Compute the 12-point IMDCT for each of the 3 short windows using a half-size IMDCT |
371 | 1.29M | // followed by post-processing. |
372 | 1.29M | // |
373 | 1.29M | // In general, the IMDCT is defined as: |
374 | 1.29M | // |
375 | 1.29M | // (N/2)-1 |
376 | 1.29M | // y[i] = SUM { x[k] * cos(PI/2N * (2i + 1 + N/2) * (2k + 1)) } |
377 | 1.29M | // k=0 |
378 | 1.29M | // |
379 | 1.29M | // For N=12, the IMDCT becomes: |
380 | 1.29M | // |
381 | 1.29M | // 5 |
382 | 1.29M | // y[i] = SUM { x[k] * cos(PI/24 * (2i + 7) * (2k + 1)) } |
383 | 1.29M | // k=0 |
384 | 1.29M | // |
385 | 1.29M | // The cosine twiddle factors are easily indexable by i and k, and are therefore |
386 | 1.29M | // pre-computed and placed into a look-up table. |
387 | 1.29M | // |
388 | 1.29M | // Further, y[3..0] = -y[3..6], and y[12..9] = y[6..9] which reduces the amount of work |
389 | 1.29M | // by half. |
390 | 1.29M | // |
391 | 1.29M | // Therefore, it is possible to split the half-size IMDCT computation into two halves. |
392 | 1.29M | // In the calculations below, yl is the left-half output of the half-size IMDCT, and yr |
393 | 1.29M | // is the right-half. |
394 | 1.29M | |
395 | 1.29M | let yl = (x[w] * cos12[i][0]) |
396 | 1.29M | + (x[3 * 1 + w] * cos12[i][1]) |
397 | 1.29M | + (x[3 * 2 + w] * cos12[i][2]) |
398 | 1.29M | + (x[3 * 3 + w] * cos12[i][3]) |
399 | 1.29M | + (x[3 * 4 + w] * cos12[i][4]) |
400 | 1.29M | + (x[3 * 5 + w] * cos12[i][5]); |
401 | 1.29M | |
402 | 1.29M | let yr = (x[w] * cos12[i + 3][0]) |
403 | 1.29M | + (x[3 * 1 + w] * cos12[i + 3][1]) |
404 | 1.29M | + (x[3 * 2 + w] * cos12[i + 3][2]) |
405 | 1.29M | + (x[3 * 3 + w] * cos12[i + 3][3]) |
406 | 1.29M | + (x[3 * 4 + w] * cos12[i + 3][4]) |
407 | 1.29M | + (x[3 * 5 + w] * cos12[i + 3][5]); |
408 | 1.29M | |
409 | 1.29M | // Each adjacent 12-point IMDCT windows are overlapped and added in the output, with the |
410 | 1.29M | // first and last 6 samples of the output always being 0. |
411 | 1.29M | // |
412 | 1.29M | // Each sample from the 12-point IMDCT is multiplied by the appropriate window function |
413 | 1.29M | // as specified in ISO/IEC 11172-3. The values of the window function are pre-computed |
414 | 1.29M | // and given by window[0..12]. |
415 | 1.29M | // |
416 | 1.29M | // Since there are 3 IMDCT windows (indexed by w), y[0..12] is computed 3 times. |
417 | 1.29M | // For the purpose of the diagram below, we label these IMDCT windows as: y0[0..12], |
418 | 1.29M | // y1[0..12], and y2[0..12], for IMDCT windows 0..3 respectively. |
419 | 1.29M | // |
420 | 1.29M | // Therefore, the overlap-and-add operation can be visualized as below: |
421 | 1.29M | // |
422 | 1.29M | // 0 6 12 18 24 30 36 |
423 | 1.29M | // +-------------+------------+------------+------------+------------+-------------+ |
424 | 1.29M | // | 0 | y0[..6] | y0[..6] | y1[6..] | y2[6..] | 0 | |
425 | 1.29M | // | (6) | | + y1[6..] | + y2[..6] | | (6) | |
426 | 1.29M | // +-------------+------------+------------+------------+------------+-------------+ |
427 | 1.29M | // . . . . . . . |
428 | 1.29M | // . +-------------------------+ . . . |
429 | 1.29M | // . | IMDCT #1 (y0) | . . . |
430 | 1.29M | // . +-------------------------+ . . . |
431 | 1.29M | // . . +-------------------------+ . . |
432 | 1.29M | // . . | IMDCT #2 (y1) | . . |
433 | 1.29M | // . . +-------------------------+ . . |
434 | 1.29M | // . . . +-------------------------+ . |
435 | 1.29M | // . . . | IMDCT #3 (y2) | . |
436 | 1.29M | // . . . +-------------------------+ . |
437 | 1.29M | // . . . . . . . |
438 | 1.29M | // |
439 | 1.29M | // Since the 12-point IMDCT was decomposed into a half-size IMDCT and post-processing |
440 | 1.29M | // operations, and further split into left and right halves, each iteration of this loop |
441 | 1.29M | // produces 4 output samples. |
442 | 1.29M | |
443 | 1.29M | tmp[6 + 6 * w + 3 - i - 1] += -yl * window[3 - i - 1]; |
444 | 1.29M | tmp[6 + 6 * w + i + 3] += yl * window[i + 3]; |
445 | 1.29M | tmp[6 + 6 * w + i + 6] += yr * window[i + 6]; |
446 | 1.29M | tmp[6 + 6 * w + 12 - i - 1] += yr * window[12 - i - 1]; |
447 | 1.29M | } |
448 | | } |
449 | | |
450 | | // Overlap-add. |
451 | 2.72M | for i in 0..18 { |
452 | 2.58M | x[i] = tmp[i] + overlap[i]; |
453 | 2.58M | overlap[i] = tmp[i + 18]; |
454 | 2.58M | } |
455 | 143k | } |
456 | | |
457 | | /// Inverts odd samples in odd sub-bands. |
458 | 43.2k | pub fn frequency_inversion(samples: &mut [f32; 576]) { |
459 | | // There are 32 sub-bands spanning 576 samples: |
460 | | // |
461 | | // 0 18 36 54 72 90 108 558 576 |
462 | | // +-----+-----+-----+-----+-----+-----+ . . . . +------+ |
463 | | // s[i] = | sb0 | sb1 | sb2 | sb3 | sb4 | sb5 | . . . . | sb31 | |
464 | | // +-----+-----+-----+-----+-----+-----+ . . . . +------+ |
465 | | // |
466 | | // The odd sub-bands are thusly: |
467 | | // |
468 | | // sb1 -> s[ 18.. 36] |
469 | | // sb3 -> s[ 54.. 72] |
470 | | // sb5 -> s[ 90..108] |
471 | | // ... |
472 | | // sb31 -> s[558..576] |
473 | | // |
474 | | // Each odd sample in the aforementioned sub-bands must be negated. |
475 | 691k | for i in (18..576).step_by(36) { |
476 | | // Sample negation is unrolled into a 2x4 + 1 (9) operation to improve vectorization. |
477 | 1.38M | for j in (i..i + 16).step_by(8) { |
478 | 1.38M | samples[j + 1] = -samples[j + 1]; |
479 | 1.38M | samples[j + 3] = -samples[j + 3]; |
480 | 1.38M | samples[j + 5] = -samples[j + 5]; |
481 | 1.38M | samples[j + 7] = -samples[j + 7]; |
482 | 1.38M | } |
483 | 691k | samples[i + 18 - 1] = -samples[i + 18 - 1]; |
484 | | } |
485 | 43.2k | } |
486 | | |
487 | | #[cfg(test)] |
488 | | mod tests { |
489 | | use super::IMDCT_WINDOWS; |
490 | | use super::imdct12_win; |
491 | | use std::f64; |
492 | | |
493 | | fn imdct12_analytical(x: &[f32; 6]) -> [f32; 12] { |
494 | | const PI_24: f64 = f64::consts::PI / 24.0; |
495 | | |
496 | | let mut result = [0f32; 12]; |
497 | | |
498 | | for i in 0..12 { |
499 | | let mut sum = 0.0; |
500 | | for k in 0..6 { |
501 | | sum += |
502 | | (x[k] as f64) * (PI_24 * ((2 * i + (12 / 2) + 1) * (2 * k + 1)) as f64).cos(); |
503 | | } |
504 | | result[i] = sum as f32; |
505 | | } |
506 | | |
507 | | result |
508 | | } |
509 | | |
510 | | #[test] |
511 | | fn verify_imdct12_win() { |
512 | | const TEST_VECTOR: [f32; 18] = [ |
513 | | 0.0976, 0.9321, 0.6138, 0.0857, 0.0433, 0.4855, 0.2144, 0.8488, // |
514 | | 0.6889, 0.2983, 0.1957, 0.7037, 0.0052, 0.0197, 0.3188, 0.5123, // |
515 | | 0.2994, 0.7157, |
516 | | ]; |
517 | | |
518 | | let window = &IMDCT_WINDOWS[2]; |
519 | | |
520 | | let mut actual = TEST_VECTOR; |
521 | | let mut overlap = [0.0; 18]; |
522 | | imdct12_win(&mut actual, window, &mut overlap); |
523 | | |
524 | | // The following block performs 3 analytical 12-point IMDCTs over the test vector, and then |
525 | | // windows and overlaps the results to generate the final result. |
526 | | let expected = { |
527 | | let mut expected = [0f32; 36]; |
528 | | |
529 | | let mut x0 = [0f32; 6]; |
530 | | let mut x1 = [0f32; 6]; |
531 | | let mut x2 = [0f32; 6]; |
532 | | |
533 | | for i in 0..6 { |
534 | | x0[i] = TEST_VECTOR[3 * i + 0]; |
535 | | x1[i] = TEST_VECTOR[3 * i + 1]; |
536 | | x2[i] = TEST_VECTOR[3 * i + 2]; |
537 | | } |
538 | | |
539 | | let imdct0 = imdct12_analytical(&x0); |
540 | | let imdct1 = imdct12_analytical(&x1); |
541 | | let imdct2 = imdct12_analytical(&x2); |
542 | | |
543 | | for i in 0..12 { |
544 | | expected[6 + i] += imdct0[i] * window[i]; |
545 | | expected[12 + i] += imdct1[i] * window[i]; |
546 | | expected[18 + i] += imdct2[i] * window[i]; |
547 | | } |
548 | | |
549 | | expected |
550 | | }; |
551 | | |
552 | | for i in 0..18 { |
553 | | assert!((expected[i] - actual[i]).abs() < 0.00001); |
554 | | assert!((expected[i + 18] - overlap[i]).abs() < 0.00001); |
555 | | } |
556 | | } |
557 | | } |
558 | | |
559 | | mod imdct36 { |
560 | | /// Performs an Inverse Modified Discrete Cosine Transform (IMDCT) transforming 18 |
561 | | /// frequency-domain input samples, into 36 time-domain output samples. |
562 | | /// |
563 | | /// This is a straight-forward implementation of the IMDCT using Szu-Wei Lee's algorithm |
564 | | /// published in article [1]. |
565 | | /// |
566 | | /// [1] Szu-Wei Lee, "Improved algorithm for efficient computation of the forward and backward |
567 | | /// MDCT in MPEG audio coder", IEEE Transactions on Circuits and Systems II: Analog and Digital |
568 | | /// Signal Processing, vol. 48, no. 10, pp. 990-994, 2001. |
569 | | /// |
570 | | /// https://ieeexplore.ieee.org/document/974789 |
571 | 280k | pub fn imdct36(x: &mut [f32; 18], window: &[f32; 36], overlap: &mut [f32; 18]) { |
572 | 280k | let mut dct = [0f32; 18]; |
573 | | |
574 | 280k | dct_iv(x, &mut dct); |
575 | | |
576 | | // Mapping of DCT-IV to IMDCT |
577 | | // |
578 | | // 0 9 27 36 |
579 | | // +------------+------------------------+------------+ |
580 | | // | dct[9..18] | -dct[0..18].rev() | -dct[0..9] | |
581 | | // +------------+------------------------+------------+ |
582 | | // |
583 | | // where dct[] is the DCT-IV of x. |
584 | | |
585 | | // First 9 IMDCT values are values 9..18 in the DCT-IV. |
586 | 2.80M | for i in 0..9 { |
587 | 2.52M | x[i] = overlap[i] + dct[9 + i] * window[i]; |
588 | 2.52M | } |
589 | | |
590 | | // Next 18 IMDCT values are negated and /reversed/ values 0..18 in the DCT-IV. |
591 | 2.80M | for i in 9..18 { |
592 | 2.52M | x[i] = overlap[i] - dct[27 - i - 1] * window[i]; |
593 | 2.52M | } |
594 | | |
595 | 2.80M | for i in 18..27 { |
596 | 2.52M | overlap[i - 18] = -dct[27 - i - 1] * window[i]; |
597 | 2.52M | } |
598 | | |
599 | | // Last 9 IMDCT values are negated values 0..9 in the DCT-IV. |
600 | 2.80M | for i in 27..36 { |
601 | 2.52M | overlap[i - 18] = -dct[i - 27] * window[i]; |
602 | 2.52M | } |
603 | 280k | } |
604 | | |
605 | | /// Continutation of `imdct36`. |
606 | | /// |
607 | | /// Step 2: Mapping N/2-point DCT-IV to N/2-point SDCT-II. |
608 | 280k | fn dct_iv(x: &[f32; 18], y: &mut [f32; 18]) { |
609 | | // Scale factors for input samples. Computed from (16). |
610 | | // 2 * cos(PI * (2*m + 1) / (2*36) |
611 | | const SCALE: [f32; 18] = [ |
612 | | 1.998_096_443_163_715_6, // m=0 |
613 | | 1.982_889_722_747_620_8, // m=1 |
614 | | 1.952_592_014_239_866_7, // m=2 |
615 | | 1.907_433_901_496_453_9, // m=3 |
616 | | 1.847_759_065_022_573_5, // m=4 |
617 | | 1.774_021_666_356_443_4, // m=5 |
618 | | 1.686_782_891_625_771_4, // m=6 |
619 | | 1.586_706_680_582_470_6, // m=7 |
620 | | 1.474_554_673_620_247_9, // m=8 |
621 | | 1.351_180_415_231_320_7, // m=9 |
622 | | 1.217_522_858_017_441_3, // m=10 |
623 | | 1.074_599_216_693_647_8, // m=11 |
624 | | 0.923_497_226_470_067_7, // m=12 |
625 | | 0.765_366_864_730_179_7, // m=13 |
626 | | 0.601_411_599_008_546_1, // m=14 |
627 | | 0.432_879_227_876_205_8, // m=15 |
628 | | 0.261_052_384_440_103_0, // m=16 |
629 | | 0.087_238_774_730_672_0, // m=17 |
630 | | ]; |
631 | | |
632 | 280k | let samples = [ |
633 | 280k | SCALE[0] * x[0], |
634 | 280k | SCALE[1] * x[1], |
635 | 280k | SCALE[2] * x[2], |
636 | 280k | SCALE[3] * x[3], |
637 | 280k | SCALE[4] * x[4], |
638 | 280k | SCALE[5] * x[5], |
639 | 280k | SCALE[6] * x[6], |
640 | 280k | SCALE[7] * x[7], |
641 | 280k | SCALE[8] * x[8], |
642 | 280k | SCALE[9] * x[9], |
643 | 280k | SCALE[10] * x[10], |
644 | 280k | SCALE[11] * x[11], |
645 | 280k | SCALE[12] * x[12], |
646 | 280k | SCALE[13] * x[13], |
647 | 280k | SCALE[14] * x[14], |
648 | 280k | SCALE[15] * x[15], |
649 | 280k | SCALE[16] * x[16], |
650 | 280k | SCALE[17] * x[17], |
651 | 280k | ]; |
652 | | |
653 | 280k | sdct_ii_18(&samples, y); |
654 | | |
655 | 280k | y[0] /= 2.0; |
656 | 4.76M | for i in 1..17 { |
657 | 4.48M | y[i] = (y[i] / 2.0) - y[i - 1]; |
658 | 4.48M | } |
659 | 280k | y[17] = (y[17] / 2.0) - y[16]; |
660 | 280k | } |
661 | | |
662 | | /// Continutation of `imdct36`. |
663 | | /// |
664 | | /// Step 3: Decompose N/2-point SDCT-II into two N/4-point SDCT-IIs. |
665 | 280k | fn sdct_ii_18(x: &[f32; 18], y: &mut [f32; 18]) { |
666 | | // Scale factors for odd input samples. Computed from (23). |
667 | | // 2 * cos(PI * (2*m + 1) / 36) |
668 | | const SCALE: [f32; 9] = [ |
669 | | 1.992_389_396_183_491_1, // m=0 |
670 | | 1.931_851_652_578_136_6, // m=1 |
671 | | 1.812_615_574_073_299_9, // m=2 |
672 | | 1.638_304_088_577_983_6, // m=3 |
673 | | std::f32::consts::SQRT_2, // m=4 |
674 | | 1.147_152_872_702_092_3, // m=5 |
675 | | 0.845_236_523_481_398_9, // m=6 |
676 | | 0.517_638_090_205_041_9, // m=7 |
677 | | 0.174_311_485_495_316_3, // m=8 |
678 | | ]; |
679 | | |
680 | 280k | let even = [ |
681 | 280k | x[0] + x[18 - 1], |
682 | 280k | x[1] + x[18 - 2], |
683 | 280k | x[2] + x[18 - 3], |
684 | 280k | x[3] + x[18 - 4], |
685 | 280k | x[4] + x[18 - 5], |
686 | 280k | x[5] + x[18 - 6], |
687 | 280k | x[6] + x[18 - 7], |
688 | 280k | x[7] + x[18 - 8], |
689 | 280k | x[8] + x[18 - 9], |
690 | 280k | ]; |
691 | | |
692 | 280k | sdct_ii_9(&even, y); |
693 | | |
694 | 280k | let odd = [ |
695 | 280k | SCALE[0] * (x[0] - x[18 - 1]), |
696 | 280k | SCALE[1] * (x[1] - x[18 - 2]), |
697 | 280k | SCALE[2] * (x[2] - x[18 - 3]), |
698 | 280k | SCALE[3] * (x[3] - x[18 - 4]), |
699 | 280k | SCALE[4] * (x[4] - x[18 - 5]), |
700 | 280k | SCALE[5] * (x[5] - x[18 - 6]), |
701 | 280k | SCALE[6] * (x[6] - x[18 - 7]), |
702 | 280k | SCALE[7] * (x[7] - x[18 - 8]), |
703 | 280k | SCALE[8] * (x[8] - x[18 - 9]), |
704 | 280k | ]; |
705 | | |
706 | 280k | sdct_ii_9(&odd, &mut y[1..]); |
707 | | |
708 | 280k | y[3] -= y[3 - 2]; |
709 | 280k | y[5] -= y[5 - 2]; |
710 | 280k | y[7] -= y[7 - 2]; |
711 | 280k | y[9] -= y[9 - 2]; |
712 | 280k | y[11] -= y[11 - 2]; |
713 | 280k | y[13] -= y[13 - 2]; |
714 | 280k | y[15] -= y[15 - 2]; |
715 | 280k | y[17] -= y[17 - 2]; |
716 | 280k | } |
717 | | |
718 | | /// Continutation of `imdct36`. |
719 | | /// |
720 | | /// Step 4: Computation of 9-point (N/4) SDCT-II. |
721 | 560k | fn sdct_ii_9(x: &[f32; 9], y: &mut [f32]) { |
722 | | const D: [f32; 7] = [ |
723 | | -1.732_050_807_568_877_2, // -sqrt(3.0) |
724 | | 1.879_385_241_571_816_6, // -2.0 * cos(8.0 * PI / 9.0) |
725 | | -0.347_296_355_333_860_8, // -2.0 * cos(4.0 * PI / 9.0) |
726 | | -1.532_088_886_237_956_0, // -2.0 * cos(2.0 * PI / 9.0) |
727 | | -0.684_040_286_651_337_8, // -2.0 * sin(8.0 * PI / 9.0) |
728 | | -1.969_615_506_024_416_0, // -2.0 * sin(4.0 * PI / 9.0) |
729 | | -1.285_575_219_373_078_5, // -2.0 * sin(2.0 * PI / 9.0) |
730 | | ]; |
731 | | |
732 | 560k | let a01 = x[3] + x[5]; |
733 | 560k | let a02 = x[3] - x[5]; |
734 | 560k | let a03 = x[6] + x[2]; |
735 | 560k | let a04 = x[6] - x[2]; |
736 | 560k | let a05 = x[1] + x[7]; |
737 | 560k | let a06 = x[1] - x[7]; |
738 | 560k | let a07 = x[8] + x[0]; |
739 | 560k | let a08 = x[8] - x[0]; |
740 | | |
741 | 560k | let a09 = x[4] + a05; |
742 | 560k | let a10 = a01 + a03; |
743 | 560k | let a11 = a10 + a07; |
744 | 560k | let a12 = a03 - a07; |
745 | 560k | let a13 = a01 - a07; |
746 | 560k | let a14 = a01 - a03; |
747 | 560k | let a15 = a02 - a04; |
748 | 560k | let a16 = a15 + a08; |
749 | 560k | let a17 = a04 + a08; |
750 | 560k | let a18 = a02 - a08; |
751 | 560k | let a19 = a02 + a04; |
752 | 560k | let a20 = 2.0 * x[4] - a05; |
753 | | |
754 | 560k | let m1 = D[0] * a06; |
755 | 560k | let m2 = D[1] * a12; |
756 | 560k | let m3 = D[2] * a13; |
757 | 560k | let m4 = D[3] * a14; |
758 | 560k | let m5 = D[0] * a16; |
759 | 560k | let m6 = D[4] * a17; |
760 | 560k | let m7 = D[5] * a18; // Note: the cited paper has an error, a1 should be a18. |
761 | 560k | let m8 = D[6] * a19; |
762 | | |
763 | 560k | let a21 = a20 + m2; |
764 | 560k | let a22 = a20 - m2; |
765 | 560k | let a23 = a20 + m3; |
766 | 560k | let a24 = m1 + m6; |
767 | 560k | let a25 = m1 - m6; |
768 | 560k | let a26 = m1 + m7; |
769 | | |
770 | 560k | y[0] = a09 + a11; |
771 | 560k | y[2] = m8 - a26; |
772 | 560k | y[4] = m4 - a21; |
773 | 560k | y[6] = m5; |
774 | 560k | y[8] = a22 - m3; |
775 | 560k | y[10] = a25 - m7; |
776 | 560k | y[12] = a11 - 2.0 * a09; |
777 | 560k | y[14] = a24 + m8; |
778 | 560k | y[16] = a23 + m4; |
779 | 560k | } |
780 | | |
781 | | #[cfg(test)] |
782 | | mod tests { |
783 | | use super::imdct36; |
784 | | use std::f64; |
785 | | |
786 | | fn imdct36_analytical(x: &[f32; 18]) -> [f32; 36] { |
787 | | let mut result = [0f32; 36]; |
788 | | |
789 | | const PI_72: f64 = f64::consts::PI / 72.0; |
790 | | |
791 | | for i in 0..36 { |
792 | | let mut sum = 0.0; |
793 | | for j in 0..18 { |
794 | | sum += |
795 | | (x[j] as f64) * (PI_72 * (((2 * i) + 1 + 18) * ((2 * j) + 1)) as f64).cos(); |
796 | | } |
797 | | result[i] = sum as f32; |
798 | | } |
799 | | result |
800 | | } |
801 | | |
802 | | #[test] |
803 | | fn verify_imdct36() { |
804 | | const TEST_VECTOR: [f32; 18] = [ |
805 | | 0.0976, 0.9321, 0.6138, 0.0857, 0.0433, 0.4855, 0.2144, 0.8488, // |
806 | | 0.6889, 0.2983, 0.1957, 0.7037, 0.0052, 0.0197, 0.3188, 0.5123, // |
807 | | 0.2994, 0.7157, |
808 | | ]; |
809 | | |
810 | | const WINDOW: [f32; 36] = [1.0; 36]; |
811 | | |
812 | | let mut actual = TEST_VECTOR; |
813 | | let mut overlap = [0.0; 18]; |
814 | | imdct36(&mut actual, &WINDOW, &mut overlap); |
815 | | |
816 | | let expected = imdct36_analytical(&TEST_VECTOR); |
817 | | |
818 | | for i in 0..18 { |
819 | | assert!((expected[i] - actual[i]).abs() < 0.00001); |
820 | | assert!((expected[i + 18] - overlap[i]).abs() < 0.00001); |
821 | | } |
822 | | } |
823 | | } |
824 | | } |