From 733d90386e4bfd2e1aac809362bc6df3be668e02 Mon Sep 17 00:00:00 2001 From: VoidX Date: Sat, 9 Mar 2024 18:17:49 +0100 Subject: [PATCH] Optimized AC-3 exponent parsing --- Cavern.Format/Transcoders/EnhancedAC3Body.cs | 2 +- .../Transcoders/EnhancedAC3Body/Allocation.cs | 12 +- .../EnhancedAC3Body/AllocationConstants.cs | 10 +- .../EnhancedAC3Body/AllocationParsing.cs | 125 ++++++++++++++++++ .../EnhancedAC3Body/BitAllocation.cs | 87 +++--------- .../EnhancedAC3Body/DecodeAudioBlock.cs | 26 +--- .../EnhancedAC3Body/EncodeAudioBlock.cs | 10 +- .../Transcoders/EnhancedAC3Body/Memory.cs | 7 - 8 files changed, 170 insertions(+), 109 deletions(-) create mode 100644 Cavern.Format/Transcoders/EnhancedAC3Body/AllocationParsing.cs diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body.cs b/Cavern.Format/Transcoders/EnhancedAC3Body.cs index 1856933a..a831f19e 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body.cs @@ -61,7 +61,7 @@ public void PrepareUpdate(BitExtractor extractor) { LFEResult = new float[header.Blocks * 256]; } - if (exps == null) { // If caches don't exist, create them + if (chexpstr == null) { // If caches don't exist, create them CreateCacheTables(header.Blocks, channels.Length); } if (header.Decoder == EnhancedAC3.Decoders.EAC3) { diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/Allocation.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/Allocation.cs index a06f56e2..06b0508d 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/Allocation.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/Allocation.cs @@ -152,30 +152,30 @@ void DecodeTransformCoeffs(BitExtractor extractor, float[] target, int start, in bap1Next = bap1[extractor.Read(bap1Bits)]; bap1Pos = 0; } - target[bin] = (bap1Next[bap1Pos] >> exp[bin]) * BitConversions.fromInt24; + target[bin] = (bap1Next[bap1Pos] >> exponents[bin]) * BitConversions.fromInt24; break; case 2: if (++bap2Pos == 3) { bap2Next = bap2[extractor.Read(bap2Bits)]; bap2Pos = 0; } - target[bin] = (bap2Next[bap2Pos] >> exp[bin]) * BitConversions.fromInt24; + target[bin] = (bap2Next[bap2Pos] >> exponents[bin]) * BitConversions.fromInt24; break; case 3: - target[bin] = (bap3[extractor.Read(bap3Bits)] >> exp[bin]) * BitConversions.fromInt24; + target[bin] = (bap3[extractor.Read(bap3Bits)] >> exponents[bin]) * BitConversions.fromInt24; break; case 4: if (++bap4Pos == 2) { bap4Next = bap4[extractor.Read(bap4Bits)]; bap4Pos = 0; } - target[bin] = (bap4Next[bap4Pos] >> exp[bin]) * BitConversions.fromInt24; + target[bin] = (bap4Next[bap4Pos] >> exponents[bin]) * BitConversions.fromInt24; break; case 5: - target[bin] = (bap5[extractor.Read(bap5Bits)] >> exp[bin]) * BitConversions.fromInt24; + target[bin] = (bap5[extractor.Read(bap5Bits)] >> exponents[bin]) * BitConversions.fromInt24; break; default: // Asymmetric quantization - target[bin] = ((extractor.Read(bitsToRead[bap[bin]]) << (32 - bitsToRead[bap[bin]])) >> exp[bin]) + target[bin] = ((extractor.Read(bitsToRead[bap[bin]]) << (32 - bitsToRead[bap[bin]])) >> exponents[bin]) * BitConversions.fromInt32; break; } diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationConstants.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationConstants.cs index 5bbfd0be..d1756303 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationConstants.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationConstants.cs @@ -5,10 +5,6 @@ namespace Cavern.Format.Transcoders { partial class EnhancedAC3Body { partial class Allocation { - public readonly int[] dexp; - public readonly int[] exp; - public readonly int[] psd; - public readonly int[] bndpsd; public readonly int[] excite; public readonly int[] mask; public readonly byte[] bap; @@ -104,10 +100,10 @@ partial class Allocation { public Allocation(EnhancedAC3Body host, int maxLength) { this.host = host; - dexp = new int[maxLength]; - exp = new int[maxLength]; + groupedExponents = new int[maxLength]; + exponents = new int[maxLength]; psd = new int[maxLength]; - bndpsd = new int[maxLength]; + integratedPSD = new int[maxLength]; excite = new int[maxLength]; mask = new int[maxLength]; bap = new byte[maxLength]; diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationParsing.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationParsing.cs new file mode 100644 index 00000000..9244de85 --- /dev/null +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/AllocationParsing.cs @@ -0,0 +1,125 @@ +using System; +using System.Runtime.CompilerServices; + +using Cavern.Format.Utilities; + +namespace Cavern.Format.Transcoders { + partial class EnhancedAC3Body { + // Convert bitstream data to allocation data + partial class Allocation { + /// + /// Reference exponent for decoding others with offsets (absexp). + /// + public int absoluteExponent; + + /// + /// Grouped exponents read from the bitstream (gexp/exps). A 7-bit encoded exponent data contains 3 consecutive exponents. + /// + /// Handling of this array differs from the reference code, + /// the first element is extracted to . + public readonly int[] groupedExponents; + + /// + /// Fully decoded, final exponents, used for shifting mantissas to place (exp). + /// + public readonly int[] exponents; + + /// + /// Exponents mapped for power spectral density. + /// + public readonly int[] psd; + + /// + /// PSD summed for each masked band (bndpsd). + /// + public readonly int[] integratedPSD; + + /// + /// Read grouped full-range channel exponent data from the bitstream and decode it. + /// + public void ReadChannelExponents(BitExtractor extractor, ExpStrat expstr, int nchgrps) { + ReadExponents(extractor, nchgrps); + extractor.Skip(2); // This is gainrng, telling the max gain as 1/(2^gainrng). It's useless. + UngroupExponents(nchgrps, expstr, 0, 1); + } + + /// + /// Read grouped coupling channel exponent data from the bitstream and decode it. + /// + public void ReadCouplingExponents(BitExtractor extractor, ExpStrat expstr, int startMantissa, int ncplgrps) { + ReadExponents(extractor, ncplgrps); + absoluteExponent <<= 1; + UngroupExponents(ncplgrps, expstr, startMantissa, startMantissa); + } + + /// + /// Read grouped LFE exponent data from the bitstream and decode it. + /// + public void ReadLFEExponents(BitExtractor extractor) { + absoluteExponent = extractor.Read(4); + groupedExponents[0] = extractor.Read(7); + groupedExponents[1] = extractor.Read(7); + UngroupExponents(nlfegrps, ExpStrat.D15, lfestrtmant, lfestrtmant + 1); + } + + /// + /// Read the grouped exponents from the bitstream. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void ReadExponents(BitExtractor extractor, int ngrps) { + absoluteExponent = extractor.Read(4); + for (int group = 0; group < ngrps; group++) { + groupedExponents[group] = extractor.Read(7); + } + } + + /// + /// Ungroup what's grouped in , decode the differential coding, + /// and calculate power spectral density values. + /// + [MethodImpl(MethodImplOptions.AggressiveInlining)] + void UngroupExponents(int ngrps, ExpStrat expstr, int startMantissa, int exponentOffset) { + // Ungrouping and decoding exponents + int grpsize = expstr != ExpStrat.D45 ? (int)expstr : 4, + absexp = absoluteExponent, // Rolling differential decoding value (dexp in the reference code) + endMantissa = exponentOffset; + exponents[0] = absexp; + for (int grp = 0; grp < ngrps; grp++) { + int expacc = groupedExponents[grp]; + absexp += expacc / 25 - 2; // Ungroup and unbias mapped values in the same step + for (int j = 0; j < grpsize; j++) { + exponents[endMantissa++] = absexp; + } + + absexp += expacc % 25 / 5 - 2; + for (int j = 0; j < grpsize; j++) { + exponents[endMantissa++] = absexp; + } + + absexp += expacc % 5 - 2; + for (int j = 0; j < grpsize; j++) { + exponents[endMantissa++] = absexp; + } + } + + // Exponent mapping into PSD + for (int bin = startMantissa; bin < endMantissa; bin++) { + psd[bin] = 3072 - (exponents[bin] << 7); + } + + // PSD integration + int i = startMantissa, + k = masktab[startMantissa], + lastbin; + do { + lastbin = Math.Min(bndtab[k], endMantissa); + integratedPSD[k] = psd[i++]; + while (i < lastbin) { + integratedPSD[k] = LogAdd(integratedPSD[k], psd[i++]); + } + k++; + } while (endMantissa > lastbin); + } + } + } +} \ No newline at end of file diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/BitAllocation.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/BitAllocation.cs index dd1c4787..c95068b1 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/BitAllocation.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/BitAllocation.cs @@ -4,24 +4,23 @@ namespace Cavern.Format.Transcoders { partial class EnhancedAC3Body { - void Allocate(int channel, ExpStrat expstr) { + void Allocate(int channel) { if (csnroffst == 0 && fsnroffst[channel] == 0) { allocation[channel].bap.Clear(); return; } int snroffset = (((csnroffst - 15) << 4) + fsnroffst[channel]) << 2; - Allocate(0, endmant[channel], fastgain[fgaincod[channel]], snroffset, exps[channel], nchgrps[channel], - exps[channel][0], expstr, allocation[channel], deltba[channel], 0, 0, false); + Allocate(0, endmant[channel], fastgain[fgaincod[channel]], snroffset, allocation[channel], deltba[channel], 0, 0); } - void AllocateCoupling(ExpStrat expstr) { + void AllocateCoupling() { if (csnroffst == 0 && cplfsnroffst == 0) { couplingAllocation.bap.Clear(); return; } int snroffset = (((csnroffst - 15) << 4) + cplfsnroffst) << 2; - Allocate(cplstrtmant, cplendmant, fastgain[cplfgaincod], snroffset, cplexps, ncplgrps, cplexps[0] << 1, - expstr, couplingAllocation, cpldeltba, (cplfleak << 8) + 768, (cplsleak << 8) + 768, true); + Allocate(cplstrtmant, cplendmant, fastgain[cplfgaincod], snroffset, couplingAllocation, cpldeltba, + (cplfleak << 8) + 768, (cplsleak << 8) + 768); } void AllocateLFE() { @@ -30,36 +29,11 @@ void AllocateLFE() { return; } int snroffset = (((csnroffst - 15) << 4) + lfefsnroffst) << 2; - Allocate(lfestrtmant, lfeendmant, fastgain[lfefgaincod], snroffset, lfeexps, nlfegrps, lfeexps[0], - ExpStrat.D15, lfeAllocation, lfedeltba, 0, 0, false); + Allocate(lfestrtmant, lfeendmant, fastgain[lfefgaincod], snroffset, lfeAllocation, lfedeltba, 0, 0); } - void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrps, int absexp, - ExpStrat expstr, Allocation allocation, DeltaBitAllocation dba, int fastleak, int slowleak, bool coupling) { - // Unpack the mapped values - int[] dexp = allocation.dexp; - for (int grp = 0; grp < ngrps; ++grp) { - int expacc = gexp[grp + 1]; - dexp[grp * 3] = expacc / 25; - expacc -= 25 * dexp[grp * 3]; - dexp[grp * 3 + 1] = expacc / 5; - expacc -= (5 * dexp[grp * 3 + 1]); - dexp[grp * 3 + 2] = expacc; - } - - // Expand to full absolute exponent array - int i, j; - int grpsize = expstr != ExpStrat.D45 ? (int)expstr : 4; - int[] exp = allocation.exp; - exp[0] = absexp; - int expOffset = coupling ? start : (start + 1); - for (i = 0; i < (ngrps * 3); ++i) { - absexp += dexp[i] - 2; // Convert from differentials to absolutes using unbiased mapped values - for (j = 0; j < grpsize; ++j) { - exp[expOffset++] = absexp; - } - } - + void Allocate(int start, int end, int fgain, int snroffset, + Allocation allocation, DeltaBitAllocation dba, int fastleak, int slowleak) { // Initialization int sdecay = slowdec[sdcycod]; int fdecay = fastdec[fdcycod]; @@ -67,28 +41,10 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp int dbknee = dbpbtab[dbpbcod]; int floor = floortab[floorcod]; - // Exponent mapping into psd - int[] psd = allocation.psd; - for (int bin = start; bin < end; ++bin) { - psd[bin] = 3072 - (exp[bin] << 7); - } - - // psd integration - int[] bndpsd = allocation.bndpsd; - j = start; - int k = masktab[start]; - int lastbin; - do { - lastbin = Math.Min(bndtab[k], end); - bndpsd[k] = psd[j++]; - for (i = j; i < lastbin; ++i, ++j) { - bndpsd[k] = LogAdd(bndpsd[k], psd[j]); - } - ++k; - } while (end > lastbin); - // Compute excitation function - int[] excite = allocation.excite; + int[] psd = allocation.psd, + bndpsd = allocation.integratedPSD, + excite = allocation.excite; int bndstrt = masktab[start]; int bndend = masktab[end - 1] + 1; int begin; @@ -98,7 +54,7 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp lowcomp = CalcLowcomp(lowcomp, bndpsd[1], bndpsd[2], 1); excite[1] = bndpsd[1] - fgain - lowcomp; begin = 7; - for (int bin = 2; bin < 7; ++bin) { + for (int bin = 2; bin < 7; bin++) { if (bndend != 7 || bin != 6) { lowcomp = CalcLowcomp(lowcomp, bndpsd[bin], bndpsd[bin + 1], bin); } @@ -110,7 +66,7 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp break; } } - for (int bin = begin, bins = Math.Min(bndend, 22); bin < bins; ++bin) { + for (int bin = begin, bins = Math.Min(bndend, 22); bin < bins; bin++) { if (bndend != 7 || bin != 6) { lowcomp = CalcLowcomp(lowcomp, bndpsd[bin], bndpsd[bin + 1], bin); } @@ -122,7 +78,7 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp } else { // Coupling channel begin = bndstrt; } - for (int bin = begin; bin < bndend; ++bin) { + for (int bin = begin; bin < bndend; bin++) { fastleak = Math.Max(fastleak - fdecay, bndpsd[bin] - fgain); slowleak = Math.Max(slowleak - sdecay, bndpsd[bin] - sgain); excite[bin] = Math.Max(fastleak, slowleak); @@ -130,7 +86,7 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp // Compute masking curve int[] mask = allocation.mask; - for (int bin = bndstrt; bin < bndend; ++bin) { + for (int bin = bndstrt; bin < bndend; bin++) { if (bndpsd[bin] < dbknee) { excite[bin] += (dbknee - bndpsd[bin]) >> 2; } @@ -142,10 +98,10 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp int[] offset = dba.Offset; int[] length = dba.Length; int[] bitAllocation = dba.BitAllocation; - for (int band = bndstrt, seg = 0; seg < offset.Length; ++seg) { + for (int band = bndstrt, seg = 0; seg < offset.Length; seg++) { band += offset[seg]; int delta = bitAllocation[seg] >= 4 ? (bitAllocation[seg] - 3) << 7 : ((bitAllocation[seg] - 4) << 7); - for (k = 0; k < length[seg]; ++k) { + for (int k = 0; k < length[seg]; k++) { mask[band++] += delta; } } @@ -153,8 +109,9 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp // Compute bit allocation byte[] bap = allocation.bap; - i = start; - j = masktab[start]; + int i = start, + j = masktab[start], + lastbin; do { lastbin = Math.Min(bndtab[j], end); int masked = mask[j] - snroffset - floor; @@ -173,7 +130,7 @@ void Allocate(int start, int end, int fgain, int snroffset, int[] gexp, int ngrp Array.Clear(bap, i, bap.Length - i); } - int LogAdd(int a, int b) { + static int LogAdd(int a, int b) { int c = a - b; int address = Math.Min(Math.Abs(c) >> 1, 255); if (c >= 0) { @@ -182,7 +139,7 @@ int LogAdd(int a, int b) { return b + latab[address]; } - int CalcLowcomp(int a, int b0, int b1, int bin) { + static int CalcLowcomp(int a, int b0, int b1, int bin) { if (bin < 7) { if (b0 + 256 == b1) { return 384; diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/DecodeAudioBlock.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/DecodeAudioBlock.cs index dcbb1ca1..9f62067f 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/DecodeAudioBlock.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/DecodeAudioBlock.cs @@ -76,28 +76,19 @@ internal void DecodeAudioBlock(int block) { // Exponents ParseParametricBitAllocation(block); if (cplinu[block] && cplexpstr[block] != ExpStrat.Reuse) { - cplexps[0] = extractor.Read(4); - for (int group = 0; group < ncplgrps;) { - cplexps[++group] = extractor.Read(7); - } + couplingAllocation.ReadCouplingExponents(extractor, cplexpstr[block], cplstrtmant, ncplgrps); } // Exponents for full bandwidth channels for (int channel = 0; channel < channels.Length; channel++) { if (chexpstr[block][channel] != ExpStrat.Reuse) { - exps[channel][0] = extractor.Read(4); - for (int group = 0; group < nchgrps[channel];) { - exps[channel][++group] = extractor.Read(7); - } - gainrng[channel] = extractor.Read(2); + allocation[channel].ReadChannelExponents(extractor, chexpstr[block][channel], nchgrps[channel]); } } // Exponents for LFE channel if (header.LFE && lfeexpstr[block]) { - lfeexps[0] = extractor.Read(4); - lfeexps[1] = extractor.Read(7); - lfeexps[2] = extractor.Read(7); + lfeAllocation.ReadLFEExponents(extractor); } // Bit allocation parametric information @@ -252,16 +243,14 @@ internal void DecodeAudioBlock(int block) { bap2Pos = 2; bap4Pos = 1; - // TODO: optimize for reallocation - currently it doesn't reallocate all the time when needed - if (cplinu[block]) { // && cplexpstr[block] != ExpStrat.Reuse) - AllocateCoupling(cplexpstr[block]); + if (cplinu[block]) { + AllocateCoupling(); } bool got_cplchan = false; for (int channel = 0; channel < channels.Length; channel++) { if (chahtinu[channel] == 0) { - //if (chexpstr[block][channel] != ExpStrat.Reuse) - Allocate(channel, chexpstr[block][channel]); + Allocate(channel); allocation[channel].ReadTransformCoeffs(extractor, channelOutput[channel], 0, endmant[channel]); } else { throw new UnsupportedFeatureException("AHT"); @@ -293,8 +282,7 @@ internal void DecodeAudioBlock(int block) { // Combined mantissa and output handling for LFE if (header.LFE) { if (lfeahtinu == 0) { - //if (lfeexpstr[block]) - AllocateLFE(); + AllocateLFE(); lfeAllocation.ReadTransformCoeffs(extractor, lfeOutput, lfestrtmant, lfeendmant); lfeAllocation.IMDCT512(lfeOutput); Array.Copy(lfeOutput, 0, LFEResult, block * 256, 256); diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/EncodeAudioBlock.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/EncodeAudioBlock.cs index c8c63560..acacb627 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/EncodeAudioBlock.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/EncodeAudioBlock.cs @@ -73,16 +73,18 @@ internal void EncodeAudioBlock(BitPlanter planter, int block) { // Exponents for full bandwidth channels for (int channel = 0; channel < channels.Length; channel++) { if (chexpstr[block][channel] != ExpStrat.Reuse) { - planter.Write(exps[channel][0], 4); - for (int group = 0; group < nchgrps[channel];) { - planter.Write(exps[channel][++group], 7); + planter.Write(allocation[channel].absoluteExponent, 4); + int[] exps = allocation[channel].groupedExponents; + for (int group = 0; group < nchgrps[channel]; group++) { + planter.Write(exps[group], 7); } - planter.Write(gainrng[channel], 2); + planter.Write(0, 2); // gainrng, unused } } // Exponents for LFE channel if (header.LFE && lfeexpstr[block]) { + int[] lfeexps = lfeAllocation.groupedExponents; planter.Write(lfeexps[0], 4); planter.Write(lfeexps[1], 7); planter.Write(lfeexps[2], 7); diff --git a/Cavern.Format/Transcoders/EnhancedAC3Body/Memory.cs b/Cavern.Format/Transcoders/EnhancedAC3Body/Memory.cs index 7383454a..572a2292 100644 --- a/Cavern.Format/Transcoders/EnhancedAC3Body/Memory.cs +++ b/Cavern.Format/Transcoders/EnhancedAC3Body/Memory.cs @@ -139,8 +139,6 @@ partial class EnhancedAC3Body { int[] fgaincod; int[] frmchexpstr; int[] fsnroffst; - int[] gainrng; - int[] lfeexps; int[] mstrcplco; int[] mstrspxco; int[] nchgrps; @@ -152,7 +150,6 @@ partial class EnhancedAC3Body { int[][] cplco; int[][] cplcoexp; int[][] cplcomant; - int[][] exps; int[][] spxcoexp; int[][] spxcomant; @@ -225,14 +222,11 @@ void CreateCacheTables(int blocks, int channels) { deltba = new DeltaBitAllocation[channels]; dithflag = new bool[channels]; endmant = new int[channels]; - exps = new int[channels][]; fgaincod = new int[channels]; firstcplcos = new bool[channels]; firstspxcos = new bool[channels]; frmchexpstr = new int[channels]; fsnroffst = new int[channels]; - gainrng = new int[channels]; - lfeexps = new int[nlfegrps + 1]; lfeexpstr = new bool[blocks]; mstrcplco = new int[channels]; mstrspxco = new int[channels]; @@ -261,7 +255,6 @@ void CreateCacheTables(int blocks, int channels) { cplcoexp[channel] = new int[cplbndstrc.Length]; cplcomant[channel] = new int[cplbndstrc.Length]; deltba[channel].Reset(); - exps[channel] = new int[maxAllocationSize]; } }