mirror of
https://github.com/storytold/effectcraft.git
synced 2026-10-31 13:37:37 -06:00
New L0 crate effectcraft-opusenc: clean-room RFC 6716 CELT-only encoder (fullband 48 kHz, 20 ms packets, mono/stereo as dual L/R stereo, constant packet size from the bitrate) with the RFC 7845 OpusHead and pre-skip. Forward low-overlap MDCT, inter/intra coarse energy, fine/final energy, the decoder's exact allocation, recursive band splitting, PVQ search and codeword indexing, silence frames. crates/export depends on it; filmcraft-opus (dev) is the round-trip oracle in tests/opus_roundtrip.rs, with ffmpeg reading an in-test Ogg Opus file as a second oracle when installed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
123 lines
5.6 KiB
Rust
123 lines
5.6 KiB
Rust
//! Constant tables of the 48 kHz CELT mode (RFC 6716 §4.3, Tables 55–58 and §4.3.2).
|
||
//!
|
||
//! Transcribed from RFC 6716; copied from FilmCraft's spec-derived `filmcraft-opus` decoder
|
||
//! (first-party, MIT OR Apache-2.0) so both sides share the exact same values.
|
||
|
||
/// Number of energy bands.
|
||
pub const NB_EBANDS: usize = 21;
|
||
/// Band edges in units of 2.5 ms MDCT bins (Table 55).
|
||
pub const EBANDS: [i32; NB_EBANDS + 1] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 20, 24, 28, 34, 40, 48, 60, 78, 100];
|
||
/// Overlap of the low-overlap window (samples at 48 kHz).
|
||
pub const OVERLAP: usize = 120;
|
||
/// Largest LM (20 ms frames).
|
||
pub const MAX_LM: usize = 3;
|
||
/// Pre-emphasis coefficient.
|
||
pub const PREEMPH: f32 = 0.850_006_1;
|
||
|
||
/// Static allocation table (Table 57), indexed `[quality][band]`, in 1/32 bit per MDCT bin.
|
||
pub const ALLOC: [[u8; NB_EBANDS]; 11] = transpose([
|
||
[0, 90, 110, 118, 126, 134, 144, 152, 162, 172, 200],
|
||
[0, 80, 100, 110, 119, 127, 137, 145, 155, 165, 200],
|
||
[0, 75, 90, 103, 112, 120, 130, 138, 148, 158, 200],
|
||
[0, 69, 84, 93, 104, 114, 124, 132, 142, 152, 200],
|
||
[0, 63, 78, 86, 95, 103, 113, 123, 133, 143, 200],
|
||
[0, 56, 71, 80, 89, 97, 107, 117, 127, 137, 200],
|
||
[0, 49, 65, 75, 83, 91, 101, 111, 121, 131, 200],
|
||
[0, 40, 58, 70, 78, 85, 95, 105, 115, 125, 200],
|
||
[0, 34, 51, 65, 72, 78, 88, 98, 108, 118, 198],
|
||
[0, 29, 45, 59, 66, 72, 82, 92, 102, 112, 193],
|
||
[0, 20, 39, 53, 60, 66, 76, 86, 96, 106, 188],
|
||
[0, 18, 32, 47, 54, 60, 70, 80, 90, 100, 183],
|
||
[0, 10, 26, 40, 47, 54, 64, 74, 84, 94, 178],
|
||
[0, 0, 20, 31, 39, 47, 57, 67, 77, 87, 173],
|
||
[0, 0, 12, 23, 32, 41, 51, 61, 71, 81, 168],
|
||
[0, 0, 0, 15, 25, 35, 45, 55, 65, 75, 163],
|
||
[0, 0, 0, 4, 17, 29, 39, 49, 59, 69, 158],
|
||
[0, 0, 0, 0, 12, 23, 33, 43, 53, 63, 153],
|
||
[0, 0, 0, 0, 1, 16, 26, 36, 46, 56, 148],
|
||
[0, 0, 0, 0, 0, 10, 15, 20, 30, 45, 129],
|
||
[0, 0, 0, 0, 0, 1, 1, 1, 1, 20, 104],
|
||
]);
|
||
|
||
const fn transpose(t: [[u8; 11]; NB_EBANDS]) -> [[u8; NB_EBANDS]; 11] {
|
||
let mut out = [[0u8; NB_EBANDS]; 11];
|
||
let mut b = 0;
|
||
while b < NB_EBANDS {
|
||
let mut q = 0;
|
||
while q < 11 {
|
||
out[q][b] = t[b][q];
|
||
q += 1;
|
||
}
|
||
b += 1;
|
||
}
|
||
out
|
||
}
|
||
|
||
/// Mean band energy (log2 domain) removed before coarse quantisation.
|
||
pub const E_MEANS: [f32; 25] = [
|
||
6.4375, 6.25, 5.75, 5.3125, 5.0625, 4.8125, 4.5, 4.375, 4.875, 4.6875, 4.5625, 4.4375, 4.875, 4.625, 4.3125, 4.5, 4.375, 4.625, 4.75, 4.4375, 3.75, 3.75,
|
||
3.75, 3.75, 3.75,
|
||
];
|
||
|
||
/// Inter-frame prediction coefficient (alpha) per LM, Q15.
|
||
pub const PRED_COEF: [f32; 4] = [29440.0 / 32768.0, 26112.0 / 32768.0, 21248.0 / 32768.0, 16384.0 / 32768.0];
|
||
/// Inter-band prediction coefficient (beta) per LM for inter frames, Q15.
|
||
pub const BETA_COEF: [f32; 4] = [30147.0 / 32768.0, 22282.0 / 32768.0, 12124.0 / 32768.0, 6554.0 / 32768.0];
|
||
/// Beta for intra frames.
|
||
pub const BETA_INTRA: f32 = 4915.0 / 32768.0;
|
||
|
||
/// Laplace model parameters for coarse energy: `[LM][intra][2*band + {0: prob, 1: decay}]`.
|
||
pub const E_PROB_MODEL: [[[u8; 42]; 2]; 4] = [
|
||
[
|
||
[
|
||
72, 127, 65, 129, 66, 128, 65, 128, 64, 128, 62, 128, 64, 128, 64, 128, 92, 78, 92, 79, 92, 78, 90, 79, 116, 41, 115, 40, 114, 40, 132, 26, 132,
|
||
26, 145, 17, 161, 12, 176, 10, 177, 11,
|
||
],
|
||
[
|
||
24, 179, 48, 138, 54, 135, 54, 132, 53, 134, 56, 133, 55, 132, 55, 132, 61, 114, 70, 96, 74, 88, 75, 88, 87, 74, 89, 66, 91, 67, 100, 59, 108, 50,
|
||
120, 40, 122, 37, 97, 43, 78, 50,
|
||
],
|
||
],
|
||
[
|
||
[
|
||
83, 78, 84, 81, 88, 75, 86, 74, 87, 71, 90, 73, 93, 74, 93, 74, 109, 40, 114, 36, 117, 34, 117, 34, 143, 17, 145, 18, 146, 19, 162, 12, 165, 10,
|
||
178, 7, 189, 6, 190, 8, 177, 9,
|
||
],
|
||
[
|
||
23, 178, 54, 115, 63, 102, 66, 98, 69, 99, 74, 89, 71, 91, 73, 91, 78, 89, 86, 80, 92, 66, 93, 64, 102, 59, 103, 60, 104, 60, 117, 52, 123, 44,
|
||
138, 35, 133, 31, 97, 38, 77, 45,
|
||
],
|
||
],
|
||
[
|
||
[
|
||
61, 90, 93, 60, 105, 42, 107, 41, 110, 45, 116, 38, 113, 38, 112, 38, 124, 26, 132, 27, 136, 19, 140, 20, 155, 14, 159, 16, 158, 18, 170, 13, 177,
|
||
10, 187, 8, 192, 6, 175, 9, 159, 10,
|
||
],
|
||
[
|
||
21, 178, 59, 110, 71, 86, 75, 85, 84, 83, 91, 66, 88, 73, 87, 72, 92, 75, 98, 72, 105, 58, 107, 54, 115, 52, 114, 55, 112, 56, 129, 51, 132, 40,
|
||
150, 33, 140, 29, 98, 35, 77, 42,
|
||
],
|
||
],
|
||
[
|
||
[
|
||
42, 121, 96, 66, 108, 43, 111, 40, 117, 44, 123, 32, 120, 36, 119, 33, 127, 33, 134, 34, 139, 21, 147, 23, 152, 20, 158, 25, 154, 26, 166, 21, 173,
|
||
16, 184, 13, 184, 10, 150, 13, 139, 15,
|
||
],
|
||
[
|
||
22, 178, 63, 114, 74, 82, 84, 83, 92, 82, 103, 62, 96, 72, 96, 67, 101, 73, 107, 72, 113, 55, 118, 52, 125, 52, 118, 52, 117, 55, 135, 49, 137, 39,
|
||
157, 32, 145, 29, 97, 33, 77, 40,
|
||
],
|
||
],
|
||
];
|
||
|
||
/// Inverse CDF for coarse energy when only a few bits remain.
|
||
pub const SMALL_ENERGY_ICDF: [u8; 3] = [2, 1, 0];
|
||
/// Conservative log2 in 1/8 bits of the number of coded bands (intensity reservation).
|
||
pub const LOG2_FRAC_TABLE: [i32; 24] = [0, 8, 13, 16, 19, 21, 23, 24, 26, 27, 28, 29, 30, 31, 32, 32, 33, 34, 34, 35, 36, 36, 37, 37];
|
||
/// TF resolution changes: `[LM][4*transient + 2*tf_select + tf_flag]`.
|
||
pub const TF_SELECT_TABLE: [[i8; 8]; 4] = [[0, -1, 0, -1, 0, -1, 0, -1], [0, -1, 0, -2, 1, 0, 1, -1], [0, -2, 0, -3, 2, 0, 1, -1], [0, -2, 0, -3, 3, 0, 1, -1]];
|
||
/// Spreading decision `{7, 2, 21, 2}/32`.
|
||
pub const SPREAD_ICDF: [u8; 4] = [25, 23, 2, 0];
|
||
/// Allocation trim (Table 58).
|
||
pub const TRIM_ICDF: [u8; 11] = [126, 124, 119, 109, 87, 41, 19, 9, 4, 2, 0];
|