Skip to content

Commit

Permalink
Optimized AC-3 exponent parsing
Browse files Browse the repository at this point in the history
  • Loading branch information
VoidXH committed Mar 9, 2024
1 parent 03c15aa commit 733d903
Show file tree
Hide file tree
Showing 8 changed files with 170 additions and 109 deletions.
2 changes: 1 addition & 1 deletion Cavern.Format/Transcoders/EnhancedAC3Body.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
12 changes: 6 additions & 6 deletions Cavern.Format/Transcoders/EnhancedAC3Body/Allocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
10 changes: 3 additions & 7 deletions Cavern.Format/Transcoders/EnhancedAC3Body/AllocationConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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];
Expand Down
125 changes: 125 additions & 0 deletions Cavern.Format/Transcoders/EnhancedAC3Body/AllocationParsing.cs
Original file line number Diff line number Diff line change
@@ -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 {
/// <summary>
/// Reference exponent for decoding others with offsets (absexp).
/// </summary>
public int absoluteExponent;

/// <summary>
/// Grouped exponents read from the bitstream (gexp/exps). A 7-bit encoded exponent data contains 3 consecutive exponents.
/// </summary>
/// <remarks>Handling of this array differs from the reference code,
/// the first element is extracted to <see cref="absoluteExponent"/>.</remarks>
public readonly int[] groupedExponents;

/// <summary>
/// Fully decoded, final exponents, used for shifting mantissas to place (exp).
/// </summary>
public readonly int[] exponents;

/// <summary>
/// Exponents mapped for power spectral density.
/// </summary>
public readonly int[] psd;

/// <summary>
/// PSD summed for each masked band (bndpsd).
/// </summary>
public readonly int[] integratedPSD;

/// <summary>
/// Read grouped full-range channel exponent data from the bitstream and decode it.
/// </summary>
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);
}

/// <summary>
/// Read grouped coupling channel exponent data from the bitstream and decode it.
/// </summary>
public void ReadCouplingExponents(BitExtractor extractor, ExpStrat expstr, int startMantissa, int ncplgrps) {
ReadExponents(extractor, ncplgrps);
absoluteExponent <<= 1;
UngroupExponents(ncplgrps, expstr, startMantissa, startMantissa);
}

/// <summary>
/// Read grouped LFE exponent data from the bitstream and decode it.
/// </summary>
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);
}

/// <summary>
/// Read the grouped exponents from the bitstream.
/// </summary>
[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);
}
}

/// <summary>
/// Ungroup what's grouped in <see cref="groupedExponents"/>, decode the differential coding,
/// and calculate power spectral density values.
/// </summary>
[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);
}
}
}
}
87 changes: 22 additions & 65 deletions Cavern.Format/Transcoders/EnhancedAC3Body/BitAllocation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {
Expand All @@ -30,65 +29,22 @@ 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];
int sgain = slowgain[sgaincod];
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;
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -122,15 +78,15 @@ 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);
}

// 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;
}
Expand All @@ -142,19 +98,20 @@ 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;
}
}
}

// 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;
Expand All @@ -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) {
Expand All @@ -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;
Expand Down
Loading

0 comments on commit 733d903

Please sign in to comment.