Coverage Report

Created: 2026-07-26 06:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}