From 18c34d46b5d073a026ef65e9fa58e6179b04e757 Mon Sep 17 00:00:00 2001 From: Hawkynt Date: Sun, 6 Sep 2026 16:44:16 +0200 Subject: [PATCH] # DTS core decoded nothing from streams other encoders wrote Every libavcodec-encoded core stream came back empty. The per-channel arrays holding the sample code-book selector and the scale-factor adjustment were sized to the ten code-book groups, but the subframe header allows a bit-allocation index up to 26, so the first wide-band subband indexed past the end and the frame was abandoned. Our own encoder only ever writes small indices, which is why nothing caught it. Behind that sat three more faults, each of which the reader had been hiding: - The QMF synthesis fed the filterbank a half-IMDCT with the wrong modulation phase, so a 997 Hz tone came out as full-amplitude images at 2.0, 2.5 and 3.5 kHz. The stage is the second half of a 64-point IMDCT over the 32 subband values; the kernel now says so. - The filterbank output was left in the subband domain instead of being scaled to the +/-1 range the caller quantizes from, so every sample clipped. - The quantizer step sizes were stored as short decimals. The finest six steps are 0.14 percent away from the values the format defines, which put a level error on anything coded at high resolution; they are now the exact Q22 integers. - The LFE interpolation FIR reaches back past the first decimated sample of a frame, but the buffer was cleared each frame, restarting the filter from silence once per frame. Decoded PCM now comes out in the ITU/WAVE interleave order rather than the bit stream's centre-first AMODE order. Verified against ffmpeg n9.0.1 over 44 streams (mono through 5.1, 32/44.1/48 kHz, 384-1411 kbit/s, tones, noise, chirps, squares, transients, silence): all decode at full length, no sample differs by more than one LSB, and 99 to 100 percent of samples are identical. --- Codecs/Codec.Dts/DtsCodec.cs | 11 +- Codecs/Codec.Dts/DtsFrameDecoder.cs | 103 ++- Codecs/Codec.Dts/DtsFrameHeader.cs | 29 + Codecs/Codec.Dts/DtsQmf.cs | 26 +- Codecs/Codec.Dts/DtsTables.cs | 41 +- .../Codecs/Dts/DtsForeignStreamTests.cs | 808 ++++++++++++++++++ Hawkynt.FileFormats.Audio/REFERENCE.md | 10 +- 7 files changed, 957 insertions(+), 71 deletions(-) create mode 100644 Compression.Tests/Codecs/Dts/DtsForeignStreamTests.cs diff --git a/Codecs/Codec.Dts/DtsCodec.cs b/Codecs/Codec.Dts/DtsCodec.cs index 7087b57a4..fb3badd3d 100644 --- a/Codecs/Codec.Dts/DtsCodec.cs +++ b/Codecs/Codec.Dts/DtsCodec.cs @@ -9,11 +9,10 @@ public sealed record DtsStreamInfo( int SampleRate, int Channels, int Bitrate, int Amode, bool Lfe, long DurationSamples); /// -/// Clean-room DTS Coherent Acoustics (DCA) core codec. The decoder is a faithful managed port of -/// the FFmpeg reference decoder (libavcodec/dcadec.c + dcadata.c + -/// dcahuff.h) and the encoder lives in the companion partial source file. The core decoder -/// emits interleaved little-endian signed 16-bit PCM at the stream's native channel count (the -/// AMODE full-bandwidth channels in document order, with the LFE channel last when present). +/// Clean-room DTS Coherent Acoustics (DCA) core codec; the encoder lives in the companion partial +/// source file. The core decoder emits interleaved little-endian signed 16-bit PCM at the stream's +/// native channel count, in the ITU/WAVE interleave order — front left, front right, front centre, +/// LFE, then the surrounds — rather than the centre-first AMODE order the bit stream uses. /// /// Scope: only the standard 16-bit big-endian framing (sync 0x7FFE8001) is decoded; the 14-bit and /// byte-swapped framings throw . DTS-HD extension substreams @@ -44,7 +43,7 @@ public static DtsStreamInfo ReadStreamInfo(Stream input) { /// /// Decodes a DTS stream into raw interleaved little-endian signed 16-bit PCM on - /// . Channels are emitted in AMODE document order with LFE last. + /// . Channels are emitted in the ITU/WAVE interleave order. /// Throws for the unsupported 14-bit / LE framings. /// public static void Decompress(Stream input, Stream output) { diff --git a/Codecs/Codec.Dts/DtsFrameDecoder.cs b/Codecs/Codec.Dts/DtsFrameDecoder.cs index 339616ea7..a961ed848 100644 --- a/Codecs/Codec.Dts/DtsFrameDecoder.cs +++ b/Codecs/Codec.Dts/DtsFrameDecoder.cs @@ -5,7 +5,7 @@ namespace Codec.Dts; /// /// Decodes a single DTS Coherent Acoustics (DCA) core frame to per-channel float PCM. One instance /// is reused across the stream so the QMF synthesis memory and the ADPCM predictor history persist -/// between frames. The decode follows the DTS core bitstream / FFmpeg's dcadec.c: +/// between frames. The decode follows the DTS core bitstream: /// the primary audio coding header (subband activity, VQ start, joint intensity, the bit-allocation /// / scale-factor / transient / quantization-index code-book selections and scale-factor adjusts), /// then per subframe a set of sub-subframes carrying bit allocation, scale factors, the quantized @@ -23,6 +23,8 @@ internal sealed class DtsFrameDecoder { private const int MaxPrimChannels = 7; // DCA_PRIM_CHANNELS_MAX private const int Subbands = 32; // DCA_SUBBANDS private const int MaxSubsubframes = 4; + private const int MaxAbits = 32; // DCA_ABITS_MAX: bit-allocation index range + private const int CodeBooks = 10; // bit-allocation indices that can carry a sample code book // ── Code books (built once) ─────────────────────────────────────────────── private static readonly DtsBitAllocBook BitAllocIndex = BuildBitAllocIndex(); @@ -62,6 +64,7 @@ public DtsFrameDecoder() { private int _subframes; private int _bitRateIndex; private bool _perfectReconstruction; + private int[] _channelMap = []; private readonly int[] _subbandActivity = new int[MaxPrimChannels]; private readonly int[] _vqStartSubband = new int[MaxPrimChannels]; @@ -69,8 +72,11 @@ public DtsFrameDecoder() { private readonly int[] _transientHuffman = new int[MaxPrimChannels]; private readonly int[] _scalefactorHuffman = new int[MaxPrimChannels]; private readonly int[] _bitallocHuffman = new int[MaxPrimChannels]; - private readonly int[][] _quantIndexHuffman = NewIntMatrix(MaxPrimChannels, 11); - private readonly float[][] _scalefactorAdj = NewFloatMatrix(MaxPrimChannels, 11); + // Indexed by the bit-allocation index, which the subframe header allows up to 26 even though only + // groups 1..10 carry a transmitted code-book selector and scale-factor adjustment. Sizing these to + // the group count alone puts every wide-band subband out of bounds. + private readonly int[][] _quantIndexHuffman = NewIntMatrix(MaxPrimChannels, MaxAbits); + private readonly float[][] _scalefactorAdj = NewFloatMatrix(MaxPrimChannels, MaxAbits); // Per-subframe header state. private readonly int[][] _predictionMode = NewIntMatrix(MaxPrimChannels, Subbands); @@ -85,13 +91,15 @@ public DtsFrameDecoder() { // LFE decimated samples (history + current frame). Sized for the worst case. private readonly float[] _lfeData = new float[64 * 2 + 256]; private float _lfeScaleFactor; + private int _lfeWritten; + private int _lfeHistoryLfe = -1; // Subband samples for the whole frame: [block][chan][subband][8]. private float[][][][]? _subbandSamples; /// /// Decodes the core frame at . On success - /// is the native channel count (AMODE channels + LFE last when present) and the return value is + /// is the native channel count and the return value is, in ITU/WAVE interleave order, /// [channel][sample] float PCM (normalised to roughly ±1). Returns /// when the frame cannot be decoded. /// @@ -112,6 +120,7 @@ public DtsFrameDecoder() { // MULTIRATE_INTER selects the perfect-reconstruction prototype; it is bit 0 of the field that // immediately follows the (optional) header CRC. Re-read it from the parsed header position. this._perfectReconstruction = ReadMultirateInter(data, offset, header); + this._channelMap = DtsFrameHeader.ChannelMap(header.Amode, header.Lfe > 0); // Seek the reader to the end of the parsed frame header (which is byte-exact in bits). r.SkipBits(header.HeaderBitLength); @@ -120,12 +129,19 @@ public DtsFrameDecoder() { return null; var totalChannels = this._primChannels + (this._lfe > 0 ? 1 : 0); + // The audio coding header carries its own channel count; when it disagrees with AMODE the + // arrangement is unknown, so fall back to bit-stream order rather than shuffle blindly. + if (this._channelMap.Length != totalChannels) { + this._channelMap = new int[totalChannels]; + for (var c = 0; c < totalChannels; ++c) + this._channelMap[c] = c; + } var blocks = this._sampleBlocks / 8; if (blocks <= 0) return null; this._subbandSamples = NewSubbandSamples(blocks, this._primChannels); - Array.Clear(this._lfeData, 0, this._lfeData.Length); + this.CarryLfeHistory(blocks); var currentSubframe = 0; var currentSubsubframe = 0; @@ -163,6 +179,20 @@ public DtsFrameDecoder() { } } + // The interpolation FIR reaches back past the first decimated sample of the frame, so the tail of + // the previous frame has to stay in place; clearing the whole buffer restarts the filter from + // silence once per frame and leaves a periodic artefact on the LFE channel. + private void CarryLfeHistory(int blocks) { + var history = 8 * this._lfe; + if (this._lfeHistoryLfe == this._lfe && this._lfeWritten >= history) + Array.Copy(this._lfeData, this._lfeWritten - history, this._lfeData, 0, history); + else + Array.Clear(this._lfeData, 0, Math.Max(history, 0)); + Array.Clear(this._lfeData, history, this._lfeData.Length - history); + this._lfeWritten = history + 2 * this._lfe * blocks; + this._lfeHistoryLfe = this._lfe; + } + // The MULTIRATE_INTER flag sits right after the optional 16-bit header CRC. Re-parse minimally. private static bool ReadMultirateInter(byte[] data, int offset, DtsFrameHeader header) { var buffer = data.AsSpan(offset, Math.Min(data.Length - offset, 32)).ToArray(); @@ -196,14 +226,13 @@ private bool ParseAudioCodingHeader(DtsBitReader r) { for (var i = 0; i < this._primChannels; ++i) this._bitallocHuffman[i] = (int)r.ReadBits(3); for (var i = 0; i < this._primChannels; ++i) - this._quantIndexHuffman[i][0] = 0; + Array.Clear(this._quantIndexHuffman[i]); for (var j = 1; j < 11; ++j) for (var i = 0; i < this._primChannels; ++i) this._quantIndexHuffman[i][j] = (int)r.ReadBits(QuantIndexBitLen[j]); - for (var j = 0; j < 11; ++j) - for (var i = 0; i < this._primChannels; ++i) - this._scalefactorAdj[i][j] = 1f; + for (var i = 0; i < this._primChannels; ++i) + Array.Fill(this._scalefactorAdj[i], 1f); for (var j = 1; j < 11; ++j) for (var i = 0; i < this._primChannels; ++i) if (this._quantIndexHuffman[i][j] < QuantIndexThreshold[j]) @@ -257,7 +286,7 @@ private bool ParseSubframeHeader(DtsBitReader r, int blockIndex, int currentSubf for (var j = 0; j < this._primChannels; ++j) { uint[] scaleTable; int logSize; - if (this._scalefactorHuffman[j] == 6) { scaleTable = DtsTables.ScaleFactorQuant7; logSize = 7; } + if (this._scalefactorHuffman[j] > 5) { scaleTable = DtsTables.ScaleFactorQuant7; logSize = 7; } else { scaleTable = DtsTables.ScaleFactorQuant6; logSize = 6; } for (var k = 0; k < this._subbandActivity[j]; ++k) { @@ -355,25 +384,30 @@ private bool DecodeSubsubframe(DtsBitReader r, int blockIndex, int currentSubfra rscale[l] = 0f; Array.Clear(block, 8 * l, 8); } else { + // A code book is only in play when the transmitted selector falls inside that group's + // size; otherwise the samples are block codes (abits <= 7) or plain fixed-width values. + var book = abits <= CodeBooks ? SampleBitAlloc[abits] : null; + var huffman = book != null && sel < QuantIndexThreshold[abits] && book.Vlc[sel] != null; + var sfi = this._transitionMode[k][l] != 0 && subsubframe >= this._transitionMode[k][l] ? 1 : 0; - rscale[l] = quantStepSize * this._scaleFactor[k][l][sfi] * this._scalefactorAdj[k][sel]; - - var book = abits < SampleBitAlloc.Length ? SampleBitAlloc[abits] : null; - if (abits >= 11 || book == null || book.Vlc[sel] == null) { - if (abits <= 7) { - var size = DtsBlockCode.Sizes[abits - 1]; - var levels = DtsBlockCode.Levels[abits - 1]; - var code1 = (int)r.ReadBits(size); - var code2 = (int)r.ReadBits(size); - if (DtsBlockCode.DecodeBlockCodes(code1, code2, levels, block, 8 * l) != 0) - return false; - } else { - for (var m = 0; m < 8; ++m) - block[8 * l + m] = r.ReadSigned(abits - 3); - } + // The scale-factor adjustment belongs to the bit-allocation index, and it applies only + // when the samples were Huffman coded. + var adj = huffman ? this._scalefactorAdj[k][abits] : 1f; + rscale[l] = quantStepSize * this._scaleFactor[k][l][sfi] * adj; + + if (huffman) { + for (var m = 0; m < 8; ++m) + block[8 * l + m] = book!.Get(r, sel); + } else if (abits <= 7) { + var size = DtsBlockCode.Sizes[abits - 1]; + var levels = DtsBlockCode.Levels[abits - 1]; + var code1 = (int)r.ReadBits(size); + var code2 = (int)r.ReadBits(size); + if (DtsBlockCode.DecodeBlockCodes(code1, code2, levels, block, 8 * l) != 0) + return false; } else { for (var m = 0; m < 8; ++m) - block[8 * l + m] = book.Get(r, sel); + block[8 * l + m] = r.ReadSigned(abits - 3); } } } @@ -432,21 +466,20 @@ private bool DecodeSubsubframe(DtsBitReader r, int blockIndex, int currentSubfra // ── QMF synthesis + LFE interpolation → per-channel PCM ─────────────────── private void FilterChannels(int blocks, float[][] pcm) { - var totalChannels = this._primChannels + (this._lfe > 0 ? 1 : 0); - // Output ordering: decoded prim channels in document (AMODE) order, LFE last. The QMF scale - // 1/sqrt(2) / 32768 matches the reference; we keep ±1-normalised floats (no /32768) for WAV. - var qmfScale = (float)(1.0 / Math.Sqrt(2.0)); + // The QMF output is scaled to the +/-1 float domain the caller quantizes from; the reference + // filterbank gain is 1/sqrt(2) over a 15-bit sample range. + var qmfScale = (float)(1.0 / Math.Sqrt(2.0) / 32768.0); + var map = this._channelMap; for (var blk = 0; blk < blocks; ++blk) { var subbandSamples = this._subbandSamples![blk]; for (var k = 0; k < this._primChannels; ++k) - this._qmf[k].Process(subbandSamples[k], this._subbandActivity[k], pcm[k], blk * 256, + this._qmf[k].Process(subbandSamples[k], this._subbandActivity[k], pcm[map[k]], blk * 256, this._perfectReconstruction, qmfScale); - if (this._lfe > 0) { - var lfeChannel = totalChannels - 1; - DtsLfe.Interpolate(this._lfe, this._lfeData, 2 * this._lfe * (blk + 4), pcm[lfeChannel], blk * 256); - } + if (this._lfe > 0) + DtsLfe.Interpolate(this._lfe, this._lfeData, 2 * this._lfe * (blk + 4), + pcm[map[this._primChannels]], blk * 256); } } diff --git a/Codecs/Codec.Dts/DtsFrameHeader.cs b/Codecs/Codec.Dts/DtsFrameHeader.cs index 666887e3d..e700c6991 100644 --- a/Codecs/Codec.Dts/DtsFrameHeader.cs +++ b/Codecs/Codec.Dts/DtsFrameHeader.cs @@ -64,6 +64,35 @@ public readonly record struct DtsFrameHeader( _ => $"user-defined ({amode})", }; + /// + /// Maps a channel's position in the bit stream to its position in the decoded interleaved PCM. + /// The DTS core transmits the front channels centre-first (C, L, R) and carries the LFE channel + /// last, while interleaved PCM uses the ITU/WAVE order — front left, front right, front centre, + /// LFE, then the surrounds. The returned array is indexed by bit-stream channel (AMODE channels in + /// document order, LFE appended) and yields the interleave slot. AMODE codes above 9 describe + /// arrangements the core decoder does not reconstruct, and keep the bit-stream order. + /// + internal static int[] ChannelMap(int amode, bool lfe) => (amode, lfe) switch { + (0, false) => [0], (0, true) => [0, 1], + (1, false) => [0, 1], (1, true) => [0, 1, 2], + (2, false) => [0, 1], (2, true) => [0, 1, 2], + (3, false) => [0, 1], (3, true) => [0, 1, 2], + (4, false) => [0, 1], (4, true) => [0, 1, 2], + (5, false) => [2, 0, 1], (5, true) => [2, 0, 1, 3], + (6, false) => [0, 1, 2], (6, true) => [0, 1, 3, 2], + (7, false) => [2, 0, 1, 3], (7, true) => [2, 0, 1, 4, 3], + (8, false) => [0, 1, 2, 3], (8, true) => [0, 1, 3, 4, 2], + (9, false) => [2, 0, 1, 3, 4], (9, true) => [2, 0, 1, 4, 5, 3], + _ => Identity(AmodeChannelCount(amode) + (lfe ? 1 : 0)), + }; + + private static int[] Identity(int count) { + var map = new int[count]; + for (var i = 0; i < count; ++i) + map[i] = i; + return map; + } + /// Channel count implied by an AMODE code (excluding the LFE channel). public static int AmodeChannelCount(int amode) => amode switch { 0 => 1, 1 => 2, 2 => 2, 3 => 2, 4 => 2, 5 => 3, 6 => 3, 7 => 4, diff --git a/Codecs/Codec.Dts/DtsQmf.cs b/Codecs/Codec.Dts/DtsQmf.cs index bdca95c47..fd8236cd8 100644 --- a/Codecs/Codec.Dts/DtsQmf.cs +++ b/Codecs/Codec.Dts/DtsQmf.cs @@ -4,13 +4,12 @@ namespace Codec.Dts; /// /// 32-band cosine-modulated QMF synthesis filterbank for the DCA core, reconstructing 256 PCM -/// samples per channel per block from 8 sub-subframe vectors of 32 subband samples each. This is a -/// faithful port of FFmpeg's dca_qmf_32_subbands + synth_filter_float -/// (libavcodec/dcadsp.c, synth_filter.c): the per-subband sign flip -/// ((i-1)&2), a direct (matrix-multiply) 64→32 imdct_half, and the 512-tap -/// polyphase window/overlap stage driven by the perfect- or non-perfect-reconstruction prototype -/// ( / ). The direct -/// IMDCT replaces FFmpeg's FFT path; the task permits a direct matrix multiply for the transform. +/// samples per channel per block from 8 sub-subframe vectors of 32 subband samples each. The stage +/// is the per-subband sign flip ((i-1)&2), the second half of a 64-point IMDCT over the +/// 32 subband values, and a 512-tap polyphase window/overlap driven by the perfect- or +/// non-perfect-reconstruction prototype +/// ( / ). The IMDCT is +/// evaluated as a direct matrix multiply, which is exact and cheap enough at this size. /// public sealed class DtsQmf { @@ -20,9 +19,14 @@ public sealed class DtsQmf { private readonly float[] _synthBuf2 = new float[32]; private int _synthBufOffset; - // Pre-computed imdct_half cosine matrix: out[k] = sum_n in[n] * Cos[k][n], k,n in 0..31. - // FFmpeg's MDCT-half of size 64 produces 32 outputs; the equivalent direct kernel is - // cos(pi/64 * (2k+1) * (n + 0.5)) scaled to match the synth_filter window convention. + // Pre-computed half-IMDCT cosine matrix: out[k] = sum_n in[n] * Cos[k][n], k,n in 0..31. + // + // The filterbank stage is the second half of a 64-point IMDCT over 32 coefficients: + // full[i] = -sum_n in[n] * cos(pi * (2i + 1 + 32) * (2n + 1) / 128), i = 0..63 + // of which the synthesis window consumes full[16..47]. Substituting i = k + 16 leaves + // out[k] = -sum_n in[n] * cos(pi * (2k + 65) * (2n + 1) / 128). + // The phase term is what places each subband at its own centre frequency; getting it wrong + // still yields a full-amplitude signal, just one built from the wrong modulation images. private static readonly float[][] ImdctCos = BuildImdctCos(); private static float[][] BuildImdctCos() { @@ -30,7 +34,7 @@ private static float[][] BuildImdctCos() { for (var k = 0; k < 32; ++k) { m[k] = new float[32]; for (var n = 0; n < 32; ++n) - m[k][n] = (float)Math.Cos(Math.PI / 64.0 * (2 * k + 1) * (2 * n + 1)); + m[k][n] = (float)-Math.Cos(Math.PI * (2 * k + 65) * (2 * n + 1) / 128.0); } return m; } diff --git a/Codecs/Codec.Dts/DtsTables.cs b/Codecs/Codec.Dts/DtsTables.cs index 63d0ae6b9..7a740cad0 100644 --- a/Codecs/Codec.Dts/DtsTables.cs +++ b/Codecs/Codec.Dts/DtsTables.cs @@ -80,25 +80,38 @@ public static class DtsTables { ]; /// - /// Provides the lossy quant value. + /// Quantizer step sizes for the lossy (constant bit rate) core, as the Q22 fixed-point integers + /// the format defines. Rounding them to short decimals loses up to 0.14 percent on the finest + /// steps, and that shows up as a level error on any content the encoder codes at high resolution. /// - public static readonly float[] LossyQuant = [ - 0f, 1.6f, 1.0f, 0.8f, 0.59f, 0.50f, 0.42f, 0.34f, - 0.19f, 0.11f, 0.06f, 0.035f, 0.019f, 0.011f, 0.0065f, 0.0040f, - 0.0025f, 0.0014f, 0.0008f, 0.00045f, 0.00030f, 0.00017f, 0.00008f, 0.00004f, - 0.00002f, 0.00001f, 0.000005f, 0f, 0f, 0f, 0f, 0f, + private static readonly int[] LossyQuantQ22 = [ + 0, 6710886, 4194304, 3355443, 2474639, 2097152, 1761608, 1426063, + 796918, 461373, 251658, 146801, 79692, 46137, 27263, 16777, + 10486, 5872, 3355, 1887, 1258, 713, 336, 168, + 84, 42, 21, 0, 0, 0, 0, 0, ]; - /// - /// Provides the lossless quant value. - /// - public static readonly float[] LosslessQuant = [ - 0f, 1.0f, 0.5f, 0.33f, 0.25f, 0.166f, 0.125f, 0.083f, - 0.0625f, 0.03125f, 0.0156f, 7.874E-3f, 3.922E-3f, 1.957E-3f, 9.775E-4f, 4.885E-4f, - 2.442E-4f, 1.221E-4f, 6.104E-5f, 3.052E-5f, 1.526E-5f, 7.629E-6f, 3.815E-6f, 1.907E-6f, - 9.537E-7f, 4.768E-7f, 2.384E-7f, 0f, 0f, 0f, 0f, 0f, + /// Quantizer step sizes for the lossless (variable bit rate) core, Q22 fixed point. + private static readonly int[] LosslessQuantQ22 = [ + 0, 4194304, 2097152, 1384120, 1048576, 696254, 524288, 348127, + 262144, 131072, 65431, 33026, 16450, 8208, 4100, 2049, + 1024, 512, 256, 128, 64, 32, 16, 8, + 4, 2, 1, 0, 0, 0, 0, 0, ]; + /// Lossy-core quantizer step size indexed by the bit-allocation index. + public static readonly float[] LossyQuant = FromQ22(LossyQuantQ22); + + /// Lossless-core quantizer step size indexed by the bit-allocation index. + public static readonly float[] LosslessQuant = FromQ22(LosslessQuantQ22); + + private static float[] FromQ22(int[] q22) { + var result = new float[q22.Length]; + for (var i = 0; i < q22.Length; ++i) + result[i] = q22[i] / 4194304f; + return result; + } + private static short[][]? _adpcmVb; /// /// Gets the adpcm vb. diff --git a/Compression.Tests/Codecs/Dts/DtsForeignStreamTests.cs b/Compression.Tests/Codecs/Dts/DtsForeignStreamTests.cs new file mode 100644 index 000000000..1be917634 --- /dev/null +++ b/Compression.Tests/Codecs/Dts/DtsForeignStreamTests.cs @@ -0,0 +1,808 @@ +#pragma warning disable CS1591 +using System.IO.Compression; +using Codec.Dts; + +namespace Compression.Tests.Codecs.Dts; + +/// +/// Decodes DTS Coherent Acoustics core streams produced by libavcodec and compares the result +/// against libavcodec's own decode of the same bytes, sample by sample. +/// +/// The core has no dither and no other implementation-defined stage, so the two decoders have to +/// agree to within the rounding of the final 16-bit conversion — one least significant bit. Any +/// looser bound would hide the faults these streams exist to catch: an out-of-range table index +/// that killed every real stream outright, a filterbank modulation phase that put the signal in the +/// wrong sub-bands, a quantizer step table rounded to short decimals, and an interpolation filter +/// that restarted from silence once per frame. +/// +/// +[TestFixture] +public class DtsForeignStreamTests { + + /// Pink noise, 48 kHz stereo, 384 kbit/s, encoded by libavcodec. + private const string StereoPinkStream = + "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"; + + /// libavcodec's decode of : interleaved 16-bit PCM, deflated. + private const string StereoPinkReference = + "eNrtmnd0FVX776eXM6emEEroYF4IUsQISu+G3okIIrx0pEsLCALSXjpBinSkEwLS1IBSQiDSBAMISg8h5fQzvd+9ubqW61333v/v" + + "Wr/9+ePMOmdm731m9n7K9xkE+Z/2P+1/2v/Pzf6r/b/OQf/B/+m7//7t/3bNf4/5z/bf12IA/C/+Pib+4p/f/z3G38fYP4Dn/HN8" + + "y7Zs4y/+buY/+OfYJODvsf7ZJ+wDAo8ZAAugAfA82J8OgH1QAAcA9gP7htfAT3idav9v4LkSAB7D3/W/gOPCazkAvB7+BsdwAzwA" + + "L8AJ+Ps3+AnPh2PC+XB/kQD4e3z4nyOAEkAx4O9xg4AoAH7Cc+D58H9VBbgA8YC/+4HHcG7wd3hcH9AE8C6gEeBtQHVAC0DLv6gL" + + "gPcDzr3ULrXLADcBIYAMgHOqiFREFFt5M3bMjtmpSCqSAfABLtoX7UWAfYAzAPjfhwNaAeC9zLfz7dsAeC1v8za8B/8CNARMBMC+" + + "OgLgXF/aL99cD8/tDqgNmG/Pt78BvAKkIWlv7mcFpAIyFwDnBucPrwnbYTsFSXnzX0/Zp+zXgBeAZkgzpC1gKACO3d/ub18A/Bv5" + + "NzINAO8PnDvsPx1JR9bb6+08wHYEYG+3JwB+RH5EMBuz4X0baA+0k5Ak5CSgHeBrwGh7tP0HYAkgAIDPEM4FfjcZmfzm3uUgOW/G" + + "plAKzUKykKfWU+sychnZam99M5cagNn2bBs+jCZ2Exven9aAB/YDOx6NR9faa988lwLAZ4BipBhx2257KjIV2WJvsdcAJiGTkLWA" + + "zoDqdnU7W8/WZ5uzzUPSIelh6GGoSaxJ7AtANb4av1xcLq431huv9Ff6DeuGVYgX4lP1qfoQeghdm6pNfYl/ia9iV7FRIkpMM6YZ" + + "64n1xHbndudzx3PHDHIGCdfOImIRcZ+6Tx3ljnIbmA3Mr/avdrFerM8CPBWeCvDaDewG9qz3rDcRT8RXAz43Pje2WlutZDlZ3qxu" + + "Vtfp6/T7gFPkKbIYL8b7qf3UBeICcYo5xUzAE/C5xFxij2OP4yn6FO3KdmVreWp5bqO30SQ7yU7AErBxrnGu8b7xPhzgSHAkrPau" + + "9u5O2p3UqmKrihnJGcmFnkLPDGoG5SN8RH+6P73Wudb5se9jn8/pc+biufhSz1LPXtdeV5SO0q2p1lRfvC9+jDhGtFHbqJPESWI5" + + "Vo7B/VHMFDOYgRmrHKsceXqe/iP1I+Vz+BxwLfe2elsMaN8DFiOLkTAbZme6Z7pbelt6C6lC6iZ9kx7uGe4JOAPOhY6FDg3V0DXM" + + "GuaQ55An253tbuRr5NsXty9uR9KOpIHOgc5SspRczixnznJnuXXoOvSuflf/1P70zX5srjRXeqG90APkAXKttlZrbDQ21mHrsDjA" + + "I+WR8rn6uRozY2Y9ph5Tma5Mz0JnoYpDcTxDniGD9EE6j/DIUHuoXcGqYHVDu6GzkdnIAf4Af9W4amRKmdIn/Cc8KqNyE6WJslhY" + + "LLQz25nvAN7i3+K/F74XnBFnZDrgqHZUm4HOQE9SJ6mHzENmDiDLkeXo4ezh9Nk+m9AIbYI8QeYETuhp9DR6Af5U/1SHGcOMMBkm" + + "u6pd1U3KJuUmfhOXHbKjP9mffIg9xE44TjjqxNeJ3wmoRFeiH7AP2E3YJuwwd5hrj7ZHWY3VtohbxOvodbQYEFEjaj2qHpUD2Ozb" + + "7LsNsFiL3cft45A4JC4/Pj/+jveO97P4z+L3+Pb4zrnOuVjQZmAzsB/dP7oFXMA3I5uRJCvJGm4MN6B9ecg+ZMcwY5i3qbepdu52" + + "7lGuUa6Z7Ey2KdeUg3vrKQCu0ynUFCqDAXgyPEPIIWQTo4mRrqfrT+2nb2zer/SvNG7iZhFZRN5ibjHHkePIA8AUego9zznPOcAa" + + "YCWpSWpfo68xCBmE5NA5dJqdZquSKkWsiNXF0cXBUzw1nZnOrERWIm/+E3AkP+A/4K/11/px+jh9gjhBLGIWMauR1chJAuA66ero" + + "6eh5FPcobgI+AZ/CTGHg3E96T3rfcrzluOG74duTtCdpGbWM+sL3hY8C7QPrA2sXu4uFNuS5+FxM0BK0e+F74UQ1UYXPMJfIJfKN" + + "fGMfv48vBLSJtYnlWrnWmvCacFWpqtQy2jJaJ1Qn1FPpqbSyWlkLASV8Cb8itCJ0LHYsdlu/rU8yJhkRPsL/HP05utfcaw4LDwsH" + + "y4PlaBFaZAC+Kv6quFGgUcDP+/kxwTHBykJlIQEgmqK5HLDSWmlBuxsCrNHWaIIoiA2kBlJrvjW/Wlwt/hH4I3C79HbpouJFxW/W" + + "sfmJ+Sj6KDqjbEbZc/W52l/oL3SQO8hhK2zF9Jg+mBhMJMlJ8pf2l7ZKqmRPpifDuTjXV76vfLvsXbbH8BildCldj6hH9KB6UPcd" + + "9x35vnzf957vPZnxmfEnqBNUNpvNXuAucCu9K703mBvMh54PPb3je8fPds92v054nZDKpXJnPGc8pVQp9R0AmH5qLbGWqEPXoXM8" + + "OR64xu8Qd4jp2HTsPn4fv0PeIZuZzUxe4IUcLAc7i5/FhyBDkCQiiegGaO9p7xnrGuuC+wzelynGFGMFvYLuinfFM5VMRQXtHnOP" + + "OY4dxxY6FzpbeFp4+nj7eAvYAvZr9Gu0Hl4Pf2Q8MgrMAhP6mF3WLusceY5Mp9KpcrQczaPyqLnMXOYV9Yrq5OzkTPOmeU+4TrjS" + + "4tLitlTYUmGpY6ljHb2Obko2JfMS8hLO+s76miY3Tb4SfyX+cqXLlQ5UPVD1SpUrVer76vs2ezd7WzpbOpeTy8lD9iE7zhnn/Cju" + + "ozg+no+PY+PYKCANT8Nro7XRk/ZJe4priqtyXOW4q4lXE6t7q3tv0bfoV9wrbiQxknjseex5hDxCZnOzuVX4KjzNSrMea4+1YrVY" + + "faY9074G3Jfvy2WxshgaRIPbte3aNGIaAf1YZ7Wz+rH5sVkJq4TVIeoQf+p/6g20Btq39Lf0feu+NUwYJkCfOkgeJDflm/LZWrbW" + + "TGwm9kR6IpekS5Iz5oxlBjIDPfw9/Mel49IR/gif6c/0k0EyCM8dEB4QFmNiDPoKPIyHoX/NNDPNy/hlvACwR9+jx9lx9mXxsvgb" + + "8hsC57pN2Cacl8/Lpm7qG62N1hpljQJ9XQOuAafSKv2W9ZYFff8T7YlWbBQb16nr1HZsO9bB6mC9I70jcRiHybiMX6Ov0d/I38h7" + + "xb3ieHO8eVW4KiRryVp7ob2w1lxrntBP6I2txtYl/ZI+iB3EwjU+h55DawYA13AYt4xHx6NtlbbKWn4tvwZZg6wV14p3w3fDP+o/" + + "6qiFWluRrcgr7BW2l95LL2OWMc/R5+g6dZ16Vb+qZ1AZVEdfR18XVxfXtrhtcdUSqyVWrFSxUnmF8gr7qf3UWewsNtQ51LkncU9i" + + "f6Y/Y+EWPoYaQz2mH9NZepYOA9wSrUTrpnRT5ovzxQ5IB2S5vFz+gfiB2CXtkpaHlocOxQ7FKoYrhlsFWwUXhxaHrvuv+7OD2cEh" + + "yhBlB2CwPFiG8e9Yaiz1tvi2mG1kG62F1sIJ6YQEfVt1s7rpUl3qzOjMKPSVA5WBSr6er49GRiMwzrZpm+5h9DAmYBOwNEeaA8Z5" + + "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" + + "tlpblRRJ2apv1SNoBL1KX6UH64P1ZkIz4afwT2GZl/mQElKGmEPMA9IBCdbFHkYfRpPNZDOiR3RYNl4vrBeexZ7FirQi7d+xf8e+" + + "0L/QQ3JI7hntGX1Y/rB8bHhseHlseeyaeE1s5G/kb6g2VOG4SeGkcGexs7g6sjriU30qzL3DaljdEdsR+zr0dSgjmhE9xB/i4bsa" + + "BEqgjMIoS4QlAqx3fI19jd2l7lKrjdUGrG28rbytLI4tjiUqicoeY48B9yL0QxPKJ5RLqqTKiqzcEm4Ja621FtRn/8X/i88P54eh" + + "7TsNaBtrG1uqLlXv6ff0D4QPBBZwQb4gP5eeS9AnnuZP89XF6mJuLDc2s2xm2W5+N78kvCScFcgKdOY78/dK75W+H3w/qAiKsEZd" + + "o+4Ud4o91Z4qfG9ADagBWLcfy4/lK6oV1TnaHK071h2D703A9yBgvbC70d2oRdWioK+ZpExSrpvXTagV/Ez/TDdnmjM4gROMg3FA" + + "2/y9+b3pR/3oBeYC80h/pH+FfoVC/wvXy9v42ziMP2Hs+G/i30SBt8D7nvM9ZyqWilVBqiD/Cx7DPC0="; + + /// A 997 Hz tone, 48 kHz mono, 1411 kbit/s, encoded by libavcodec. + private const string ToneStream = + "f/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1rWta1rWta1rWta1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly" + + "5YAAAP/6LgA/E/8uqA90T5MMQfS2cP4PAAAAAADsACGf/yTgBzbf6QCgnrG/elIAAAD//6//v7P/JJ/13B/9hB+rnIG3XAAAAAAB" + + "X//tIAB8x/wngBMcv7jeAeHBAAAAAAB7//YMABcf/sqABt2/93pA3+mAAAAAAHH/+BAAI3f+oqAFPD/qkoCJTQAAAAAAV//8ZAAS" + + "R/+OAAPr3/yXwHh0gAAAAAAv//t4AA7n/0MgAjN/9TIAOm0AAAAAADv//kgADPf/yyACjt/+aABM/4AAAAAAF//9CAAHf/+F8AEW" + + "f/lGABzRgAAAAAAt//8sAAm3/+ZAAdP//0aANneAAAAAAAn//dAAA+f/qHAAjt/7SoAOkIAAAAAAIf//kAAHf//0YAFj///EACkk" + + "gAAAAAAH//5cAAIX/71QAEWf/HRABt+AAAAAAB///9wABhf//SABGeAAGUAgfYAAAAAAAf/+qAAA3//LQAAZX/0+gAIpAAAAAAAX" + + "///4AATwAAHAAOagAD/AGn4AAAAAAAH//uwAAD//1H///h/9uX//RgAAAAAAFAAAHAAESAAFgADDQABkABZngAAA///7//8P//+v" + + "/9sP/+q//hR//SmAAAAAABAAACgAA6AAB/AAp4AAeoATK4AAAP///f//O///Z//gD//b3/5U//uRgAAAAAAQAABEAAMwAAqgAJEg" + + "AJ3AEKeAAAD///v//0///wf/5F//z3/+lz/6OQAAAAAADAAARAACwAAL0AB/oACkAA6qgAAA///7//9r//7n/+ef/8ef/r1/+UUA" + + "AAAAAA4AAFgAAogADZAAcKAAt0AM4gAAAP//+f//c//+l//qH/+9f/7d//g9AAAAAAAKAABYAAIwAA7wAGTgAMyAC46AAAD///v/" + + "/4v//n//7J//tr//AL/3hAAAAAAADAAAbAACCAAQMABawADXwApygAAA///3//+L//4v/+2//6w//wi/9mKys8oPWkQxV9/jwkgX" + + "SdB5Pn8y97/GPxHjz5DkCElFyTVdrDZAMuSAaSKO4b+5cRkd5rTx8efv8F0v+Ifv++Af/zS//054buf9UEn93M//OQf8dsAAhB/+" + + "HUAAFv03Gvrv9f5Be/z/V/6i//7lP/+NAABd/RRu/uFL/x+v/4UX/rrf/9Y//2xAAAz+7mX9rt//ZBP+rUf/eJ//bMAACb//8f5f" + + "v/95C/96+//GL/9E4AAjoAAA//+yf39u/p/7/7cb/zRv/9av/8Lf/+HAAA3+7s//v2//p7P/4Ef/jo//8b//ywAAlv+/6f8Nz//e" + + "U/+C///n7//bf//xAAAGfzrY/+PD/8Fr/+83/6WgABDf/+e//9n/4i3/T+H/7u//nz//6U//z2AAEP//hX9ovv/3Of/PJ//29/+n" + + "D//dP/+7P/+Sf/cr/3UH//Jf/7g4AAIP/+bgAAwAABJ/iCQABCX/2jwABB//ul//5b//1AAACoACbP+RXgAA9//E1//pIAAJYABy" + + "//+l/5yQAAqR/9tsAAnP/9ZP//q//9A//8WACz7/o5YAAUf/whgAGW///KAADj//UP+rmAATu//nGAAFp//YL//0P//JgABMgBE4" + + "/7GKAAQH/9poAANP/9igAAGAABn/tqMAF4X/5kf//4//4q//9mAALoAAW4AXdP+55AAMa//QWAAbv//lQAAlf//Mf8FHABqb/+zQ" + + "ABAH/+KwAB8gAAeAAJ4AGnP/wsoAB1//17gACo//4v//1wAAav/IEwAi4//wLAAK5//VX//e3/+3///QAB4x/8lWAAtH/9wH//uv" + + "/+hAAFo//8l/zgYAIVX/8BwAE2f/5J///N//wn//s4Ah+P/RZAAPH//kn//2AAAmv//awAAT/9OpACPV/+2MAA13/+g//+mgABX/" + + "/9CAJMD/0moADqv/6ggAFW//6J///n//7v/WTQAkM//x4AATr//10AAv3//Rf/9O//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1rWta1rWta1rWta1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5c" + + "uXLly5cuXLly5YPS6/kXQ/7P4B4Rz8dpP/ueAOvNfi9+GT6sRW41DSeY2oHSDOO4ZWOnVfiPbOV//voAAPf//0P/+7AAAl//8GAA" + + "BL//yX//Lf/+4AABtAAB8AABT//uwAABP//0gAAY///gAAR0AALX//EAABEf//ZAADIAAKP//4oAAYf//DAACVAAISAAIIAAb3//" + + "pQABxAAE4AAA3//sf//nH//4AAA1gAA8AADwAALwAAAv/+ff/9X//7v//97//73//+v/+4P/9KgAAJ//+WAAOP//tf//uQABAf//" + + "sAAA1//rsAAGIABDf/+3f/7y//6z//6kAA4oAAVwAAfgACA//85///UAAEQAAjv///AAAB//6B//1z//qn/+vAAA2f/+kAAD1//0" + + "3//4X//1f//+gACo//8b///EAAeYABBf//1gADw//9x//9kAAMAAAjAAAi//7x//07//wD//aAAAOwACbAAB8AAD0AAR0AApX//6" + + "wABGf/+I//+z//r4AATP///QAAsAAGm//3Z//24AATP//3//+ff/+X//9r//47//un//fv/+3//+aAABn//9EAARv//mgABogADB" + + "AADGAADsAAg3//z///E//9B//7sAADr//lIAAPAABI//9b//+5//8UAAa3//5v/+uf/8bAAHn//7cAAWP//NQABh//9t//9Z//+k" + + "AAgP/+6P//6//+zAADr//egAAP///fgADFf/3B///J//4QAAWoAAOgACef/+4AADYAACsAAMn//yQAAZgABxAAM2AAIoAAFf//J/" + + "/7v//+CAAJf//+X//gv//1P//7//96AAD8AAAEAAPgAAgQAAdgAAF//8n///b//5oAA0QAA0AABcAABX//30AALoAACQACUAACT/" + + "/54AALgAATQAA6f/+EAAEPAANgAAKj///3//xP//7//+iAABJ//yMAADn/+2f//hf/9UAAAyAABkAADX//3wAAA///9//+qABVoH" + + "NP/xK1QBaEA4Tm+HMGAVbIG3TkMjHD6qBnNmnhmHihRr01nl8NUZ0ZZe//+oAAAP///z//hP//nQABrf//h//6kAAHP//xQABF//" + + "/i///9//+b//4L//0P//+P//zf/8z///IAAK4AAIP/+1gAAx///S///7//i7//yn//bwAAWAABiAABl//4D//n4ABRv//6AABXAA" + + "FUAAQ0AAPX//1f//Pf//o//0QAAGX//y4AAHAABHf/+e//+r///wAAUgAARgAAof/+gAAFt//8AAAoX//dv//j//9e//69//wj//" + + "uv/+wwAANf//qAADJ//5YAAGP//uwAAhf//N//7v//9gAAF3//z//9A//9p//7l///MAAOn//5//+EgAA/AADYAAAL//ff//R//8" + + "7//9K//77//pcAALQAADv/9qf//3//9EAAJ///1oABCwAAHf/+DAAAn//sgAAH3//7AABNgADsAABT//4j//zIAADgABnAAAi//0" + + "d//3IAAHwAABP/9KgAApAAAt//pb//2n//Sv//Df/87AAEJ//v3//pn//Qv//m//5b//7kAAHIAAOf/+OgAAsAACgAACqAASAAAf" + + "P//u//+SAAAG///3//34AAJQAAav/+4gABIAANX//7cAAJH//lv/+Q//9VAABKAAL8AAF3//0f/+Xf/8ZAAJ9//gj//8X//jgAAQ" + + "AAB+//4KAAXz/+7IABIv/7uAABe//4wAAEoAAKAAAdgAArAABH//8r///4AAJgAByQACFf//w//7B//hT//HX//mgAAtAAA8AADv" + + "//5gAArIAAHf/9MgABW//6N//5UAAYwAARAAAJgAB6//8N//2gAAZv//Qv//+gAAyAAAJ//+j//pAAAWv//QgADkAAGp//9j//j/" + + "//ff//egAAXAABn//yQAAPwAA7//+mAACJ//9d//18AAIn//ugAArAABy//8J//9L//04AAQAABu//+AAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1rWta1rWta1rWta1rWta0AAAAAS6kuXLly5cuX" + + "Lly5cuXLly5cuXLly5cuXLly5fwnwAD9sg1eP+DHyAp10GernwLLgGclGLt4hxz8lBYstPPsPedk2hQlHkJiIlF//00AAR4AAWgA" + + "DCgAC+AABMAAMsAAFf//Iv/8wAAA4//zn///f//cwAAVf//j//8GAAH///68AASQAAsf//x//9x///WAADgAADwAA1AAASf//oAA" + + "H3//+z//SYAA6QACo//8PAAEaAAQkAAYAAAmwAAQgAA6AAKkAAPwAABwACP///+///JAAAL//4gAAO///JgAAY//9+//3yAAJr//" + + "1P//bQAAM///y///aAALQAAfn//Yf//0f/8jAAISAAXEAALoAAYwAAPgAID//8R//2b//tX//igAAuf/9k//0H//7wAAD4ABJAAB" + + "af/+dAABYAAKcAAIAAAvf/8pAADYAAE8AACUAAQgABVf/99//+z//59//9gAAMX//7v/+m///u///R//zr//xf/+u///N//6C//4" + + "aAAJr//1v//vAAAtf/6sAACt//1///woABNAAC/f//tAAA3//usAA3f//RgAA2AACKAACMAAAIAALIAARQAARAADY//6EAAD///5" + + "f//9AABnf//5AADUAAFX//en//iP//+//41AAD2AAJwAABP/++P//ygACa//1x//ncAAQH//wQACRAAEv//+YAAIP///oAAWv//p" + + "gADS//4MAAdL//2gAAlf/9lAAEx//+eAAJH//tQAAPP//p//+XAADJ//60AAuYAAFQAB7f/+qAAE5///X//woAAUf//cgAHTAAJH" + + "//7b//In/+uP/+rgACtAAGsAAG///eIAAVP//lAABC//9L//6gAAcf//6QAA+f/9W//7GAAEn//wP//7QAA7gACXAAAAAAOAAAD/" + + "//X///9AAAfAAB0AAGb//xwAASAADvf/+2//9v//9sAAPwABZv//Z///v//7WAAHr//3gAAKwACRgADtAAF+AALT///X//T//+4g" + + "AAkAACIDIp33/GwD1uwYSH+/G3AjnmC6kL3zjaiOUySY2RL/8VHjyQGI2KoTqvboRu+k///c//8b//5T//+wAAO//96f//0//9UA" + + "AC0AAZf//YgAANgADm//+8AANYAAE3//8v/+of//hAACKAAVz//1oABvAABXAABVAADV//0///xP//nf//YAACg//+x//sT//pX/" + + "/fAAB9AAAXAACr//0z//z//+Sv/9m///gAAHuAADwAAUgAAPAAASAABRAAHgAAjwAAZgAAVv//Q//6kAABb//1YAAYYAA3QAAB//" + + "+w//8l//4T//3f//yAAB5f/9M//86AANsAAP///vP/+6f/79AABmAAJoAAQIAAhQABOAADI///2AAEgAALoAAbwAAGgAB0//9R//" + + "ywAAdwAAGv/+Cf/6j//3t//9gAAYoABAQAAqf//5//4n//ygAAPH//uwAAqAAAjAAAUAAOAAATQAAsgAAsAACaAAMH//0L//cv//" + + "6gAETf/6K//5QAAT0AAIv//4v//egAC7//5GAALr//b3//Mf/+Vf//p//6V//0cAAH3//BQAAEgAAWAAEd//5YAAlf//8v//LAAF" + + "NAAEP//4v//+3//sv/91gAAk//86AAF///9IAASQAA2AAAv//80AAKr//44AAcP/+3f/92//8L//13//0YAAev//gf//s///z//0" + + "QAAPf//hwAABf/7kAAHn//dQAAZH/94gAA4//8BAAFqAAEYAALgAAhAACfgABH//5b//33//rYAA/AACKf/6M//ur//dv//44ACU" + + "QABrAACb//68AAH///0QAAwwABb//8cAAHgAAJoAAZH//kf//VAADvAAHF//4kAAZ4AACv//if/+aAAL7//0H///P//0gAAff/97" + + "AAEf//y0AAu///FwAAzgADe//8QAAJQAAOIABE///w///7AAAB//10AADIAAcwABMAACgAAB8AAJb//83//6wACVf/+AAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1rWta1rWta1rWta1rWta0AAAAA" + + "S6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly5YFEjgWa3+9+pAtdMCrZH3r8oGQtAUcnxfehWtZELoTWNZiy7pyhK9Ox4N5YK6Z/" + + "/lwAAc3//MAABTf/+vAAAF//+///ygAAIwAB3gAEP//85//8r//z3//XQAAz//84//8WAAKf//mYAAGgABAf/+uAAA9//9X//+oA" + + "BEP/+NAABf//6j//+H//lgAATP/+g//9TAAAKAAA///mwAA+AAAV///YAAHGAABMAAE4AA0gAAef/+j//2Z//2wAABv//RwAAb//" + + "/C//5MAAFn//xX//vAABI//+fAACh//+b//yX//aP/+oAABgAAC1//oX//33//3P/9wf/78///SAADf//rAAAmAAA1f/+W//8YAA" + + "CoAAfwAAov/+k//91AADB//57//vv//r///jAAB5AAHL//5MAAgP/+ggAAY//+NAAB5///0AAH4AAM///ugAB9AADyAAO7//9QAA" + + "OgAB4gADP//7YAAH3//t4ABBQAAagAA9//7L//qj//RAAAbAABU//70//9T//tf//5H//dAAAqf//jAAN2AAGUAAEv//WwAAngAF" + + "lAAEmAACv//oAAAf///1gAACAAAOAABH//sIAAqwABQgACbAADJ//8gAAAv//xAADAf/+pAABEAAOv//lX//0v/+0//8s//8wAAK" + + "z//mYAAKv//BgAB1AALF//psAACH//0P//rgAAcAAAV//4j//qAAAT//7egAHM//1uAAOT//DAAAJgACD//+0///sAAJMAAef//5" + + "f/9YgAClAAA9//xj//If//AgAAsAAEIAAISAAPQAAFAAAMgABLAAAYAACsAAHb//8P//EgAAk//8r//+mAAFEAAEQAAZf//TAAAT" + + "AADuAAHAAAVQAAFP//wf//t//9YAAJIAAA//+xv//5gAAw//6n//4MAAeQAADgAA1gABmAAE+AAZj//vn//HgAATf/+7///R//6j" + + "//rIAAuQABBAABU//9h70qEDbLQJtnfedKgdszBJmN7zDoD/zQcYiZDQmMbOQCaBBJYXVl+r4KF/JTdvf/+LAAJaAAM4AAb3//tP" + + "//1AABUAAAaAAEcAAcIAAXP//PAAAk///KAAHMAAHAABMAAAaf/+F//+x//2L//poAAHP//qAAE5AABZ//9gAAmwAAdv/+D//+JA" + + "AFp//4H//vQAAhwAAT///1//5z//9H//sgAAyv//n//96//79//2j//kAABSf/9egAAh///r//5IAAGP//wf/+HgAAuAADcAAIcA" + + "AB///YP/+n//+vAAAAAAGn//mwABUv/+UgABLAAC0AAAL//5n//lQAA5gAA6AAOmAAGgAAPX//3AABFgAAsAAGf//7z//8AAAM//" + + "/M//7R//6+AAOT//0AAAGv//MAAA8///mAAUwAAoQAAbwABIf/8J//1F//vQAAHwAAbwAAPAACH//1///xUAADYAAhf//ef/99AA" + + "ASAAPkAAKoAAHP//EAABB//9yAAAf//2H//rv/+RgACr//8yAAIsAACwAAz///lAADF//5CAALf//ov//xgACrAAAy//63//+0AA" + + "M///mf//o//8I//7+AAAEABDn//3v//UgACr//5x//8v//zIAA6QAA4AADf//5yAAEwAAdn//yAAAO///aAAGp//pf//6YAAvP//" + + "bf//P//98AAPL//egAARP/+xAACx//6UAAMH//zoABMP/+0gACWAABp//0QAAU3/+/gAAKAACS//8L//yUAARgABkQABqf/6Q//x" + + "7//4EAA5YABcP//1gAA3//+4AAK0AAagAASf/+p//7h//4IAAEUAAJv//qf//x///Z//+5//1gAABAABMQAASAAAuAABD//1z//4" + + "X//BgABPAAAq///WAANf//93//6P//sAADQ//+uAAAn//fYAADgAABf/9y//+QAAHcAAIn//h//+5AABGAAKyAALsAACwAAp///i" + + "//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1rWta1rWta1rWt" + + "a1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly5YIpsfeXugiKJBApX7y5QEiKQHiUvebuNDTTAHXDLj5tqsrH3vup" + + "mExNmnl90oH//sP//2YAAG//+y//+rAAB3//6UAAZwAA5v//1//8ZAAEv//vIAARH//cQAA5gABrAAKQAAAr//QP/+oP/9pgAAP/" + + "/99//8UAAKf//3P/84gABd//+0AAH7//0P//uAAAGgABCAAE4AANf//y4AAq//+gAAA7AAKAAATkAAAQAA9f/+ggACc//6AAAXwA" + + "AA3//ywAAOgADNAAAT//yD//u3//mv/+S//+UAAE2AAKX//P3//aP//xf/7y//8oAABn//5gAAkgACgAAEPAAIUAADH//5wAAnv/" + + "9bgABz//1j//8T//8IAALP//yAACR//9Z//4j//7AAAO///3f/69AAF1//xT//koAAE//8S///oAADv///3//xQAAAQAAdf//f//" + + "8D//9oAAA3//+AAAMgACH//7////UAAXwAAXv/+s//88///d//skAAdAAAHP/+5gAA9AABaAACMAAG3//nQABX//8+AAB5//8EAA" + + "PIAAYf/+8//6pAAKL//+b//+3//X////f/+///+2AAEIAAJ4AAIgABSf/9z//2n//3QAANgAAcQAAfgACO//7eAAF7//kn//WAAB" + + "RAAAR//+B//9UAAG4AABAAA9gABl//8cAAJD//34AAbP/+zAAE+//+R///T//i///UQAD6f/5LAAID//qUAA+AAATQABfgADw//7" + + "R//0f//yn//7P//vAAD6//71//sX//84AAqQADBgAEsAAE6AAHgAAbQAADP//ngAAkAAAAAAG0AAEP//+AAAf//62//6iAAAYAAR" + + "f//FwAAo//7oAAFkAANf//jP//+///6///8AACR//+D//ZP//xAABdAABd//4Z//xIAALQAA0gAAGgAC7AAMiAALj//v3//TAAA8" + + "f//G//7x//60AAQQAAfgABwAAAT//9wCZroDe7PvRfwUOYgZgp968mCqMkC5T84JH27PF+VwNnsNu4UE3uKmNDLoXJJuf/+z///j" + + "//1r//uwABLQABcf/+u//7f//+L//z3//1wABJgAEs//9h//23//3gABWP//+//8///9r//xr//z4AA/P//R//9F///gAACgAAno" + + "AABAAA5f//h///x//9IAAcwABPf//hgAAv///IAAPoAAGv//Zf/+Nf//1AAEQAABQAAQP//Vf//Af//SAAC5//7n//qAAAsAAA0/" + + "/+zAAAJ//s0AAGf//pv/+6AAAJAADb//9YAAMoAAPf//LAACO///x//zv//0YABMAAAcAAEs///D//0v//uP/+1QAAN///D//6p/" + + "/y///6gAAGwAAcAAB8//5kAAIIAACP//agABBgAE1AAJQAAcgAAnoAAtv//pgAEx//40AAFwAAHf//+///Rf/6bAAFsAAEv//kf/" + + "/Lf//9///4AAE0AAGn//04AAF//++//+9//6gAAbb//dH//dwAACf/+eAAFCAAJ0AAEn//xv//ogABa//9SAALD//uIAAUgAAD//" + + "8IAAC2AAHX//zX/+bv/+v///f//9KAAREAA5oAA2AACvgAF/AAFB//6gAAGP//twAA2f//+AADT//67//3v///AAD9AADUAAD9//" + + "pf//24AAMQAC0//88AAHb//sAAAMv/+e//+Bf/9CAANh//ooAAKQAAEAABhAACy//4YAAE///ooAAmwAA3gABZ///r//2b//p3//" + + "Pv/8Lf/6CAABCAAOQABUQAA7P//VAAAL//9yAAS8AAGIAAmP/+2f/+nAAB3//6v//oYAAEwAAYgABp//7gAAHcAAdX//JwAAA///" + + "J///+AAKf//23//nAAACAAAmAABl//+QAAUH//ZQAAr///lAAEL//ssAAz4AAvgAAQf//S//+N//s4AAigAAF//+wf/+9//+N//7" + + "3//zn//3gAAlf/+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QAAAAAa1rWta1r" + + "Wta1rWta1rWta1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly5fvdTwWMuAUv298xIC5I0CTo3vwSwYCP9NJRpI4S" + + "55KvlJL1PTBXUeKcFh9qt4GAALAAAKv//EwABrgAAH//9GAAPr//nAAA3wAAOgAFS//70AAE7//bAABJQACnf/+pAAAV//skAAZo" + + "ABTgAAxf/9v//8kAAFf//hwAARf/+0gADSAAGcAAD///7P//IwAAl//+aAAFwAAWcAAaP//2AABZf/+tAACGAAAUAAkwABCgAA4A" + + "ACBAAHWAAHAAASgAAmf//Nf//l//zoAADH//tAAAvP/+DAAGs///t//7f//8IABSAACYgABNAAG8AAE3//sIAAWwAAw//90//7AA" + + "AN8AAMP//dP/9zgABb//4MAABP///gAAQv/+r///zAAAIAAG3//0AAARf//lgAA9//4d//pf//+4AAAgAAPAABnAAAL//qj//5YA" + + "AxAAB6AAEAAAF0AAD8AASP//3P/+fAACyAAB+AAHT//7P//rAABF//+u//4f//7AAAZ4AAeP/9+f/+SAACv//1oAAWQAAwf///gA" + + "DI//+b//9sAATX//SP//cAAAHAAAWAAFX//2wAAMwAAogACpAADf//6H//3P//+AABF///E///QAAaP//jwAA0QAAf///G///x//" + + "5kAANP//mQAA4gAArAALF//y8AAfoAAZAAALf/+pAAIR//2H///4ABEP/9ggAAy//6YAABH//OoABzP/+nAACY//yEAAWUAARP//" + + "nAAAMgAAP///1//4wAAIgAAnAAAqf/6g//6mAABwAALAABygACeAADUAABKAAAX//2IAAeAAAaf//EAAAl//1wAATX//2f//zgAB" + + "W//8L//2sAALAAAqv/+SAAEaAAC1//74AAN//+6AAAVAAEi//3OAAKwAAUn/+twABJ//8sAAH7//7YAAW3//WP//xgAAe//4Z//0" + + "f//eoAAiv//Df/+7AAJiAASL//74AANf//yf//6AAPmA/nz39twMdcwGsAfAhQBm9YArxr4KjTskldxH5Vxpud0l9r9YGxqQDwO2" + + "tOdC//9DAAB9//6L//lH//xf/+9AAAgAAG5//+8AAh4AANwAAmgACEAAFWAAIf//5v/+dQAAmf//AAABaAAFP//7X//2QAAof/+b" + + "AABh//9YAAUP//mv//Kf//h//x5//1gAAKX/+7QAAN//83AAAn//2///+P//7QABJ//+lAACp//xr//xn//Z///OAACv///+AAJk" + + "AAVn/+ygABX///r//9MAAQkAAPH//8gAAF///8AAB2AAUT//1wAAWv//pf/9rAABp//44AAaX//HAABx///wAAC8AABv//8n//0f" + + "//OgAB6AAFmAAEv//rX///QACggACsAAAsAAFwAAxAAApAAB9gADc//74AATkAAawAABgACpf//bAAIL//8f//jv//9//88f/+M/" + + "/75//8L//jQAARv//GgAA1//87//+EAAKf//H//9jgADNAAFv//0z//vgAAfAACb///wAAEb///sAAJv//dwAAnf/8XAACyAACb/" + + "/vP//6QABeAAA0//7B//wQAAN3//yv/+z//9w//8WAAQMAAEP//Ef//nf//hAABCAARn//t4ABFwAAy///YAACuAABD//iIAAcgA" + + "Bzf//O///GAALEAAYYABjv/+fAAFT//8gAANX//nX//TAABRgAEnAADiAAAEAAW//98wABu//6mAAMiAAF4AATP//aP/+3gAAD//" + + "7qAADsAABf/+0f/7lgAGMAAGuAAin//zAAAkQACGAADcAAHV//5wAAJ///4gAAz//9JAAAcAAFIAAWAAAxAAALAADNAAAkAAAD//" + + "xYAAbv//bf/9YAACMAAIkAAC3//FwADGf//OAADQAALX//i//+9v/+MgAAo//4KAAIYAAB4AAhQAAQAACP//5AAAFf//zoAAmgAB" + + "GAAAM//9EAAEj//9gAAlQAAz//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4AAHvg2////QA" + + "AAAAa1rWta1rWta1rWta1rWta1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly5YNR5QEKr/CSRBtSCAXcb4bPAOAf" + + "ABpzWp2eeMZpnr8a45zDyhvss0A3lb0eZNoAAAUAADwAANP/9tf/+2AAGD//0cAAqAAAIf/+2f/+E//4gAACgAAT4AAyP//+f/9t" + + "//zaAAIQAAA///Qv/+zAAALAAAiAAIL//3P//+AAA9AADr//9eAADYAAIn//VAABF///FAAAqAACEAAJn/+YQAAXf//R//+T//xM" + + "AAngAAbAAA0AAEeAAGUAAAv//8n//mQACAf/8yAACR//5H//63//+gAB0f/+U//5yAAOkAAK3//P///agAF9//5v//zb//yH//v/" + + "/9n//8BAAEQAAPMAAXQAAPv//HAACL///4AAWcAAM3//5gABrAAAUAAE9//pQAAA///mAABB//70//yl//oT//cX/+/v/9IAADq/" + + "/8iAACoAAe///IwAANgABoAACaAARL//4///Tf/+z//83///V//4T//lP//ZAAAk//7t//+0AAHsAASoAAPf/+VAAC0AAC5//98A" + + "ADgAA3f/9/gABA//uZ//7b//0f//dAAAnf/8JAAHuAADwAAEP//BQABQgAAu//8iAANoAAXIABZP//cf//9//8sAAI///toAA9v/" + + "+S//9YAAKD//+MAAK//+5QAAff/6vAABz//9H//hv//y///b//+y///n//2QAAQ///tP//+///k//5H//yUAAfP//eAABEf//wAA" + + "B6AAIQAAEn//agAAJgAFNAAB2AAD3//+QAANgACgf/5n//u4AAJgAATAACCAACG///B//1f//zn//6AAAGv/+3f/8y//+R//8z//" + + "tn//t//////9q//7///qH//4YABbQAA5///kAAAV//xUAAS3//1AAAZf//nAABGAACIAAHv//uP//wgAHU//75//u///zf//4gAC" + + "FAABo//7mAAFP//9X//eAAAX///1//9J//+3//1n//lAAAQf//QAAKB8R9cHLmgAM+Pi6lg6vs/8Vv8cXoHepuON9cCNFvNxOwxb" + + "JiQXJ5sD9+UXsiOmf//w//8///2n//u///DgAAlgABC//7sAABX//3gAAXv/+8///p//5YAAO3//5YAALf//cAAAI//0R//mgAAO" + + "H//PwAAAgABb///AAAHz//vYAAs///QgABk//3MAAHL//k4AAEf//zgAC8AADj//5b//+v//MQAAjAAC7//60AAFL//toAADv/+w" + + "//9EAAGcAAMwAAK///gv/+k//4yAAAr//+8AAOoABDQAAFgABRAABV//3UAAJ3//VQAAS///BAAGF//+P//eQAAlwABuAAEMAAFl" + + "//7IAAHYAAawABhAAD9//+OAAFL//3IAA5f/+iAAAn//+F//58AAMIAA9gACuAAENAAFb//8D//6QAA4v//m//+8//+J//4P//y4" + + "AAB//+0f/9H//8sAAAT//vf/+rv//Of//lAACWAAQ4AAKH/+X///NAADEAADd//6EAAfgAAoAABU//9u//87//wX//q///uAACs/" + + "/9rAADEAAer//7YABYv//s//+MAAAGAANP//vgAAlP//Mf/+d//9v//6L//5IAAJgAB1//99//8sAAI0AAZv//rgAAqf//xAABJ/" + + "/3UAAOgAAAP/9m//91AAGj///T//3v//dwADXf/+E//96AAMoAAEwAAiv/+DgADw//3UAAdH//YYACB//8dAAH0//z9//64AAJv/" + + "/Q///OgAEoAAGKAAIkAAYX/97v/+mAAGxAAOqAAVIAAD///Z//8F///hAACwAAAb//sIAAfAABAAAAx//++AAGP//vX//M//9t//" + + "9PAABWAAFX//9P///P//y//8B//5KAAS3//x///Mf//xAACT//yUAAXf//n4AAqgAACf//cAAE4AACkAAoIAAgv//zAABM//5Z//" + + "5f//0YAAmf/9cAAAhAAHcAAAj//5v//1P/+o//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAf/6AAfw8dbA2wAE4" + + "AAHvg2////QAAAAAa1rWta1rWta1rWta1rWta1rWta0AAAAAS6kuXLly5cuXLly5cuXLly5cuXLly5cuXLly5f+7wvkNOA89U/ya" + + "J8nocHwpX9qQPltHPL4Tu1NfmVzx5G79vLU9DgdPEfCs/Kd//xT//4///Dv/+5///XAADf//90AAWP//xv//m//+EAABZ//2YAAR" + + "H//kP//h///h//78AAJQAAUX//rwAAjgAAL//8EAAVH//9X//8v//u//96//+6AABX//1//+zgABV//+KAAG2AAOgAAD4AAJQAET" + + "//8Y//9X///QAAL4ABVwABzAACCAACv//8MAAFP//1gABYAACWAAGIAAGAAATYAAgP/+ggAAO//6j//tQAAP//+0QABQ///KAAIc" + + "AAIsAAHwAACQABdgAB6//7N//7QAAkH//G//+2f/8EAAF1//7b///4ABH//+7//+g//6R//8z//03//Jv/86//7o//zAAAGMAAWf" + + "//tf//2gABCAAA+AAFr//3QABEP//h///rAAHOAAGz//9oAAgQACDAAHqAAAD///MAAUv//bQAA4f/8pAAEV//8cAAJ3//swAARg" + + "ABM//40AAHIAAPAAACQABF//9r///8AAQX//soAACv/9w//++//3T///EAACP//iP//bf/+tAAGf//5UAAEoAAHv/+a//5jAAIeA" + + "ARoAAiX/+yv/82f/9SAABf//8b//ngAAdwAAzgAATAACN//74AATYABc//9mAACq//2z//7MAAAn//tf//A///r//5+AAEj//Hv/" + + "/vgAAzf/+bAAA7//xMAAkoAAEv//3///N//y3//3T//iAAATAABGf/4S//5gAASMAAvIAAB//9E//6z//0uAAAn//6wAATv//QgA" + + "CV///0AAAIAAJP//hv/9sgABHAAFD//24AAIAAAkP//vgABJ//+cAAIb//6gAAFQAATAABOAAEF//7IAAFoAAtP//+//9AAAOCAA" + + "Qr//6wAAfgABlgAEbAAAwAAJ8AAC4AAeP/9lf/74///QAALUAANIABeQAAS//+IAACID73v+gaPzofgdDIAFGV86waIwQ/pKr+qR" + + "s3fWnWEQc2XyC8C/us9G/Iji3Igp///Y//hD/8aP/h2X8VxPoID+tZm2RssAALkAAYQAOAgAUagHdCAZecBKbENNAwAAlv/9If/g" + + "h/9Cr/phb+BkP6hTvJf8f/+kAAJSAB0QAFgQBDFQEhjgLhOCJ7SAAAr//5//7Iv/i1/8Td/sX7/Lf73V9oAAaAAAtAATwABMaALb" + + "UA2SgCJ+wZqa//94//zx/+3r/6fv/Tev8Xsf2cF+Y8T//+oAACAAD4QAPcACU3ALOaAcF8FKmAAAI///3f/1I/+6R/3I3/Rp/+Gl" + + "/rSMAAAIAAEkAAxgACPwAgAACVpgF/4BF1d//97//f//8if/w6f+Ix/2EL/mfH7n0YAAZAAD1gANwAApyAGuUAg5oBTawPSU///t" + + "AAAv//QD/88n/k1/94g/6e2/C49//rX//uYACKwAHmABiuAHciAS08Da4H//Gf//O//6l//Rn/6Av/ho/+ybPyS4AACP//9AAAgo" + + "AB84AWqABtNgEZsAyHGAAEv//63/9U//2Y/+sU/5HN/t/38384AAYAAAygAIvAAimAFC0AZ8IA/BQLqCf/9rAAHN//oD/9tf/s/v" + + "+by/714/Rhh//y4AAaAAB5QAGEgBJhAF4QAPBICvRf//WgAAj//4F//sH/7tT/no3/CRv1F2f/5nAAWEAAaUACx4ARXwBdQADlIA" + + "py5//1X//uX/9/f/4Jf+39/6PV/xYv9Yuv//zgAAWgALuAAniAEikAVcAA2vAKDPf/+c///n//qH/+BX/t2/+nQf8X+/Xut//8T/" + + "/mQADOAAHPABG9AFYGANsoCdAf//0gAA///1q//iP/7n3/qGn/JWv2Jk//+5AAHwAAD8AB8QAQjABVQADW8AmyZ///wAAQf/+JP/" + + "24f+8Y/6nf/yVj9lfoABIQAAKgAIcAAeoAEOQAVLIA09gJm+//+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" + + "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"; + + /// libavcodec's decode of : interleaved 16-bit PCM, deflated. + private const string ToneReference = + "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" + + "FWfw/wPgd+rS"; + + /// Six distinct tones, 48 kHz 5.1, 768 kbit/s, encoded by libavcodec. + private const string SurroundStream = + "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" + + "FupeMzzto1/g3m9a4uYUO/pG7yf205MHvRmVF6l40M5UYNKQbNqsIZyAGeyAcT1GWmhoN+0VpSXdzLKw6rkIh1o6DXJ5R22j/mgD" + + "5UsyLetGHirg13N4q+cfcidvKM0lS1ED+/+EifPGejo40uChmGkr41X0J56iXZ0CJcte1ExR2C5dzKw+dan4+ubP/2a7ltQh4UPY" + + "oxLs1X5oI6mCjEuzVf7vbNqfRk0pKkpXq6fRmz7f9Pvp9a7b2/V59Prut2d6vavX+/lJMm+n1VVdnb4+n0Sk29vp7lBGrNuc3urD" + + "3dhfFzq+aXpgRX46iYXR1tDkgBmy9d5Aiqo4/njd59Rw5KoYYR5DyN4lveoOKcUWKs5C7EyhZ5uJRCZDzEuOnUN98HDC2KiIfGRv" + + "YG6xfFpGwSaINFF3zxP9RB8lEwLeSKlzrcca9/ofsuZyRJwj89FCvWu3HcI0sXY9Egur2N8a1mzs1CesZEls6EFWMRe8R1gd/QOs" + + "GQKslCezemdwSwHvZcbm9aGOfUWgUcJyiD7ph+Cp0tsTXtA4lwzULN68Ak3tc6T3MuyZ0eQKlo68vVyA7DDyjxkd95k+FELYl020" + + "lDrCUIrVXhj1NU+hGfZ2gKwOIqusUZZccPurH9s/Vam7/v/8Vnbl0hfnAlf/+A=="; + + /// libavcodec's decode of : interleaved 16-bit PCM, deflated. + private const string SurroundReference = + "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" + + "gvqANpyZRkNrHkmro2LN2A9GIxtWKK3UKLJfMocmHuamNoBe6VahgrPJr63l6Pjss/Rr9zL/Cm02HSt8TTd6z+ebaneanVWL/pw5" + + "kbtZM+JG3KWX5NvlirVqenV6hD7KF2U7atMVj86lywSavUdbIbxltqSWNDrTT1ubrx9bY9aPHE8P0k6la0YuNou0bukh2kj3evKA" + + "US36lfd37e1Eq8yE+KbY9d4L2rNWLeeN2M3G4tRU7W72ojUp+qhZmJqpXcYirL9+O33XfVf7mZWwptpOWtf9SHvD/ixxPCIWvO98" + + "ol3kPOy9rBynjZzV2u3epf6l8hC6KrlB+znbUF4hfmremdymbeZv6cXCPKM0sUdbKi0xdvHO8RGJA1o/dTvt6a+OVk8c1d6Pzqcn" + + "c6e11fZJraVxpzE6ey7Sz/5NU6iiV898LcdsS/uF/kNa700V11uetoUOz49MtSUjLF9bZu5ItXYz+essRV8Wb5W43Pky67KYfkaf" + + "wmolp6ZLWIH+ckRnVyZeSL3Cauprxd1WD/tjpz+ro2/yX3XmWkKymF2m/5LZn1Gst+1C1lDvmvqWTGOjLJM10dsll0RuYfOZX1ak" + + "J6w3Ygq7nDllN+jj2UEzWZYsS5Q112WWpRF2MfPKWsLx783b2NtMYq10x3ozNpMNtGqwNvpdyWURw3rRbsra691TB8l861SiI+ug" + + "H8t8l+lnz3CeZp31rf4M56bEkNTbrJu+SdxjXZl8Lr2D/VV/JVLAmjgfZ/Osh16mv8LudH/N32z11FfG2yVGppqR0da9+nJzV+pL" + + "bzT0z/36bvpcPp5ZJet2b/08nSg9kf0hcr/dR69KTf1A7rC2zO6r/8XoZLT3F0XjiX76suhCuoG3jj8J8WB1B71RmGPsh3iN9LGx" + + "WFxs3pbsp3/N39Avlv9Gl0Jcmi2UxytH6WVOX/0ar65/NJKhsyH2k729xtpGWtPtoz9jL0sM0G+kb7nQHraVTYveZ16Suk83YVy9" + + "G7vWmJfqpXdg06yZMA6beffoz1mFzhCjRnSd111/K9E+c415g9Yp3U0f5t5C9poNIz+nu+j/ShdGulJPGpHpqH+Zvya2lK4S4tm7" + + "9E+FeeZZOogvyLbVJylQdBTUz7fKtdLjMF8y9GSmNHebLsbl+B66xRuTb67farZSx9Nd7kX+DfqrdLhQlYrO536RbtHh2efNYYle" + + "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" + + "n4hssjyWtdSPCbPJADbZv561UceJr5AN1novyhoo9aWXiJdIJ0R2VlwrPUOYe47VZR+QXvJQMjIjs/tZD9+SB5Hu/iPWl6wgN0np" + + "RzqKB5O3WMfS9SM9SP3IsfRh67vU8khH8pq+k8+0y5w2ajF5Kr5WGQxvZZ/anEwwS6OPJvfZvbUisoYONoc7H1sntAbEpS6dBaNn" + + "oF6H3ExvpvtTLdkpvRqZaHSO10mLrH80SqTYPHhbddnRqED2qceF0uxUdnfM44VyabZDvrP1VayM30Secjf4fewm8X/xdrm+9o1k" + + "ReLV+FE+3WvJlgidHDt+gHd27mf1pbqpLsY3fIW9wp4s10nPNzbzc4y555XirGus5VXZ6FwbdWy+tbkc5sA4cZK2k080F/POVlNt" + + "vV5T3GF+wLsmPouXRnvJEqzztd1H6YnY+EhzOoM/lz5D98cnwd5lIu+Qv8qcZzwSfYWO48VCf6jq1PhndBQvVBooZebjxh76NLzd" + + "xvxROsE8SQfykliX9BrakyboA5yaA+CtH6B52pOPp4UWp5ySgi6cU4GVH78zPL6W9qJJejuc/2hyQHidm8LrlJlPwPWbwfWv4uX3" + + "bcgbKIXKfKM/tKcubw0jel98sjaQ1uAd843Mk7Hnof0GHwvtL43eC8+lwHMNoOv1WvC8eT943snaLugHx+9iNdNg9OSLzfMwjsbD" + + "PG+ddYxf/aow4qfIdaE/j/rnoHIq7+fv/aCflwqdof+/8Ts5vdlNZGViRnyLPw3eS4nf124cX+e3zfWxO+a7WCWxlf6N5Em3NDuN" + + "dYst8YP3Owree2l0nr9PPSbUDcfDHF+Mfah+F46T1/0JRqf47HD8TPKD8VM+rp73g3E1AMZbH22MvxbG2+BEKxiHQ/xgHL4Zjs/H" + + "/Kfj65TD1n4Ytw/6r+u7ePl4vtcPxvOXjOYmK139TuKhZG/W3bflO/0eMP7rQl6+V77dHwXzIpgv66QbfQvmS5QVwjy62k/DPArm" + + "13ix0C+B+TWWtdCPC5f4A9gk6Bcx1lqo6gfzMZinHxDdnw1P0QDmLSzBfvBZBvP6QZ7KT04eoMF8L/Ev5OenptODMCLq+7/miyEP" + + "XHAydFy+NH8VvOHyvLE/H+SNPpBP2uW+zp+AfPIJ5JlF2a/yt0KeMSD/0OzqPPydGJ0bGRuVWZovNceyM5CvjqXn5UtpIXsgzGOz" + + "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" + + "n4voz0tF9Od5IvrzbBH9eYaI/vyyiP78DxH9eZiI/vyYiP78gIj+3FNEf+4koj+3EdGfbxHRn68R0Z8bihX+3FerK6I/VxPRn6Mi" + + "+rMgoj97AvrzBQH9+VcB/blUqPDn9dJ3AvrzTgH9eaOA/vylgP68TEB//lhAf35PQH9+U0B/niagP78koD8/K6A/DxfQn/8moD/3" + + "F9Cf+wjozz0E9OdOAvpzWwH9+TYB/flGAf25qYD+3FBAf64noD/XEtCfCwT0Z11AfxYF9OcsQX9OEvTn8wT9+V8E/fkXgv78A0F/" + + "/o6gP39D0J+3EfTnrwj68xqC/rySoD9/QtCfFxL05w8J+vNcgv78FkF/fo2gP08l6M8TCPrz8wT9+VmC/jyaoD8PI+jPTxH058cJ" + + "+vOjBP35IYL+3IegP99L0J97EPTnrgT9uSNBf25P0J/bEPTnVgT9uSVBf25O0J9vJOjP1xL052YE/bkJQX++kjzwx+8/FxL8/efL" + + "CPpzPYL+fAlBf76IoD/XJOjP1Qj6cwFBfzYJ+nOMoD/rBP1ZJejPcqXffxYJ+jMh6M8+R3/OcfTnDEd/9jj6s8vRnx2O/pzg6M8W" + + "R39mHP35Akd/Ps/Rn89x9OezHP35DK/kzxz9+V8c/flXjv58spI/n6jkz8cr+fOxSv78yx/+XBL7uZI///Qffv/5aCV/Lq3kzz9W" + + "8ucjlfz5SCV//qGSP/9QyZ8PV/LnQ5X8+VAlfz5YyZ8PVvLng5X8+UAlfz5QyZ+//w/+/H0lf/6ukj9/V8mfv6vkz99V8uf9HP15" + + "P0d/3l/p95/3V/r9530c/XkfR3/ex9Gf93H0530c/flbjv78LUd//pajP3/L0Z+/5ejP33L05285+vNejv78LUd/3svRn/dy9Oe9" + + "HP15L0d/3svRn/dy9Oe9HP15D0d/3sPRn/dw9Oc9HP15D0d/3sPRn/dw9Oc9HP15N0d/3s3Rn3dz9OfdHP15N0d/3s3Rn3dz9Odv" + + "OPrzNxz9+RuO/vwNR3+G+A9/3sXRn3dx9OddHP15F0d/3sXRn3dy9OedHP15J0d/3snRn3dy9OevOfrz1xz9+WuO/vw1R3/ewdGf" + + "d3D05x0c/XkH7AJe/d2ft3P05+0c/Xk7R3/eztGft3H0520c/XkbR3/extGft3L0560c/XkrR3/eyvH3n7dw9OctHP15C0d/3sLR" + + "nzdz9OfNHP15M0d/3szRnzdz9OfNHP15E0d/3sTRnzdx9OdNHP15E0d/3sTRnzdx9OeNHP15I0d/3sjRnzdy9OeNHP15I0d/3sjR" + + "nzdy9OeNHP15I0d/3sjRnzdy9OdNHP15E0d/3sTRnzdx9OdNHP15E0d/3szRnzdz9OfNHP15C0d/3sLRn7dw9OetHP15Kw/q8LWh" + + "P2/j6M/bOPrzdo7+vJ2jP+/g6M9fc/Tnrzn6806O/ryLoz9/w9Gfv+Hoz7s5+vMejv68l6M/f8vRn/dx9Of9HP35O47+fICjPx/k" + + "6M+HOPrzYY7+fISjP//I0Z+PcvTnnzj68y8c/fkYR38+ydGff+Xoz6c4+vMZjv78G0d/Ps/Rn8s4+rPF0Z8THP3Z4ejPKY7+nOHo" + + "zzmO/sw5+rNA0J9lgv4cIejPOkF/jhP0Z0rQn6sS9OcaBP35IoL+fAlBf65H0J8vJ+jPDQj6cyOC/nw1QX++hqA/X0/Qn28m6M8t" + + "CPrzHQT9uQ1Bf25P0J87EvTnrgT9uQdBf76XoD/3IejPDxH05wEE/flxgv78NEF/Hk7Qn8cQ9OfnCPrzCwT9eRJBf55O0J9fJ+jP" + + "swj683sE/Xk+QX9eTNCflxH059UE/XkdQX/eRNCfdxD0570E/fkAQX8uJYET/l8hjcSA"; + + private static short[] Decode(byte[] stream) { + using var src = new MemoryStream(stream, writable: false); + using var dst = new MemoryStream(); + DtsCodec.Decompress(src, dst); + return ToSamples(dst.ToArray()); + } + + private static short[] Reference(string deflated) { + using var src = new MemoryStream(Convert.FromBase64String(deflated), writable: false); + using var inflate = new ZLibStream(src, CompressionMode.Decompress); + using var dst = new MemoryStream(); + inflate.CopyTo(dst); + return ToSamples(dst.ToArray()); + } + + private static short[] ToSamples(byte[] pcm) { + var samples = new short[pcm.Length / 2]; + Buffer.BlockCopy(pcm, 0, samples, 0, samples.Length * 2); + return samples; + } + + private static void AssertMatchesReference(string streamBase64, string referenceBase64, int channels) { + var reference = Reference(referenceBase64); + var ours = Decode(Convert.FromBase64String(streamBase64)); + + Assert.That(ours, Has.Length.EqualTo(reference.Length), + "the decode must cover the whole stream, not stop at the first frame it cannot parse"); + Assert.That(reference.Length % channels, Is.Zero); + + var peak = 0; + var exact = 0; + for (var i = 0; i < reference.Length; ++i) { + var d = Math.Abs(reference[i] - ours[i]); + if (d > peak) peak = d; + if (d == 0) ++exact; + } + + Assert.Multiple(() => { + Assert.That(peak, Is.LessThanOrEqualTo(1), "no sample may differ by more than one LSB"); + Assert.That((double)exact / reference.Length, Is.GreaterThan(0.98), + "at least 98 % of samples must be identical, not merely close"); + }); + } + + /// 0.064 s of pink noise, 48 kHz stereo at 384 kbit/s. + [Test] + [Category("RoundTrip")] + public void StereoMatchesLibavcodec() + => AssertMatchesReference(StereoPinkStream, StereoPinkReference, channels: 2); + + /// + /// 0.08 s of a 997 Hz tone, 48 kHz mono at the full 1411 kbit/s. A single tone at that rate is + /// coded with the finest quantizer steps in the table, the ones whose short-decimal spelling was + /// 0.14 percent off; broadband content never reaches them. + /// + [Test] + [Category("RoundTrip")] + public void FineQuantizedToneMatchesLibavcodec() + => AssertMatchesReference(ToneStream, ToneReference, channels: 1); + + /// + /// 0.05 s of six distinct tones, 48 kHz 5.1 at 768 kbit/s. The core transmits the front channels + /// centre-first and the LFE last, so this covers both the channel map and the LFE interpolation + /// filter, whose history has to survive the frame boundary. + /// + [Test] + [Category("RoundTrip")] + public void SurroundMatchesLibavcodec() + => AssertMatchesReference(SurroundStream, SurroundReference, channels: 6); + + /// Stream info has to agree with libavcodec's view of the same stream. + [Test] + [Category("HappyPath")] + public void SurroundStreamInfoMatchesLibavcodec() { + using var src = new MemoryStream(Convert.FromBase64String(SurroundStream), writable: false); + var info = DtsCodec.ReadStreamInfo(src); + + Assert.Multiple(() => { + Assert.That(info.SampleRate, Is.EqualTo(48_000)); + Assert.That(info.Channels, Is.EqualTo(6)); + Assert.That(info.Amode, Is.EqualTo(9)); + Assert.That(info.Lfe, Is.True); + }); + } +} diff --git a/Hawkynt.FileFormats.Audio/REFERENCE.md b/Hawkynt.FileFormats.Audio/REFERENCE.md index 3bd57ed98..ed4625ab6 100644 --- a/Hawkynt.FileFormats.Audio/REFERENCE.md +++ b/Hawkynt.FileFormats.Audio/REFERENCE.md @@ -1081,11 +1081,11 @@ DCA "block code" sample unpacking for low bit-allocation indexes (abits 1..7 whe #### `DtsCodec` -Clean-room DTS Coherent Acoustics (DCA) core codec. The decoder is a faithful managed port of the FFmpeg reference decoder (`libavcodec/dcadec.c` + `dcadata.c` + `dcahuff.h`) and the encoder lives in the companion partial source file. The core decoder emits interleaved little-endian signed 16-bit PCM at the stream's native channel count (the AMODE full-bandwidth channels in document order, with the LFE channel last when present). Scope: only the standard 16-bit big-endian framing (sync 0x7FFE8001) is decoded; the 14-bit and byte-swapped framings throw `NotSupportedException`. DTS-HD extension substreams (XCH / XXCH / X96 / XBR / XLL and EXSS) are not decoded; an embedded core remains decodable. +Clean-room DTS Coherent Acoustics (DCA) core codec; the encoder lives in the companion partial source file. The core decoder emits interleaved little-endian signed 16-bit PCM at the stream's native channel count, in the ITU/WAVE interleave order — front left, front right, front centre, LFE, then the surrounds — rather than the centre-first AMODE order the bit stream uses. Scope: only the standard 16-bit big-endian framing (sync 0x7FFE8001) is decoded; the 14-bit and byte-swapped framings throw `NotSupportedException`. DTS-HD extension substreams (XCH / XXCH / X96 / XBR / XLL and EXSS) are not decoded; an embedded core remains decodable. | Member | Signature | Summary | | --- | --- | --- | -| `Decompress` | `static void Decompress(Stream input, Stream output)` | Decodes a DTS stream into raw interleaved little-endian signed 16-bit PCM on `output`. Channels are emitted in AMODE document order with LFE last. Throws `NotSupportedException` for the unsupported 14-bit / LE framings. | +| `Decompress` | `static void Decompress(Stream input, Stream output)` | Decodes a DTS stream into raw interleaved little-endian signed 16-bit PCM on `output`. Channels are emitted in the ITU/WAVE interleave order. Throws `NotSupportedException` for the unsupported 14-bit / LE framings. | | `Encode` | `static byte[] Encode(ReadOnlySpan interleaved, DtsEncoderOptions options = null)` | Encodes interleaved PCM16 to standard 16-bit-big-endian DTS Coherent Acoustics core frames. This is a managed adaptation of FFmpeg's LGPL `dcaenc.c` core path: 512 PCM samples per frame, 32-band cosine analysis, direct bit allocation and scale-factor transmission, the no-Huffman quantizer selector, two sub-subframes and the mandatory 0xFFFF DSYNC marker. Prediction, high-frequency VQ and LFE coding are intentionally left off so every coded subband remains independently decodable and the generated core is deterministic. | | `ReadStreamInfo` | `static DtsStreamInfo ReadStreamInfo(Stream input)` | Reads stream-level info (sample rate, native channel count, bitrate, duration) from the first core frame. | @@ -1169,7 +1169,7 @@ DCA Huffman code tables ported verbatim from the FFmpeg reference decoder (`liba #### `DtsQmf` -32-band cosine-modulated QMF synthesis filterbank for the DCA core, reconstructing 256 PCM samples per channel per block from 8 sub-subframe vectors of 32 subband samples each. This is a faithful port of FFmpeg's `dca_qmf_32_subbands` + `synth_filter_float` (`libavcodec/dcadsp.c`, `synth_filter.c`): the per-subband sign flip `((i-1)&2)`, a direct (matrix-multiply) 64→32 `imdct_half`, and the 512-tap polyphase window/overlap stage driven by the perfect- or non-perfect-reconstruction prototype (`Fir32Perfect` / `Fir32NonPerfect`). The direct IMDCT replaces FFmpeg's FFT path; the task permits a direct matrix multiply for the transform. +32-band cosine-modulated QMF synthesis filterbank for the DCA core, reconstructing 256 PCM samples per channel per block from 8 sub-subframe vectors of 32 subband samples each. The stage is the per-subband sign flip `((i-1)&2)`, the second half of a 64-point IMDCT over the 32 subband values, and a 512-tap polyphase window/overlap driven by the perfect- or non-perfect-reconstruction prototype (`Fir32Perfect` / `Fir32NonPerfect`). The IMDCT is evaluated as a direct matrix multiply, which is exact and cheap enough at this size. | Member | Signature | Summary | | --- | --- | --- | @@ -1209,8 +1209,8 @@ Constant numeric tables for the DCA core decoder, ported faithfully from the FFm | `LfeFir128` | `static readonly float[] LfeFir128` | Provides the lfe fir 128 value. | | `LfeFir64` | `static readonly float[] LfeFir64` | Provides the lfe fir 64 value. | | `LfeIndex` | `static readonly int[] LfeIndex` | Provides the lfe index value. | -| `LosslessQuant` | `static readonly float[] LosslessQuant` | Provides the lossless quant value. | -| `LossyQuant` | `static readonly float[] LossyQuant` | Provides the lossy quant value. | +| `LosslessQuant` | `static readonly float[] LosslessQuant` | Lossless-core quantizer step size indexed by the bit-allocation index. | +| `LossyQuant` | `static readonly float[] LossyQuant` | Lossy-core quantizer step size indexed by the bit-allocation index. | | `SampleRates` | `static readonly int[] SampleRates` | Provides the sample rates value. | | `ScaleFactorQuant6` | `static readonly uint[] ScaleFactorQuant6` | Provides the scale factor quant 6 value. | | `ScaleFactorQuant7` | `static readonly uint[] ScaleFactorQuant7` | Provides the scale factor quant 7 value. |