398 lines
17 KiB
C#
398 lines
17 KiB
C#
/*
|
|
* NLayer - A C# MPEG1/2/2.5 audio decoder
|
|
*
|
|
*/
|
|
|
|
using System;
|
|
|
|
namespace NLayer.Decoder
|
|
{
|
|
// Layers I & II are basically identical... Layer II adds sample grouping, per subband allocation schemes, and granules
|
|
// Because of this fact, we can use the same decoder for both
|
|
abstract class LayerIIDecoderBase : LayerDecoderBase
|
|
{
|
|
protected const int SSLIMIT = 12;
|
|
|
|
static protected bool GetCRC(MpegFrame frame, int[] rateTable, int[][] allocLookupTable, bool readScfsiBits, ref uint crc)
|
|
{
|
|
// ugh... we basically have to re-implement the allocation logic here.
|
|
|
|
// keep up with how many active subbands we need to read selection info for
|
|
var scfsiBits = 0;
|
|
|
|
// only read as many subbands as we actually need; pay attention to the intensity stereo subbands
|
|
var subbandCount = rateTable.Length;
|
|
var jsbound = subbandCount;
|
|
if (frame.ChannelMode == MpegChannelMode.JointStereo)
|
|
{
|
|
jsbound = frame.ChannelModeExtension * 4 + 4;
|
|
}
|
|
|
|
// read the full stereo subbands
|
|
var channels = frame.ChannelMode == MpegChannelMode.Mono ? 1 : 2;
|
|
var sb = 0;
|
|
for (; sb < jsbound; sb++)
|
|
{
|
|
var bits = allocLookupTable[rateTable[sb]][0];
|
|
for (int ch = 0; ch < channels; ch++)
|
|
{
|
|
var alloc = frame.ReadBits(bits);
|
|
if (alloc > 0) scfsiBits += 2;
|
|
|
|
MpegFrame.UpdateCRC(alloc, bits, ref crc);
|
|
}
|
|
}
|
|
|
|
// read the intensity stereo subbands
|
|
for (; sb < subbandCount; sb++)
|
|
{
|
|
var bits = allocLookupTable[rateTable[sb]][0];
|
|
|
|
var alloc = frame.ReadBits(bits);
|
|
if (alloc > 0) scfsiBits += channels * 2;
|
|
|
|
MpegFrame.UpdateCRC(alloc, bits, ref crc);
|
|
}
|
|
|
|
// finally, read the scalefac selection bits
|
|
if (readScfsiBits)
|
|
{
|
|
while (scfsiBits >= 2)
|
|
{
|
|
MpegFrame.UpdateCRC(frame.ReadBits(2), 2, ref crc);
|
|
scfsiBits -= 2;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
#region Lookup Tables
|
|
|
|
// this is from the formula: C = 1 / (1 / (1 << (Bits / 2 + Bits % 2 - 1)) + .5f)
|
|
// index by real bits (Bits / 2 + Bits % 2 - 1)
|
|
static readonly float[] _groupedC = { 0, 0, 1.33333333333f, 1.60000000000f, 1.77777777777f };
|
|
|
|
// these are always -0.5
|
|
// index by real bits (Bits / 2 + Bits % 2 - 1)
|
|
static readonly float[] _groupedD = { 0, 0, -0.5f, -0.5f, -0.5f };
|
|
|
|
// this is from the formula: 1 / (1 - (1f / (1 << Bits)))
|
|
// index by bits
|
|
static readonly float[] _C = {
|
|
0.00000000000f,
|
|
0.00000000000f, 1.33333333333f, 1.14285714286f, 1.06666666666f, 1.03225806452f, 1.01587301587f, 1.00787401575f, 1.00392156863f,
|
|
1.00195694716f, 1.00097751711f, 1.00048851979f, 1.00024420024f, 1.00012208522f, 1.00006103888f, 1.00003051851f, 1.00001525902f
|
|
};
|
|
|
|
// this is from the formula: 1f / (1 << Bits - 1) - 1
|
|
// index by bits
|
|
static readonly float[] _D = {
|
|
0.00000000000f - 0f,
|
|
0.00000000000f - 0f, 0.50000000000f - 1f, 0.25000000000f - 1f, 0.12500000000f - 1f, 0.062500000000f - 1f, 0.03125000000f - 1f, 0.01562500000f - 1f, 0.00781250000f - 1f,
|
|
0.00390625000f - 1f, 0.00195312500f - 1f, 0.00097656250f - 1f, 0.00048828125f - 1f, 0.000244140630f - 1f, 0.00012207031f - 1f, 0.00006103516f - 1f, 0.00003051758f - 1f
|
|
};
|
|
|
|
// this is from a (really annoying) formula:
|
|
// x = Math.Pow(4, 1 / ((2 << (idx % 3) + 1) - (idx % 3))) / (1 << (idx / 3))
|
|
// Basically...
|
|
// [0] = Math.Pow(4, 1 / 2), [1] = Math.Pow(4, 1 / 3), [2] = Math.Pow(4, 1 / 6)
|
|
// For every remaining element, calculate (in order): [idx] = [idx - 3] / 2
|
|
static readonly float[] _denormalMultiplier = {
|
|
2.00000000000000f, 1.58740105196820f, 1.25992104989487f, 1.00000000000000f, 0.79370052598410f, 0.62996052494744f, 0.50000000000000f, 0.39685026299205f,
|
|
0.31498026247372f, 0.25000000000000f, 0.19842513149602f, 0.15749013123686f, 0.12500000000000f, 0.09921256574801f, 0.07874506561843f, 0.06250000000000f,
|
|
0.04960628287401f, 0.03937253280921f, 0.03125000000000f, 0.02480314143700f, 0.01968626640461f, 0.01562500000000f, 0.01240157071850f, 0.00984313320230f,
|
|
0.00781250000000f, 0.00620078535925f, 0.00492156660115f, 0.00390625000000f, 0.00310039267963f, 0.00246078330058f, 0.00195312500000f, 0.00155019633981f,
|
|
0.00123039165029f, 0.00097656250000f, 0.00077509816991f, 0.00061519582514f, 0.00048828125000f, 0.00038754908495f, 0.00030759791257f, 0.00024414062500f,
|
|
0.00019377454248f, 0.00015379895629f, 0.00012207031250f, 0.00009688727124f, 0.00007689947814f, 0.00006103515625f, 0.00004844363562f, 0.00003844973907f,
|
|
0.00003051757813f, 0.00002422181781f, 0.00001922486954f, 0.00001525878906f, 0.00001211090890f, 0.00000961243477f, 0.00000762939453f, 0.00000605545445f,
|
|
0.00000480621738f, 0.00000381469727f, 0.00000302772723f, 0.00000240310869f, 0.00000190734863f, 0.00000151386361f, 0.00000120155435f, 0.00000095367432f
|
|
};
|
|
|
|
#endregion
|
|
|
|
int _channels, _jsbound, _granuleCount;
|
|
int[][] _allocLookupTable, _scfsi, _samples;
|
|
int[][][] _scalefac;
|
|
float[] _polyPhaseBuf;
|
|
|
|
int[][] _allocation;
|
|
|
|
protected LayerIIDecoderBase(int[][] allocLookupTable, int granuleCount)
|
|
: base()
|
|
{
|
|
_allocLookupTable = allocLookupTable;
|
|
_granuleCount = granuleCount;
|
|
|
|
_allocation = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
|
|
_scfsi = new int[][] { new int[SBLIMIT], new int[SBLIMIT] };
|
|
_samples = new int[][] { new int[SBLIMIT * SSLIMIT * _granuleCount], new int[SBLIMIT * SSLIMIT * _granuleCount] };
|
|
|
|
// NB: ReadScaleFactors(...) requires all three granules, even in Layer I
|
|
_scalefac = new int[][][] { new int[3][], new int[3][] };
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
_scalefac[0][i] = new int[SBLIMIT];
|
|
_scalefac[1][i] = new int[SBLIMIT];
|
|
}
|
|
|
|
_polyPhaseBuf = new float[SBLIMIT];
|
|
}
|
|
|
|
internal override int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1)
|
|
{
|
|
InitFrame(frame);
|
|
|
|
var rateTable = GetRateTable(frame);
|
|
|
|
ReadAllocation(frame, rateTable);
|
|
|
|
for (int i = 0; i < _scfsi[0].Length; i++)
|
|
{
|
|
// Since Layer II has to know which subbands have energy, we use the "Layer I valid" selection to mark that energy is present.
|
|
// That way Layer I doesn't have to do anything else.
|
|
_scfsi[0][i] = _allocation[0][i] != 0 ? 2 : -1;
|
|
_scfsi[1][i] = _allocation[1][i] != 0 ? 2 : -1;
|
|
}
|
|
ReadScaleFactorSelection(frame, _scfsi, _channels);
|
|
|
|
ReadScaleFactors(frame);
|
|
|
|
ReadSamples(frame);
|
|
|
|
return DecodeSamples(ch0, ch1);
|
|
}
|
|
|
|
// this just reads the channel mode and set a few flags
|
|
void InitFrame(IMpegFrame frame)
|
|
{
|
|
switch (frame.ChannelMode)
|
|
{
|
|
case MpegChannelMode.Mono:
|
|
_channels = 1;
|
|
_jsbound = SBLIMIT;
|
|
break;
|
|
case MpegChannelMode.JointStereo:
|
|
_channels = 2;
|
|
_jsbound = frame.ChannelModeExtension * 4 + 4;
|
|
break;
|
|
default:
|
|
_channels = 2;
|
|
_jsbound = SBLIMIT;
|
|
break;
|
|
}
|
|
}
|
|
|
|
abstract protected int[] GetRateTable(IMpegFrame frame);
|
|
|
|
void ReadAllocation(IMpegFrame frame, int[] rateTable)
|
|
{
|
|
var _subBandCount = rateTable.Length;
|
|
if (_jsbound > _subBandCount) _jsbound = _subBandCount;
|
|
|
|
Array.Clear(_allocation[0], 0, SBLIMIT);
|
|
Array.Clear(_allocation[1], 0, SBLIMIT);
|
|
|
|
int sb = 0;
|
|
for (; sb < _jsbound; sb++)
|
|
{
|
|
var table = _allocLookupTable[rateTable[sb]];
|
|
var bits = table[0];
|
|
for (int ch = 0; ch < _channels; ch++)
|
|
{
|
|
_allocation[ch][sb] = table[frame.ReadBits(bits) + 1];
|
|
}
|
|
}
|
|
for (; sb < _subBandCount; sb++)
|
|
{
|
|
var table = _allocLookupTable[rateTable[sb]];
|
|
_allocation[0][sb] = _allocation[1][sb] = table[frame.ReadBits(table[0]) + 1];
|
|
}
|
|
}
|
|
|
|
abstract protected void ReadScaleFactorSelection(IMpegFrame frame, int[][] scfsi, int channels);
|
|
|
|
void ReadScaleFactors(IMpegFrame frame)
|
|
{
|
|
for (int sb = 0; sb < SBLIMIT; sb++)
|
|
{
|
|
for (int ch = 0; ch < _channels; ch++)
|
|
{
|
|
switch (_scfsi[ch][sb])
|
|
{
|
|
case 0:
|
|
// all three
|
|
_scalefac[ch][0][sb] = frame.ReadBits(6);
|
|
_scalefac[ch][1][sb] = frame.ReadBits(6);
|
|
_scalefac[ch][2][sb] = frame.ReadBits(6);
|
|
break;
|
|
case 1:
|
|
// only two (2 = 1)
|
|
_scalefac[ch][0][sb] =
|
|
_scalefac[ch][1][sb] = frame.ReadBits(6);
|
|
_scalefac[ch][2][sb] = frame.ReadBits(6);
|
|
break;
|
|
case 2:
|
|
// only one (3 = 2 = 1)
|
|
_scalefac[ch][0][sb] =
|
|
_scalefac[ch][1][sb] =
|
|
_scalefac[ch][2][sb] = frame.ReadBits(6);
|
|
break;
|
|
case 3:
|
|
// only two (3 = 2)
|
|
_scalefac[ch][0][sb] = frame.ReadBits(6);
|
|
_scalefac[ch][1][sb] =
|
|
_scalefac[ch][2][sb] = frame.ReadBits(6);
|
|
break;
|
|
default:
|
|
// none
|
|
_scalefac[ch][0][sb] = 63;
|
|
_scalefac[ch][1][sb] = 63;
|
|
_scalefac[ch][2][sb] = 63;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void ReadSamples(IMpegFrame frame)
|
|
{
|
|
// load in all the data for this frame (1152 samples in this case)
|
|
// NB: we flatten these into output order
|
|
for (int ss = 0, idx = 0; ss < SSLIMIT; ss++, idx += SBLIMIT * (_granuleCount - 1))
|
|
{
|
|
for (int sb = 0; sb < SBLIMIT; sb++, idx++)
|
|
{
|
|
for (int ch = 0; ch < _channels; ch++)
|
|
{
|
|
if (ch == 0 || sb < _jsbound)
|
|
{
|
|
var alloc = _allocation[ch][sb];
|
|
if (alloc != 0)
|
|
{
|
|
if (alloc < 0)
|
|
{
|
|
// grouping (Layer II only, so we don't have to play with the granule count)
|
|
var val = frame.ReadBits(-alloc);
|
|
var levels = (1 << (-alloc / 2 + -alloc % 2 - 1)) + 1;
|
|
|
|
_samples[ch][idx] = val % levels;
|
|
val /= levels;
|
|
_samples[ch][idx + SBLIMIT] = val % levels;
|
|
_samples[ch][idx + SBLIMIT * 2] = val / levels;
|
|
}
|
|
else
|
|
{
|
|
// non-grouping
|
|
for (int gr = 0; gr < _granuleCount; gr++)
|
|
{
|
|
_samples[ch][idx + SBLIMIT * gr] = frame.ReadBits(alloc);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// no energy... zero out the samples
|
|
for (int gr = 0; gr < _granuleCount; gr++)
|
|
{
|
|
_samples[ch][idx + SBLIMIT * gr] = 0;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// copy chan 0 to chan 1
|
|
for (int gr = 0; gr < _granuleCount; gr++)
|
|
{
|
|
_samples[1][idx + SBLIMIT * gr] = _samples[0][idx + SBLIMIT * gr];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
int DecodeSamples(float[] ch0, float[] ch1)
|
|
{
|
|
// do our stereo mode setup
|
|
var chanBufs = new float[2][];
|
|
var startChannel = 0;
|
|
var endChannel = _channels - 1;
|
|
if (_channels == 1 || StereoMode == StereoMode.LeftOnly)
|
|
{
|
|
chanBufs[0] = ch0;
|
|
endChannel = 0;
|
|
}
|
|
else if (StereoMode == StereoMode.RightOnly)
|
|
{
|
|
chanBufs[1] = ch0; // this is correct... if there's only a single channel output, it goes in channel 0's buffer
|
|
startChannel = 1;
|
|
}
|
|
else // MpegStereoMode.Both or StereoMode.DownmixToMono
|
|
{
|
|
chanBufs[0] = ch0;
|
|
chanBufs[1] = ch1;
|
|
}
|
|
|
|
int idx = 0;
|
|
for (int ch = startChannel; ch <= endChannel; ch++)
|
|
{
|
|
idx = 0;
|
|
for (int gr = 0; gr < _granuleCount; gr++)
|
|
{
|
|
for (int ss = 0; ss < SSLIMIT; ss++)
|
|
{
|
|
for (int sb = 0; sb < SBLIMIT; sb++, idx++)
|
|
{
|
|
// do the dequant and the denorm; output to _polyPhaseBuf
|
|
// NB: Layers I & II use the same algorithm here... Grouping changes the bit counts, but doesn't change the algo
|
|
// - Up to 65534 possible values (65535 does not appear to be usable)
|
|
// - All values can be handled with 16-bit logic as long as the correct C and D constants are used
|
|
// - Make sure to normalize each sample to 16 bits!
|
|
|
|
var alloc = _allocation[ch][sb];
|
|
if (alloc != 0)
|
|
{
|
|
float[] c, d;
|
|
if (alloc < 0)
|
|
{
|
|
alloc = -alloc / 2 + -alloc % 2 - 1;
|
|
c = _groupedC;
|
|
d = _groupedD;
|
|
}
|
|
else
|
|
{
|
|
c = _C;
|
|
d = _D;
|
|
}
|
|
|
|
// read sample; normalize, scale & center to [-0.999984741f..0.999984741f]; apply scalefactor
|
|
_polyPhaseBuf[sb] = c[alloc] * ((_samples[ch][idx] << (16 - alloc)) / 32768f + d[alloc]) * _denormalMultiplier[_scalefac[ch][gr][sb]];
|
|
}
|
|
else
|
|
{
|
|
// no transmitted energy...
|
|
_polyPhaseBuf[sb] = 0f;
|
|
}
|
|
}
|
|
|
|
// do the polyphase output for this channel, section, and granule
|
|
base.InversePolyPhase(ch, _polyPhaseBuf);
|
|
Array.Copy(_polyPhaseBuf, 0, chanBufs[ch], idx - SBLIMIT, SBLIMIT);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (_channels == 2 && StereoMode == NLayer.StereoMode.DownmixToMono)
|
|
{
|
|
for (int i = 0; i < idx; i++)
|
|
{
|
|
ch0[i] = (ch0[i] + ch1[i]) / 2;
|
|
}
|
|
}
|
|
|
|
return idx;
|
|
}
|
|
}
|
|
}
|