1951 lines
86 KiB
C#
1951 lines
86 KiB
C#
/*
|
|
* NLayer - A C# MPEG1/2/2.5 audio decoder
|
|
*
|
|
* Portions of this file are courtesy Fluendo, S.A. They are dual licensed as Ms-PL
|
|
* and under the following license:
|
|
*
|
|
* Copyright <2005-2012> Fluendo S.A.
|
|
*
|
|
* Unless otherwise indicated, Source Code is licensed under MIT license.
|
|
* See further explanation attached in License Statement (distributed in the file
|
|
* LICENSE).
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
* this software and associated documentation files (the "Software"), to deal in
|
|
* the Software without restriction, including without limitation the rights to
|
|
* use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
* of the Software, and to permit persons to whom the Software is furnished to do
|
|
* so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in all
|
|
* copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*
|
|
*/
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace NLayer.Decoder
|
|
{
|
|
/// <summary>
|
|
/// Class Implementing Layer 3 Decoder.
|
|
/// </summary>
|
|
sealed class LayerIIIDecoder : LayerDecoderBase
|
|
{
|
|
const int SSLIMIT = 18;
|
|
|
|
#region Child Classes
|
|
|
|
// This class is based on the Fluendo hybrid logic.
|
|
class HybridMDCT
|
|
{
|
|
const float PI = (float)Math.PI;
|
|
|
|
static float[][] _swin;
|
|
|
|
static HybridMDCT()
|
|
{
|
|
_swin = new float[][] { new float[36], new float[36], new float[36], new float[36] };
|
|
|
|
int i;
|
|
|
|
/* type 0 */
|
|
for (i = 0; i < 36; i++)
|
|
_swin[0][i] = (float)Math.Sin(PI / 36 * (i + 0.5));
|
|
|
|
/* type 1 */
|
|
for (i = 0; i < 18; i++)
|
|
_swin[1][i] = (float)Math.Sin(PI / 36 * (i + 0.5));
|
|
for (i = 18; i < 24; i++)
|
|
_swin[1][i] = 1.0f;
|
|
for (i = 24; i < 30; i++)
|
|
_swin[1][i] = (float)Math.Sin(PI / 12 * (i + 0.5 - 18));
|
|
for (i = 30; i < 36; i++)
|
|
_swin[1][i] = 0.0f;
|
|
|
|
/* type 3 */
|
|
for (i = 0; i < 6; i++)
|
|
_swin[3][i] = 0.0f;
|
|
for (i = 6; i < 12; i++)
|
|
_swin[3][i] = (float)Math.Sin(PI / 12 * (i + 0.5 - 6));
|
|
for (i = 12; i < 18; i++)
|
|
_swin[3][i] = 1.0f;
|
|
for (i = 18; i < 36; i++)
|
|
_swin[3][i] = (float)Math.Sin(PI / 36 * (i + 0.5));
|
|
|
|
/* type 2 */
|
|
for (i = 0; i < 12; i++)
|
|
_swin[2][i] = (float)Math.Sin(PI / 12 * (i + 0.5));
|
|
for (i = 12; i < 36; i++)
|
|
_swin[2][i] = 0.0f;
|
|
}
|
|
|
|
#region Tables
|
|
|
|
static float[] icos72_table = {
|
|
5.004763425816599609063928255636710673570632934570312500000000e-01f,
|
|
5.019099187716736798492433990759309381246566772460937500000000e-01f,
|
|
5.043144802900764167574720886477734893560409545898437500000000e-01f,
|
|
5.077133059428725614381505693017970770597457885742187500000000e-01f,
|
|
5.121397571572545714957414020318537950515747070312500000000000e-01f,
|
|
5.176380902050414789528076653368771076202392578125000000000000e-01f,
|
|
5.242645625704053236049162478593643754720687866210937500000000e-01f,
|
|
5.320888862379560269033618169487453997135162353515625000000000e-01f,
|
|
5.411961001461970122150546558259520679712295532226562500000000e-01f,
|
|
5.516889594812458552652856269560288637876510620117187500000000e-01f,
|
|
5.636909734331712051869089918909594416618347167968750000000000e-01f,
|
|
5.773502691896257310588680411456152796745300292968750000000000e-01f,
|
|
5.928445237170802961657045671017840504646301269531250000000000e-01f,
|
|
6.103872943807280293526673631276935338973999023437500000000000e-01f,
|
|
6.302362070051321651931175438221544027328491210937500000000000e-01f,
|
|
6.527036446661392821155800447741057723760604858398437500000000e-01f,
|
|
6.781708524546284921896699415810871869325637817382812500000000e-01f,
|
|
7.071067811865474617150084668537601828575134277343750000000000e-01f,
|
|
7.400936164611303658134033867099788039922714233398437500000000e-01f,
|
|
7.778619134302061643992942663317080587148666381835937500000000e-01f,
|
|
8.213398158522907666068135768000502139329910278320312500000000e-01f,
|
|
8.717233978105488612087015098950359970331192016601562500000000e-01f,
|
|
9.305794983517888807611484480730723589658737182617187500000000e-01f,
|
|
9.999999999999997779553950749686919152736663818359375000000000e-01f,
|
|
1.082840285100100219395358180918265134096145629882812500000000e+00f,
|
|
1.183100791576249255498964885191526263952255249023437500000000e+00f,
|
|
1.306562964876376353728915091778617352247238159179687500000000e+00f,
|
|
1.461902200081543146126250576344318687915802001953125000000000e+00f,
|
|
1.662754761711521034328598034335300326347351074218750000000000e+00f,
|
|
1.931851652578135070115195048856548964977264404296875000000000e+00f,
|
|
2.310113157672649020213384574162773787975311279296875000000000e+00f,
|
|
2.879385241571815523542454684502445161342620849609375000000000e+00f,
|
|
3.830648787770197127855453800293616950511932373046875000000000e+00f,
|
|
5.736856622834929808618653623852878808975219726562500000000000e+00f,
|
|
1.146279281302667207853573927422985434532165527343750000000000e+01f
|
|
};
|
|
|
|
#endregion
|
|
|
|
List<float[]> _prevBlock;
|
|
List<float[]> _nextBlock;
|
|
|
|
internal HybridMDCT()
|
|
{
|
|
_prevBlock = new List<float[]>();
|
|
_nextBlock = new List<float[]>();
|
|
}
|
|
|
|
internal void Reset()
|
|
{
|
|
_prevBlock.Clear();
|
|
_nextBlock.Clear();
|
|
}
|
|
|
|
void GetPrevBlock(int channel, out float[] prevBlock, out float[] nextBlock)
|
|
{
|
|
while (_prevBlock.Count <= channel)
|
|
{
|
|
_prevBlock.Add(new float[SSLIMIT * SBLIMIT]);
|
|
}
|
|
while (_nextBlock.Count <= channel)
|
|
{
|
|
_nextBlock.Add(new float[SSLIMIT * SBLIMIT]);
|
|
}
|
|
prevBlock = _prevBlock[channel];
|
|
nextBlock = _nextBlock[channel];
|
|
|
|
// now swap them (see Apply(...) below)
|
|
_nextBlock[channel] = prevBlock;
|
|
_prevBlock[channel] = nextBlock;
|
|
}
|
|
|
|
internal void Apply(float[] fsIn, int channel, int blockType, bool doMixed)
|
|
{
|
|
// get the previous & next blocks so we can overlap correctly
|
|
// NB: we swap each pass so we can add the previous block in a single pass
|
|
float[] prevblck, nextblck;
|
|
GetPrevBlock(channel, out prevblck, out nextblck);
|
|
|
|
// now we have a few options for processing blocks...
|
|
int start = 0;
|
|
if (doMixed)
|
|
{
|
|
// a mixed block always has the first two subbands as blocktype 0
|
|
LongImpl(fsIn, 0, 2, nextblck, 0);
|
|
start = 2;
|
|
}
|
|
|
|
if (blockType == 2)
|
|
{
|
|
// this is the only place we care about short blocks
|
|
ShortImpl(fsIn, start, nextblck);
|
|
}
|
|
else
|
|
{
|
|
LongImpl(fsIn, start, SBLIMIT, nextblck, blockType);
|
|
}
|
|
|
|
// overlap
|
|
for (int i = 0; i < SSLIMIT * SBLIMIT; i++)
|
|
{
|
|
fsIn[i] += prevblck[i];
|
|
}
|
|
}
|
|
|
|
float[] _imdctTemp = new float[SSLIMIT];
|
|
float[] _imdctResult = new float[SSLIMIT * 2];
|
|
|
|
void LongImpl(float[] fsIn, int sbStart, int sbLimit, float[] nextblck, int blockType)
|
|
{
|
|
for (int sb = sbStart, ofs = sbStart * SSLIMIT; sb < sbLimit; sb++)
|
|
{
|
|
// IMDCT
|
|
Array.Copy(fsIn, ofs, _imdctTemp, 0, SSLIMIT);
|
|
LongIMDCT(_imdctTemp, _imdctResult);
|
|
|
|
// window
|
|
var win = _swin[blockType];
|
|
int i = 0;
|
|
for (; i < SSLIMIT; i++)
|
|
{
|
|
fsIn[ofs++] = _imdctResult[i] * win[i];
|
|
}
|
|
ofs -= 18;
|
|
for (; i < SSLIMIT * 2; i++)
|
|
{
|
|
nextblck[ofs++] = _imdctResult[i] * win[i];
|
|
}
|
|
}
|
|
}
|
|
|
|
static void LongIMDCT(float[] invec, float[] outvec)
|
|
{
|
|
int i;
|
|
float[] H = new float[17], h = new float[18], even = new float[9], odd = new float[9], even_idct = new float[9], odd_idct = new float[9];
|
|
|
|
for (i = 0; i < 17; i++)
|
|
H[i] = invec[i] + invec[i + 1];
|
|
|
|
even[0] = invec[0];
|
|
odd[0] = H[0];
|
|
var idx = 0;
|
|
for (i = 1; i < 9; i++, idx += 2)
|
|
{
|
|
even[i] = H[idx + 1];
|
|
odd[i] = H[idx] + H[idx + 2];
|
|
}
|
|
|
|
imdct_9pt(even, even_idct);
|
|
imdct_9pt(odd, odd_idct);
|
|
|
|
for (i = 0; i < 9; i++)
|
|
{
|
|
odd_idct[i] *= ICOS36_A(i);
|
|
h[i] = (even_idct[i] + odd_idct[i]) * ICOS72_A(i);
|
|
}
|
|
for ( /* i = 9 */ ; i < 18; i++)
|
|
{
|
|
h[i] = (even_idct[17 - i] - odd_idct[17 - i]) * ICOS72_A(i);
|
|
}
|
|
|
|
/* Rearrange the 18 values from the IDCT to the output vector */
|
|
outvec[0] = h[9];
|
|
outvec[1] = h[10];
|
|
outvec[2] = h[11];
|
|
outvec[3] = h[12];
|
|
outvec[4] = h[13];
|
|
outvec[5] = h[14];
|
|
outvec[6] = h[15];
|
|
outvec[7] = h[16];
|
|
outvec[8] = h[17];
|
|
|
|
outvec[9] = -h[17];
|
|
outvec[10] = -h[16];
|
|
outvec[11] = -h[15];
|
|
outvec[12] = -h[14];
|
|
outvec[13] = -h[13];
|
|
outvec[14] = -h[12];
|
|
outvec[15] = -h[11];
|
|
outvec[16] = -h[10];
|
|
outvec[17] = -h[9];
|
|
|
|
outvec[35] = outvec[18] = -h[8];
|
|
outvec[34] = outvec[19] = -h[7];
|
|
outvec[33] = outvec[20] = -h[6];
|
|
outvec[32] = outvec[21] = -h[5];
|
|
outvec[31] = outvec[22] = -h[4];
|
|
outvec[30] = outvec[23] = -h[3];
|
|
outvec[29] = outvec[24] = -h[2];
|
|
outvec[28] = outvec[25] = -h[1];
|
|
outvec[27] = outvec[26] = -h[0];
|
|
}
|
|
|
|
static float ICOS72_A(int i)
|
|
{
|
|
return icos72_table[2 * i];
|
|
}
|
|
|
|
static float ICOS36_A(int i)
|
|
{
|
|
return icos72_table[4 * i + 1];
|
|
}
|
|
|
|
static void imdct_9pt(float[] invec, float[] outvec)
|
|
{
|
|
int i;
|
|
float[] even_idct = new float[5], odd_idct = new float[4];
|
|
float t0, t1, t2;
|
|
|
|
/* BEGIN 5 Point IMDCT */
|
|
t0 = invec[6] / 2.0f + invec[0];
|
|
t1 = invec[0] - invec[6];
|
|
t2 = invec[2] - invec[4] - invec[8];
|
|
|
|
even_idct[0] = t0 + invec[2] * 0.939692621f
|
|
+ invec[4] * 0.766044443f + invec[8] * 0.173648178f;
|
|
|
|
even_idct[1] = t2 / 2.0f + t1;
|
|
even_idct[2] = t0 - invec[2] * 0.173648178f
|
|
- invec[4] * 0.939692621f + invec[8] * 0.766044443f;
|
|
|
|
even_idct[3] = t0 - invec[2] * 0.766044443f
|
|
+ invec[4] * 0.173648178f - invec[8] * 0.939692621f;
|
|
|
|
even_idct[4] = t1 - t2;
|
|
/* END 5 Point IMDCT */
|
|
|
|
/* BEGIN 4 Point IMDCT */
|
|
{
|
|
float odd1, odd2;
|
|
odd1 = invec[1] + invec[3];
|
|
odd2 = invec[3] + invec[5];
|
|
t0 = (invec[5] + invec[7]) * 0.5f + invec[1];
|
|
|
|
odd_idct[0] = t0 + odd1 * 0.939692621f + odd2 * 0.766044443f;
|
|
odd_idct[1] = (invec[1] - invec[5]) * 1.5f - invec[7];
|
|
odd_idct[2] = t0 - odd1 * 0.173648178f - odd2 * 0.939692621f;
|
|
odd_idct[3] = t0 - odd1 * 0.766044443f + odd2 * 0.173648178f;
|
|
}
|
|
/* END 4 Point IMDCT */
|
|
|
|
/* Adjust for non power of 2 IDCT */
|
|
odd_idct[0] += invec[7] * 0.173648178f;
|
|
odd_idct[1] -= invec[7] * 0.5f;
|
|
odd_idct[2] += invec[7] * 0.766044443f;
|
|
odd_idct[3] -= invec[7] * 0.939692621f;
|
|
|
|
/* Post-Twiddle */
|
|
odd_idct[0] *= 0.5f / 0.984807753f;
|
|
odd_idct[1] *= 0.5f / 0.866025404f;
|
|
odd_idct[2] *= 0.5f / 0.64278761f;
|
|
odd_idct[3] *= 0.5f / 0.342020143f;
|
|
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
outvec[i] = even_idct[i] + odd_idct[i];
|
|
}
|
|
outvec[4] = even_idct[4];
|
|
/* Mirror into the other half of the vector */
|
|
for (i = 5; i < 9; i++)
|
|
{
|
|
outvec[i] = even_idct[8 - i] - odd_idct[8 - i];
|
|
}
|
|
}
|
|
|
|
void ShortImpl(float[] fsIn, int sbStart, float[] nextblck)
|
|
{
|
|
var win = _swin[2];
|
|
|
|
for (int sb = sbStart, ofs = sbStart * SSLIMIT; sb < SBLIMIT; sb++, ofs += SSLIMIT)
|
|
{
|
|
// rearrange vectors
|
|
for (int i = 0, tmpptr = 0; i < 3; i++)
|
|
{
|
|
var v = ofs + i;
|
|
for (int j = 0; j < 6; j++)
|
|
{
|
|
_imdctTemp[tmpptr + j] = fsIn[v];
|
|
v += 3;
|
|
}
|
|
tmpptr += 6;
|
|
}
|
|
|
|
// short blocks are fun... 3 separate IMDCT's with overlap in two different buffers
|
|
|
|
Array.Clear(fsIn, ofs, 6);
|
|
|
|
// do the first 6 samples
|
|
ShortIMDCT(_imdctTemp, 0, _imdctResult);
|
|
Array.Copy(_imdctResult, 0, fsIn, ofs + 6, 12);
|
|
|
|
// now the next 6
|
|
ShortIMDCT(_imdctTemp, 6, _imdctResult);
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
// add the first half to tsOut
|
|
fsIn[ofs + i + 12] += _imdctResult[i];
|
|
}
|
|
Array.Copy(_imdctResult, 6, nextblck, ofs, 6);
|
|
|
|
// now the final 6
|
|
ShortIMDCT(_imdctTemp, 12, _imdctResult);
|
|
for (int i = 0; i < 6; i++)
|
|
{
|
|
// add the first half to nextblck
|
|
nextblck[ofs + i] += _imdctResult[i];
|
|
}
|
|
Array.Copy(_imdctResult, 6, nextblck, ofs + 6, 6);
|
|
Array.Clear(nextblck, ofs + 12, 6);
|
|
}
|
|
}
|
|
|
|
const float sqrt32 = 0.8660254037844385965883020617184229195117950439453125f;
|
|
|
|
static void ShortIMDCT(float[] invec, int inIdx, float[] outvec)
|
|
{
|
|
int i;
|
|
float[] H = new float[6], h = new float[6], even_idct = new float[3], odd_idct = new float[3];
|
|
float t0, t1, t2;
|
|
|
|
/* Preprocess the input to the two 3-point IDCT's */
|
|
var idx = inIdx;
|
|
for (i = 1; i < 6; i++)
|
|
{
|
|
H[i] = invec[idx];
|
|
H[i] += invec[++idx];
|
|
}
|
|
|
|
/* 3-point IMDCT */
|
|
t0 = H[4] / 2.0f + invec[inIdx];
|
|
t1 = H[2] * sqrt32;
|
|
even_idct[0] = t0 + t1;
|
|
even_idct[1] = invec[inIdx] - H[4];
|
|
even_idct[2] = t0 - t1;
|
|
/* END 3-point IMDCT */
|
|
|
|
/* 3-point IMDCT */
|
|
t2 = H[3] + H[5];
|
|
|
|
t0 = (t2) / 2.0f + H[1];
|
|
t1 = (H[1] + H[3]) * sqrt32;
|
|
odd_idct[0] = t0 + t1;
|
|
odd_idct[1] = H[1] - t2;
|
|
odd_idct[2] = t0 - t1;
|
|
/* END 3-point IMDCT */
|
|
|
|
/* Post-Twiddle */
|
|
odd_idct[0] *= 0.51763809f;
|
|
odd_idct[1] *= 0.707106781f;
|
|
odd_idct[2] *= 1.931851653f;
|
|
|
|
h[0] = (even_idct[0] + odd_idct[0]) * 0.50431448f;
|
|
h[1] = (even_idct[1] + odd_idct[1]) * 0.5411961f;
|
|
h[2] = (even_idct[2] + odd_idct[2]) * 0.630236207f;
|
|
|
|
h[3] = (even_idct[2] - odd_idct[2]) * 0.821339816f;
|
|
h[4] = (even_idct[1] - odd_idct[1]) * 1.306562965f;
|
|
h[5] = (even_idct[0] - odd_idct[0]) * 3.830648788f;
|
|
|
|
/* Rearrange the 6 values from the IDCT to the output vector */
|
|
outvec[0] = h[3] * _swin[2][0];
|
|
outvec[1] = h[4] * _swin[2][1];
|
|
outvec[2] = h[5] * _swin[2][2];
|
|
outvec[3] = -h[5] * _swin[2][3];
|
|
outvec[4] = -h[4] * _swin[2][4];
|
|
outvec[5] = -h[3] * _swin[2][5];
|
|
outvec[6] = -h[2] * _swin[2][6];
|
|
outvec[7] = -h[1] * _swin[2][7];
|
|
outvec[8] = -h[0] * _swin[2][8];
|
|
outvec[9] = -h[0] * _swin[2][9];
|
|
outvec[10] = -h[1] * _swin[2][10];
|
|
outvec[11] = -h[2] * _swin[2][11];
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
static internal bool GetCRC(MpegFrame frame, ref uint crc)
|
|
{
|
|
var cnt = frame.GetSideDataSize();
|
|
while (--cnt >= 0)
|
|
{
|
|
MpegFrame.UpdateCRC(frame.ReadBits(8), 8, ref crc);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
HybridMDCT _hybrid = new HybridMDCT();
|
|
BitReservoir _bitRes = new BitReservoir();
|
|
|
|
internal LayerIIIDecoder()
|
|
{
|
|
_tableSelect = new int[][][]
|
|
{
|
|
new int[][] { new int[3], new int[3] },
|
|
new int[][] { new int[3], new int[3] },
|
|
};
|
|
|
|
_subblockGain = new float[][][]
|
|
{
|
|
new float[][] { new float[3], new float[3] },
|
|
new float[][] { new float[3], new float[3] },
|
|
};
|
|
}
|
|
|
|
internal override int DecodeFrame(IMpegFrame frame, float[] ch0, float[] ch1)
|
|
{
|
|
// load the frame information
|
|
ReadSideInfo(frame);
|
|
|
|
// load the frame's main data
|
|
if (!_bitRes.AddBits(frame, _mainDataBegin))
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// prep the reusable tables
|
|
PrepTables(frame);
|
|
|
|
// do our stereo mode setup
|
|
var chanBufs = new float[2][];
|
|
var startChannel = 0;
|
|
var endChannel = _channels - 1;
|
|
if (_channels == 1 || StereoMode == StereoMode.LeftOnly || StereoMode == StereoMode.DownmixToMono)
|
|
{
|
|
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
|
|
{
|
|
chanBufs[0] = ch0;
|
|
chanBufs[1] = ch1;
|
|
}
|
|
|
|
// get the granule count
|
|
int granules;
|
|
if (frame.Version == MpegVersion.Version1)
|
|
{
|
|
granules = 2;
|
|
}
|
|
else
|
|
{
|
|
granules = 1;
|
|
}
|
|
|
|
// decode the audio data
|
|
int offset = 0;
|
|
for (var gr = 0; gr < granules; gr++)
|
|
{
|
|
for (var ch = 0; ch < _channels; ch++)
|
|
{
|
|
// read scale factors
|
|
int sfbits;
|
|
if (frame.Version == MpegVersion.Version1)
|
|
{
|
|
sfbits = ReadScalefactors(gr, ch);
|
|
}
|
|
else
|
|
{
|
|
sfbits = ReadLsfScalefactors(gr, ch, frame.ChannelModeExtension);
|
|
}
|
|
|
|
// huffman & dequant
|
|
ReadSamples(sfbits, gr, ch);
|
|
}
|
|
|
|
// stereo processing
|
|
Stereo(frame.ChannelMode, frame.ChannelModeExtension, gr, frame.Version != MpegVersion.Version1);
|
|
|
|
for (int ch = startChannel; ch <= endChannel; ch++)
|
|
{
|
|
// pull some values so we don't have to index them again later
|
|
var buf = _samples[ch];
|
|
var blockType = _blockType[gr][ch];
|
|
var blockSplit = _blockSplitFlag[gr][ch];
|
|
var mixedBlock = _mixedBlockFlag[gr][ch];
|
|
|
|
// do the short/long/mixed logic here so it's only done once per channel per granule
|
|
if (blockSplit && blockType == 2)
|
|
{
|
|
if (mixedBlock)
|
|
{
|
|
// reorder & antialias mixed blocks
|
|
Reorder(buf, true);
|
|
AntiAlias(buf, true);
|
|
}
|
|
else
|
|
{
|
|
// reorder short blocks
|
|
Reorder(buf, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// antialias long blocks
|
|
AntiAlias(buf, false);
|
|
}
|
|
|
|
// hybrid processing
|
|
_hybrid.Apply(buf, ch, blockType, blockSplit && mixedBlock);
|
|
|
|
// frequency inversion
|
|
FrequencyInversion(buf);
|
|
|
|
// inverse polyphase
|
|
InversePolyphase(buf, ch, offset, chanBufs[ch]);
|
|
}
|
|
|
|
offset += SBLIMIT * SSLIMIT;
|
|
}
|
|
|
|
return offset;
|
|
}
|
|
|
|
internal override void ResetForSeek()
|
|
{
|
|
base.ResetForSeek();
|
|
|
|
_hybrid.Reset();
|
|
|
|
_bitRes.Reset();
|
|
}
|
|
|
|
#region Side Info
|
|
|
|
#region Variables
|
|
|
|
int _channels, _privBits, _mainDataBegin;
|
|
|
|
int[][] _scfsi = { new int[4], new int[4] }; // ch, scfsi_band
|
|
int[][] _part23Length = { new int[2], new int[2] }; // gr, ch
|
|
int[][] _bigValues = { new int[2], new int[2] }; // gr, ch
|
|
float[][] _globalGain = { new float[2], new float[2] }; // gr, ch
|
|
int[][] _scalefacCompress = { new int[2], new int[2] }; // gr, ch
|
|
bool[][] _blockSplitFlag = { new bool[2], new bool[2] }; // gr, ch
|
|
bool[][] _mixedBlockFlag = { new bool[2], new bool[2] }; // gr, ch
|
|
int[][] _blockType = { new int[2], new int[2] }; // gr, ch
|
|
int[][][] _tableSelect; // gr, ch, region
|
|
float[][][] _subblockGain; // gr, ch, window
|
|
int[][] _regionAddress1 = { new int[2], new int[2] }; // gr, ch
|
|
int[][] _regionAddress2 = { new int[2], new int[2] }; // gr, ch
|
|
int[][] _preflag = { new int[2], new int[2] }; // gr, ch
|
|
float[][] _scalefacScale = { new float[2], new float[2] }; // gr, ch
|
|
int[][] _count1TableSelect = { new int[2], new int[2] }; // gr, ch
|
|
|
|
static float[] GAIN_TAB =
|
|
{
|
|
1.57009245868378E-16f, 1.86716512307887E-16f, 2.22044604925031E-16f, 2.64057024024816E-16f, 3.14018491736756E-16f, 3.73433024615774E-16f, 4.44089209850063E-16f, 5.28114048049630E-16f,
|
|
6.28036983473509E-16f, 7.46866049231544E-16f, 8.88178419700125E-16f, 1.05622809609926E-15f, 1.25607396694702E-15f, 1.49373209846309E-15f, 1.77635683940025E-15f, 2.11245619219853E-15f,
|
|
2.51214793389404E-15f, 2.98746419692619E-15f, 3.55271367880050E-15f, 4.22491238439706E-15f, 5.02429586778810E-15f, 5.97492839385238E-15f, 7.10542735760100E-15f, 8.44982476879408E-15f,
|
|
1.00485917355761E-14f, 1.19498567877047E-14f, 1.42108547152020E-14f, 1.68996495375882E-14f, 2.00971834711523E-14f, 2.38997135754094E-14f, 2.84217094304040E-14f, 3.37992990751764E-14f,
|
|
4.01943669423047E-14f, 4.77994271508190E-14f, 5.68434188608080E-14f, 6.75985981503528E-14f, 8.03887338846093E-14f, 9.55988543016378E-14f, 1.13686837721616E-13f, 1.35197196300706E-13f,
|
|
1.60777467769219E-13f, 1.91197708603275E-13f, 2.27373675443232E-13f, 2.70394392601411E-13f, 3.21554935538437E-13f, 3.82395417206551E-13f, 4.54747350886464E-13f, 5.40788785202823E-13f,
|
|
6.43109871076876E-13f, 7.64790834413101E-13f, 9.09494701772928E-13f, 1.08157757040564E-12f, 1.28621974215375E-12f, 1.52958166882621E-12f, 1.81898940354586E-12f, 2.16315514081129E-12f,
|
|
2.57243948430750E-12f, 3.05916333765241E-12f, 3.63797880709171E-12f, 4.32631028162258E-12f, 5.14487896861500E-12f, 6.11832667530482E-12f, 7.27595761418343E-12f, 8.65262056324518E-12f,
|
|
1.02897579372300E-11f, 1.22366533506096E-11f, 1.45519152283669E-11f, 1.73052411264903E-11f, 2.05795158744600E-11f, 2.44733067012193E-11f, 2.91038304567337E-11f, 3.46104822529806E-11f,
|
|
4.11590317489199E-11f, 4.89466134024385E-11f, 5.82076609134674E-11f, 6.92209645059613E-11f, 8.23180634978400E-11f, 9.78932268048772E-11f, 1.16415321826935E-10f, 1.38441929011922E-10f,
|
|
1.64636126995680E-10f, 1.95786453609754E-10f, 2.32830643653870E-10f, 2.76883858023845E-10f, 3.29272253991360E-10f, 3.91572907219509E-10f, 4.65661287307739E-10f, 5.53767716047690E-10f,
|
|
6.58544507982719E-10f, 7.83145814439016E-10f, 9.31322574615479E-10f, 1.10753543209538E-09f, 1.31708901596544E-09f, 1.56629162887804E-09f, 1.86264514923096E-09f, 2.21507086419076E-09f,
|
|
2.63417803193088E-09f, 3.13258325775607E-09f, 3.72529029846191E-09f, 4.43014172838152E-09f, 5.26835606386176E-09f, 6.26516651551212E-09f, 7.45058059692383E-09f, 8.86028345676304E-09f,
|
|
1.05367121277235E-08f, 1.25303330310243E-08f, 1.49011611938477E-08f, 1.77205669135261E-08f, 2.10734242554471E-08f, 2.50606660620485E-08f, 2.98023223876953E-08f, 3.54411338270521E-08f,
|
|
4.21468485108941E-08f, 5.01213321240971E-08f, 5.96046447753906E-08f, 7.08822676541044E-08f, 8.42936970217880E-08f, 1.00242664248194E-07f, 1.19209289550781E-07f, 1.41764535308209E-07f,
|
|
1.68587394043576E-07f, 2.00485328496388E-07f, 2.38418579101562E-07f, 2.83529070616417E-07f, 3.37174788087152E-07f, 4.00970656992777E-07f, 4.76837158203125E-07f, 5.67058141232835E-07f,
|
|
6.74349576174305E-07f, 8.01941313985553E-07f, 9.53674316406250E-07f, 1.13411628246567E-06f, 1.34869915234861E-06f, 1.60388262797110E-06f, 1.90734863281250E-06f, 2.26823256493134E-06f,
|
|
2.69739830469722E-06f, 3.20776525594221E-06f, 3.81469726562500E-06f, 4.53646512986268E-06f, 5.39479660939444E-06f, 6.41553051188442E-06f, 7.62939453125000E-06f, 9.07293025972536E-06f,
|
|
1.07895932187889E-05f, 1.28310610237688E-05f, 1.52587890625000E-05f, 1.81458605194507E-05f, 2.15791864375777E-05f, 2.56621220475377E-05f, 3.05175781250000E-05f, 3.62917210389014E-05f,
|
|
4.31583728751555E-05f, 5.13242440950754E-05f, 6.10351562500000E-05f, 7.25834420778029E-05f, 8.63167457503110E-05f, 1.02648488190151E-04f, 1.22070312500000E-04f, 1.45166884155606E-04f,
|
|
1.72633491500622E-04f, 2.05296976380301E-04f, 2.44140625000000E-04f, 2.90333768311211E-04f, 3.45266983001244E-04f, 4.10593952760603E-04f, 4.88281250000000E-04f, 5.80667536622423E-04f,
|
|
6.90533966002488E-04f, 8.21187905521206E-04f, 9.76562500000000E-04f, 1.16133507324485E-03f, 1.38106793200498E-03f, 1.64237581104241E-03f, 1.95312500000000E-03f, 2.32267014648969E-03f,
|
|
2.76213586400995E-03f, 3.28475162208482E-03f, 3.90625000000000E-03f, 4.64534029297938E-03f, 5.52427172801990E-03f, 6.56950324416964E-03f, 7.81250000000000E-03f, 9.29068058595876E-03f,
|
|
1.10485434560398E-02f, 1.31390064883393E-02f, 1.56250000000000E-02f, 1.85813611719175E-02f, 2.20970869120796E-02f, 2.62780129766786E-02f, 3.12500000000000E-02f, 3.71627223438350E-02f,
|
|
4.41941738241592E-02f, 5.25560259533572E-02f, 6.25000000000000E-02f, 7.43254446876701E-02f, 8.83883476483184E-02f, 1.05112051906714E-01f, 1.25000000000000E-01f, 1.48650889375340E-01f,
|
|
1.76776695296637E-01f, 2.10224103813429E-01f, 2.50000000000000E-01f, 2.97301778750680E-01f, 3.53553390593274E-01f, 4.20448207626857E-01f, 5.00000000000000E-01f, 5.94603557501361E-01f,
|
|
7.07106781186547E-01f, 8.40896415253715E-01f, 1.00000000000000E+00f, 1.18920711500272E+00f, 1.41421356237310E+00f, 1.68179283050743E+00f, 2.00000000000000E+00f, 2.37841423000544E+00f,
|
|
2.82842712474619E+00f, 3.36358566101486E+00f, 4.00000000000000E+00f, 4.75682846001088E+00f, 5.65685424949238E+00f, 6.72717132202972E+00f, 8.00000000000000E+00f, 9.51365692002177E+00f,
|
|
1.13137084989848E+01f, 1.34543426440594E+01f, 1.60000000000000E+01f, 1.90273138400435E+01f, 2.26274169979695E+01f, 2.69086852881189E+01f, 3.20000000000000E+01f, 3.80546276800871E+01f,
|
|
4.52548339959390E+01f, 5.38173705762377E+01f, 6.40000000000000E+01f, 7.61092553601742E+01f, 9.05096679918781E+01f, 1.07634741152475E+02f, 1.28000000000000E+02f, 1.52218510720348E+02f,
|
|
1.81019335983756E+02f, 2.15269482304951E+02f, 2.56000000000000E+02f, 3.04437021440696E+02f, 3.62038671967512E+02f, 4.30538964609902E+02f, 5.12000000000000E+02f, 6.08874042881393E+02f,
|
|
7.24077343935025E+02f, 8.61077929219803E+02f, 1.02400000000000E+03f, 1.21774808576279E+03f, 1.44815468787005E+03f, 1.72215585843961E+03f, 2.04800000000000E+03f, 2.43549617152557E+03f,
|
|
};
|
|
|
|
#endregion
|
|
|
|
void ReadSideInfo(IMpegFrame frame)
|
|
{
|
|
if (frame.Version == MpegVersion.Version1)
|
|
{
|
|
// main_data_begin 9
|
|
_mainDataBegin = frame.ReadBits(9);
|
|
|
|
// private_bits 3 or 5
|
|
if (frame.ChannelMode == MpegChannelMode.Mono)
|
|
{
|
|
_privBits = frame.ReadBits(5);
|
|
_channels = 1;
|
|
}
|
|
else
|
|
{
|
|
_privBits = frame.ReadBits(3);
|
|
_channels = 2;
|
|
}
|
|
|
|
for (var ch = 0; ch < _channels; ch++)
|
|
{
|
|
// scfsi[ch][0...3] 1 x4
|
|
_scfsi[ch][0] = frame.ReadBits(1);
|
|
_scfsi[ch][1] = frame.ReadBits(1);
|
|
_scfsi[ch][2] = frame.ReadBits(1);
|
|
_scfsi[ch][3] = frame.ReadBits(1);
|
|
}
|
|
|
|
for (var gr = 0; gr < 2; gr++)
|
|
{
|
|
for (var ch = 0; ch < _channels; ch++)
|
|
{
|
|
// part2_3_length[gr][ch] 12
|
|
_part23Length[gr][ch] = frame.ReadBits(12);
|
|
// big_values[gr][ch] 9
|
|
_bigValues[gr][ch] = frame.ReadBits(9);
|
|
// global_gain[gr][ch] 8
|
|
_globalGain[gr][ch] = GAIN_TAB[frame.ReadBits(8)];
|
|
// scalefac_compress[gr][ch] 4
|
|
_scalefacCompress[gr][ch] = frame.ReadBits(4);
|
|
// blocksplit_flag[gr][ch] 1
|
|
_blockSplitFlag[gr][ch] = frame.ReadBits(1) == 1;
|
|
if (_blockSplitFlag[gr][ch])
|
|
{
|
|
// block_type[gr][ch] 2
|
|
_blockType[gr][ch] = frame.ReadBits(2);
|
|
// switch_point[gr][ch] 1
|
|
_mixedBlockFlag[gr][ch] = frame.ReadBits(1) == 1;
|
|
// table_select[gr][ch][0..1] 5 x2
|
|
_tableSelect[gr][ch][0] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][1] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][2] = 0;
|
|
// set the region information
|
|
if (_blockType[gr][ch] == 2 && !_mixedBlockFlag[gr][ch])
|
|
{
|
|
_regionAddress1[gr][ch] = 8;
|
|
}
|
|
else
|
|
{
|
|
_regionAddress1[gr][ch] = 7;
|
|
}
|
|
_regionAddress2[gr][ch] = 20 - _regionAddress1[gr][ch];
|
|
// subblock_gain[gr][ch][0..2] 3 x3
|
|
_subblockGain[gr][ch][0] = frame.ReadBits(3) * -2f;
|
|
_subblockGain[gr][ch][1] = frame.ReadBits(3) * -2f;
|
|
_subblockGain[gr][ch][2] = frame.ReadBits(3) * -2f;
|
|
}
|
|
else
|
|
{
|
|
// table_select[0..2][gr][ch] 5 x3
|
|
_tableSelect[gr][ch][0] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][1] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][2] = frame.ReadBits(5);
|
|
// region_address1[gr][ch] 4
|
|
_regionAddress1[gr][ch] = frame.ReadBits(4);
|
|
// region_address2[gr][ch] 3
|
|
_regionAddress2[gr][ch] = frame.ReadBits(3);
|
|
// set the block type so it doesn't accidentally carry
|
|
_blockType[gr][ch] = 0;
|
|
|
|
// make subblock gain equal unity
|
|
_subblockGain[gr][ch][0] = 0;
|
|
_subblockGain[gr][ch][1] = 0;
|
|
_subblockGain[gr][ch][2] = 0;
|
|
}
|
|
// preflag[gr][ch] 1
|
|
_preflag[gr][ch] = frame.ReadBits(1);
|
|
// scalefac_scale[gr][ch] 1
|
|
_scalefacScale[gr][ch] = .5f * (1f + frame.ReadBits(1));
|
|
// count1table_select[gr][ch] 1
|
|
_count1TableSelect[gr][ch] = frame.ReadBits(1);
|
|
}
|
|
}
|
|
}
|
|
else // MPEG 2+
|
|
{
|
|
// main_data_begin 8
|
|
_mainDataBegin = frame.ReadBits(8);
|
|
|
|
// private_bits 1 or 2
|
|
if (frame.ChannelMode == MpegChannelMode.Mono)
|
|
{
|
|
_privBits = frame.ReadBits(1);
|
|
_channels = 1;
|
|
}
|
|
else
|
|
{
|
|
_privBits = frame.ReadBits(2);
|
|
_channels = 2;
|
|
}
|
|
|
|
var gr = 0;
|
|
for (var ch = 0; ch < _channels; ch++)
|
|
{
|
|
// part2_3_length[gr][ch] 12
|
|
_part23Length[gr][ch] = frame.ReadBits(12);
|
|
// big_values[gr][ch] 9
|
|
_bigValues[gr][ch] = frame.ReadBits(9);
|
|
// global_gain[gr][ch] 8
|
|
_globalGain[gr][ch] = GAIN_TAB[frame.ReadBits(8)];
|
|
// scalefac_compress[gr][ch] 9
|
|
_scalefacCompress[gr][ch] = frame.ReadBits(9);
|
|
// blocksplit_flag[gr][ch] 1
|
|
_blockSplitFlag[gr][ch] = frame.ReadBits(1) == 1;
|
|
if (_blockSplitFlag[gr][ch])
|
|
{
|
|
// block_type[gr][ch] 2
|
|
_blockType[gr][ch] = frame.ReadBits(2);
|
|
// switch_point[gr][ch] 1
|
|
_mixedBlockFlag[gr][ch] = frame.ReadBits(1) == 1;
|
|
// table_select[gr][ch][0..1] 5 x2
|
|
_tableSelect[gr][ch][0] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][1] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][2] = 0;
|
|
// set the region information
|
|
if (_blockType[gr][ch] == 2 && !_mixedBlockFlag[gr][ch])
|
|
{
|
|
_regionAddress1[gr][ch] = 8;
|
|
}
|
|
else
|
|
{
|
|
_regionAddress1[gr][ch] = 7;
|
|
}
|
|
_regionAddress2[gr][ch] = 20 - _regionAddress1[gr][ch];
|
|
// subblock_gain[gr][ch][0..2] 3 x3
|
|
_subblockGain[gr][ch][0] = frame.ReadBits(3) * -2f;
|
|
_subblockGain[gr][ch][1] = frame.ReadBits(3) * -2f;
|
|
_subblockGain[gr][ch][2] = frame.ReadBits(3) * -2f;
|
|
}
|
|
else
|
|
{
|
|
// table_select[0..2][gr][ch] 5 x3
|
|
_tableSelect[gr][ch][0] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][1] = frame.ReadBits(5);
|
|
_tableSelect[gr][ch][2] = frame.ReadBits(5);
|
|
// region_address1[gr][ch] 4
|
|
_regionAddress1[gr][ch] = frame.ReadBits(4);
|
|
// region_address2[gr][ch] 3
|
|
_regionAddress2[gr][ch] = frame.ReadBits(3);
|
|
// set the block type so it doesn't accidentally carry
|
|
_blockType[gr][ch] = 0;
|
|
|
|
// make subblock gain equal unity
|
|
_subblockGain[gr][ch][0] = 0;
|
|
_subblockGain[gr][ch][1] = 0;
|
|
_subblockGain[gr][ch][2] = 0;
|
|
}
|
|
// scalefac_scale[gr][ch] 1
|
|
_scalefacScale[gr][ch] = .5f * (1f + frame.ReadBits(1));
|
|
// count1table_select[gr][ch] 1
|
|
_count1TableSelect[gr][ch] = frame.ReadBits(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Precalc Table Prep
|
|
|
|
#region Variables
|
|
|
|
int[] _sfBandIndexL, _sfBandIndexS;
|
|
|
|
// these are byte[] to save memory
|
|
byte[] _cbLookupL = new byte[SSLIMIT * SBLIMIT], _cbLookupS = new byte[SSLIMIT * SBLIMIT], _cbwLookupS = new byte[SSLIMIT * SBLIMIT];
|
|
int _cbLookupSR;
|
|
|
|
static readonly int[][] _sfBandIndexLTable = {
|
|
// MPEG 1
|
|
// 44.1 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 52, 62, 74, 90, 110, 134, 162, 196, 238, 288, 342, 418, 576 },
|
|
// 48 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 20, 24, 30, 36, 42, 50, 60, 72, 88, 106, 128, 156, 190, 230, 276, 330, 384, 576 },
|
|
// 32 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 20, 24, 30, 36, 44, 54, 66, 82, 102, 126, 156, 194, 240, 296, 364, 448, 550, 576 },
|
|
|
|
// MPEG 2
|
|
// 22.05 kHz
|
|
new int[] { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
|
// 24 kHz
|
|
new int[] { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 114, 136, 162, 194, 232, 278, 330, 394, 464, 540, 576 },
|
|
// 16 kHz
|
|
new int[] { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
|
|
|
// MPEG 2.5
|
|
// 11.025 kHz
|
|
new int[] { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
|
// 12 kHz
|
|
new int[] { 0, 6, 12, 18, 24, 30, 36, 44, 54, 66, 80, 96, 116, 140, 168, 200, 238, 284, 336, 396, 464, 522, 576 },
|
|
// 8 kHz
|
|
new int[] { 0, 12, 24, 36, 48, 60, 72, 88, 108, 132, 160, 192, 232, 280, 336, 400, 476, 566, 568, 570, 572, 574, 576 },
|
|
};
|
|
|
|
static readonly int[][] _sfBandIndexSTable = {
|
|
// MPEG 1
|
|
// 44.1 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 22, 30, 40, 52, 66, 84, 106, 136, 192 },
|
|
// 48 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 22, 28, 38, 50, 64, 80, 100, 126, 192 },
|
|
// 32 kHz
|
|
new int[] { 0, 4, 8, 12, 16, 22, 30, 42, 58, 78, 104, 138, 180, 192 },
|
|
|
|
// MPEG 2
|
|
// 22.05 kHz
|
|
new int[] { 0, 4, 8, 12, 18, 24, 32, 42, 56, 74, 100, 132, 174, 192 },
|
|
// 24 kHz
|
|
new int[] { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 136, 180, 192 },
|
|
// 16 kHz
|
|
new int[] { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 },
|
|
|
|
// MPEG 2.5
|
|
// 11.025 kHz
|
|
new int[] { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 },
|
|
// 12 kHz
|
|
new int[] { 0, 4, 8, 12, 18, 26, 36, 48, 62, 80, 104, 134, 174, 192 },
|
|
// 8 kHz
|
|
new int[] { 0, 8, 16, 24, 36, 52, 72, 96, 124, 160, 162, 164, 166, 192 },
|
|
};
|
|
|
|
#endregion
|
|
|
|
void PrepTables(IMpegFrame frame)
|
|
{
|
|
if (_cbLookupSR != frame.SampleRate)
|
|
{
|
|
switch (frame.SampleRate)
|
|
{
|
|
case 44100:
|
|
_sfBandIndexL = _sfBandIndexLTable[0];
|
|
_sfBandIndexS = _sfBandIndexSTable[0];
|
|
break;
|
|
case 48000:
|
|
_sfBandIndexL = _sfBandIndexLTable[1];
|
|
_sfBandIndexS = _sfBandIndexSTable[1];
|
|
break;
|
|
case 32000:
|
|
_sfBandIndexL = _sfBandIndexLTable[2];
|
|
_sfBandIndexS = _sfBandIndexSTable[2];
|
|
break;
|
|
|
|
case 22050:
|
|
_sfBandIndexL = _sfBandIndexLTable[3];
|
|
_sfBandIndexS = _sfBandIndexSTable[3];
|
|
break;
|
|
case 24000:
|
|
_sfBandIndexL = _sfBandIndexLTable[4];
|
|
_sfBandIndexS = _sfBandIndexSTable[4];
|
|
break;
|
|
case 16000:
|
|
_sfBandIndexL = _sfBandIndexLTable[5];
|
|
_sfBandIndexS = _sfBandIndexSTable[5];
|
|
break;
|
|
|
|
case 11025:
|
|
_sfBandIndexL = _sfBandIndexLTable[6];
|
|
_sfBandIndexS = _sfBandIndexSTable[6];
|
|
break;
|
|
case 12000:
|
|
_sfBandIndexL = _sfBandIndexLTable[7];
|
|
_sfBandIndexS = _sfBandIndexSTable[7];
|
|
break;
|
|
case 8000:
|
|
_sfBandIndexL = _sfBandIndexLTable[8];
|
|
_sfBandIndexS = _sfBandIndexSTable[8];
|
|
break;
|
|
}
|
|
|
|
// precalculate the critical bands per bucket
|
|
int cbL = 0, cbS = 0;
|
|
int next_cbL = _sfBandIndexL[1], next_cbS = _sfBandIndexS[1] * 3;
|
|
for (int i = 0; i < 576; i++)
|
|
{
|
|
if (i == next_cbL)
|
|
{
|
|
++cbL;
|
|
next_cbL = _sfBandIndexL[cbL + 1];
|
|
}
|
|
if (i == next_cbS)
|
|
{
|
|
++cbS;
|
|
next_cbS = _sfBandIndexS[cbS + 1] * 3;
|
|
}
|
|
_cbLookupL[i] = (byte)cbL;
|
|
_cbLookupS[i] = (byte)cbS;
|
|
}
|
|
|
|
// set up the short block windows
|
|
int idx = 0;
|
|
for (cbS = 0; cbS < 12; cbS++)
|
|
{
|
|
var width = _sfBandIndexS[cbS + 1] - _sfBandIndexS[cbS];
|
|
for (int i = 0; i < 3; i++)
|
|
{
|
|
for (int j = 0; j < width; j++, idx++)
|
|
{
|
|
_cbwLookupS[idx] = (byte)i;
|
|
}
|
|
}
|
|
}
|
|
|
|
_cbLookupSR = frame.SampleRate;
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Scale Factors
|
|
|
|
#region Variables
|
|
|
|
int[][][] _scalefac = { // ch, window, cb
|
|
new int[][] { new int[13], new int[13], new int[13], new int[23] },
|
|
new int[][] { new int[13], new int[13], new int[13], new int[23] }
|
|
};
|
|
|
|
static readonly int[][] _slen = {
|
|
new int[] { 0, 0, 0, 0, 3, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4 },
|
|
new int[] { 0, 1, 2, 3, 0, 1, 2, 3, 1, 2, 3, 1, 2, 3, 2, 3 }
|
|
};
|
|
|
|
static readonly int[][][] _sfbBlockCntTab = {
|
|
new int[][] { new int[] { 6, 5, 5, 5 }, new int[] { 9, 9, 9, 9 }, new int[] { 6, 9, 9, 9 } },
|
|
new int[][] { new int[] { 6, 5, 7, 3 }, new int[] { 9, 9, 12, 6 }, new int[] { 6, 9, 12, 6 } },
|
|
new int[][] { new int[] { 11, 10, 0, 0 }, new int[] { 18, 18, 0, 0 }, new int[] { 15, 18, 0, 0 } },
|
|
new int[][] { new int[] { 7, 7, 7, 0 }, new int[] { 12, 12, 12, 0 }, new int[] { 6, 15, 12, 0 } },
|
|
new int[][] { new int[] { 6, 6, 6, 3 }, new int[] { 12, 9, 9, 6 }, new int[] { 6, 12, 9, 6 } },
|
|
new int[][] { new int[] { 8, 8, 5, 0 }, new int[] { 15, 12, 9, 0 }, new int[] { 6, 18, 9, 0 } },
|
|
};
|
|
|
|
#endregion
|
|
|
|
int ReadScalefactors(int gr, int ch)
|
|
{
|
|
var slen0 = _slen[0][_scalefacCompress[gr][ch]];
|
|
var slen1 = _slen[1][_scalefacCompress[gr][ch]];
|
|
int bits;
|
|
|
|
int cb = 0;
|
|
if (_blockSplitFlag[gr][ch] && _blockType[gr][ch] == 2)
|
|
{
|
|
if (slen0 > 0)
|
|
{
|
|
bits = slen0 * 18;
|
|
|
|
if (_mixedBlockFlag[gr][ch])
|
|
{
|
|
// mixed has bands 0..7 of long, then 3..11 of short
|
|
for (; cb < 8; cb++)
|
|
{
|
|
_scalefac[ch][3][cb] = _bitRes.GetBits(slen0);
|
|
}
|
|
cb = 3;
|
|
bits -= slen0; // mixed blocks need slen0 fewer bits
|
|
}
|
|
|
|
// short / mixed: just read from wherever cb happens to be through 11
|
|
for (; cb < 6; cb++)
|
|
{
|
|
_scalefac[ch][0][cb] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][1][cb] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][2][cb] = _bitRes.GetBits(slen0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][3], 0, 8);
|
|
Array.Clear(_scalefac[ch][0], 0, 6);
|
|
Array.Clear(_scalefac[ch][1], 0, 6);
|
|
Array.Clear(_scalefac[ch][2], 0, 6);
|
|
bits = 0;
|
|
}
|
|
|
|
if (slen1 > 0)
|
|
{
|
|
bits += slen1 * 18;
|
|
|
|
for (cb = 6; cb < 12; cb++)
|
|
{
|
|
_scalefac[ch][0][cb] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][1][cb] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][2][cb] = _bitRes.GetBits(slen1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][0], 6, 6);
|
|
Array.Clear(_scalefac[ch][1], 6, 6);
|
|
Array.Clear(_scalefac[ch][2], 6, 6);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// long: read if gr == 0, otherwise honor scfsi for the channel
|
|
bits = 0;
|
|
if (gr == 0 || _scfsi[ch][0] == 0)
|
|
{
|
|
if (slen0 > 0)
|
|
{
|
|
bits += slen0 * 6;
|
|
_scalefac[ch][3][0] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][1] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][2] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][3] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][4] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][5] = _bitRes.GetBits(slen0);
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][3], 0, 6);
|
|
}
|
|
}
|
|
if (gr == 0 || _scfsi[ch][1] == 0)
|
|
{
|
|
if (slen0 > 0)
|
|
{
|
|
bits += slen0 * 5;
|
|
_scalefac[ch][3][6] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][7] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][8] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][9] = _bitRes.GetBits(slen0);
|
|
_scalefac[ch][3][10] = _bitRes.GetBits(slen0);
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][3], 6, 5);
|
|
}
|
|
}
|
|
if (gr == 0 || _scfsi[ch][2] == 0)
|
|
{
|
|
if (slen1 > 0)
|
|
{
|
|
bits += slen1 * 5;
|
|
_scalefac[ch][3][11] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][12] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][13] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][14] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][15] = _bitRes.GetBits(slen1);
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][3], 11, 5);
|
|
}
|
|
}
|
|
if (gr == 0 || _scfsi[ch][3] == 0)
|
|
{
|
|
if (slen1 > 0)
|
|
{
|
|
bits += slen1 * 5;
|
|
_scalefac[ch][3][16] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][17] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][18] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][19] = _bitRes.GetBits(slen1);
|
|
_scalefac[ch][3][20] = _bitRes.GetBits(slen1);
|
|
}
|
|
else
|
|
{
|
|
Array.Clear(_scalefac[ch][3], 16, 5);
|
|
}
|
|
}
|
|
}
|
|
|
|
return bits;
|
|
}
|
|
|
|
int ReadLsfScalefactors(int gr, int ch, int chanModeExt)
|
|
{
|
|
var sfc = _scalefacCompress[gr][ch];
|
|
|
|
int blockTypeNumber;
|
|
// block type number = 2 if mixed short, 1 if pure short, otherwise 0
|
|
if (_blockType[gr][ch] == 2)
|
|
{
|
|
if (_mixedBlockFlag[gr][ch])
|
|
{
|
|
blockTypeNumber = 2;
|
|
}
|
|
else
|
|
{
|
|
blockTypeNumber = 1;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
blockTypeNumber = 0;
|
|
}
|
|
|
|
int[] slen = new int[4];
|
|
int blockNumber;
|
|
if ((chanModeExt & 1) == 1 && ch == 1)
|
|
{
|
|
var tsfc = sfc >> 1;
|
|
if (tsfc < 180)
|
|
{
|
|
slen[0] = tsfc / 36; // <= 4, 15
|
|
slen[1] = (tsfc % 36) / 6; // <= 5, 31
|
|
slen[2] = tsfc % 6; // <= 5, 31
|
|
slen[3] = 0;
|
|
_preflag[gr][ch] = 0;
|
|
blockNumber = 3;
|
|
}
|
|
else if (tsfc < 244)
|
|
{
|
|
slen[0] = ((tsfc - 180) % 64) >> 4; // <= 3, 7
|
|
slen[1] = ((tsfc - 180) % 16) >> 2; // <= 3, 7
|
|
slen[2] = ((tsfc - 180) % 4); // <= 3, 7
|
|
slen[3] = 0;
|
|
_preflag[gr][ch] = 0;
|
|
blockNumber = 4;
|
|
}
|
|
else if (tsfc < 255)
|
|
{
|
|
slen[0] = (tsfc - 244) / 3; // <= 3, 7
|
|
slen[1] = (tsfc - 244) % 3; // <= 1, 1
|
|
slen[2] = 0;
|
|
slen[3] = 0;
|
|
_preflag[gr][ch] = 0;
|
|
blockNumber = 5;
|
|
}
|
|
else
|
|
{
|
|
blockNumber = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// if scalefac_comp < 400
|
|
if (sfc < 400)
|
|
{
|
|
slen[0] = (sfc >> 4) / 5; // <= 4, 15
|
|
slen[1] = (sfc >> 4) % 5; // <= 4, 15
|
|
slen[2] = (sfc & 15) >> 2; // <= 3, 7
|
|
slen[3] = sfc & 3; // <= 3, 7
|
|
_preflag[gr][ch] = 0;
|
|
blockNumber = 0;
|
|
}
|
|
else if (sfc < 500)
|
|
{
|
|
slen[0] = ((sfc - 400) >> 2) / 5; // <= 4, 15
|
|
slen[1] = ((sfc - 400) >> 2) % 5; // <= 4, 15
|
|
slen[2] = (sfc - 400) & 3; // <= 3, 7
|
|
slen[3] = 0;
|
|
_preflag[gr][ch] = 0;
|
|
blockNumber = 1;
|
|
}
|
|
else if (sfc < 512)
|
|
{
|
|
slen[0] = (sfc - 500) / 3; // <= 3, 7
|
|
slen[1] = (sfc - 500) % 3; // <= 2, 3
|
|
slen[2] = 0;
|
|
slen[3] = 0;
|
|
_preflag[gr][ch] = 1;
|
|
blockNumber = 2;
|
|
}
|
|
else
|
|
{
|
|
blockNumber = 0;
|
|
}
|
|
}
|
|
|
|
// now we populate our buffer...
|
|
var buffer = new int[54];
|
|
|
|
var k = 0;
|
|
var blkCnt = _sfbBlockCntTab[blockNumber][blockTypeNumber];
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
if (slen[i] != 0)
|
|
{
|
|
for (int j = 0; j < blkCnt[i]; j++, k++)
|
|
{
|
|
buffer[k] = _bitRes.GetBits(slen[i]);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
k += blkCnt[i];
|
|
}
|
|
}
|
|
|
|
// now that we have that done, let's assign our scalefactors
|
|
k = 0;
|
|
int sfb = 0;
|
|
if (_blockSplitFlag[gr][ch] && _blockType[gr][ch] == 2)
|
|
{
|
|
if (_mixedBlockFlag[gr][ch])
|
|
{
|
|
for (; sfb < 8; sfb++)
|
|
{
|
|
_scalefac[ch][3][sfb] = buffer[k++];
|
|
}
|
|
sfb = 3;
|
|
}
|
|
|
|
for (; sfb < 12; sfb++)
|
|
{
|
|
for (int window = 0; window < 3; window++)
|
|
{
|
|
_scalefac[ch][window][sfb] = buffer[k++];
|
|
}
|
|
}
|
|
_scalefac[ch][0][12] = 0;
|
|
_scalefac[ch][1][12] = 0;
|
|
_scalefac[ch][2][12] = 0;
|
|
}
|
|
else
|
|
{
|
|
for (; sfb < 21; sfb++)
|
|
{
|
|
_scalefac[ch][3][sfb] = buffer[k++];
|
|
}
|
|
_scalefac[ch][3][22] = 0;
|
|
}
|
|
|
|
return slen[0] * blkCnt[0] + slen[1] * blkCnt[1] + slen[2] * blkCnt[2] + slen[3] * blkCnt[3];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Huffman & Dequantize
|
|
|
|
#region Variables
|
|
|
|
float[][] _samples = { new float[SSLIMIT * SBLIMIT + 3], new float[SSLIMIT * SBLIMIT + 3] };
|
|
|
|
static readonly int[] PRETAB = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 3, 3, 3, 2, 0 };
|
|
|
|
static readonly float[] POW2_TAB =
|
|
{
|
|
1.000000000000000E-00f, 7.071067811865470E-01f, 5.000000000000000E-01f, 3.535533905932740E-01f, 2.500000000000000E-01f, 1.767766952966370E-01f, 1.250000000000000E-01f, 8.838834764831840E-02f,
|
|
6.250000000000000E-02f, 4.419417382415920E-02f, 3.125000000000000E-02f, 2.209708691207960E-02f, 1.562500000000000E-02f, 1.104854345603980E-02f, 7.812500000000000E-03f, 5.524271728019900E-03f,
|
|
3.906250000000000E-03f, 2.762135864009950E-03f, 1.953125000000000E-03f, 1.381067932004980E-03f, 9.765625000000000E-04f, 6.905339660024880E-04f, 4.882812500000000E-04f, 3.452669830012440E-04f,
|
|
2.441406250000000E-04f, 1.726334915006220E-04f, 1.220703125000000E-04f, 8.631674575031100E-05f, 6.103515625000000E-05f, 4.315837287515550E-05f, 3.051757812500000E-05f, 2.157918643757770E-05f,
|
|
1.525878906250000E-05f, 1.078959321878890E-05f, 7.629394531250000E-06f, 5.394796609394440E-06f, 3.814697265625000E-06f, 2.697398304697220E-06f, 1.907348632812500E-06f, 1.348699152348610E-06f,
|
|
9.536743164062500E-07f, 6.743495761743050E-07f, 4.768371582031250E-07f, 3.371747880871520E-07f, 2.384185791015620E-07f, 1.685873940435760E-07f, 1.192092895507810E-07f, 8.429369702178800E-08f,
|
|
5.960464477539060E-08f, 4.214684851089410E-08f, 2.980232238769530E-08f, 2.107342425544710E-08f, 1.490116119384770E-08f, 1.053671212772350E-08f, 7.450580596923830E-09f, 5.268356063861760E-09f,
|
|
3.725290298461910E-09f, 2.634178031930880E-09f, 1.862645149230960E-09f, 1.317089015965440E-09f, 9.313225746154790E-10f, 6.585445079827190E-10f, 4.656612873077390E-10f, 3.292722539913600E-10f,
|
|
};
|
|
|
|
#endregion
|
|
|
|
void ReadSamples(int sfBits, int gr, int ch)
|
|
{
|
|
int region1Start, region2Start;
|
|
if (_blockSplitFlag[gr][ch] && _blockType[gr][ch] == 2)
|
|
{
|
|
region1Start = 36;
|
|
region2Start = 576;
|
|
}
|
|
else
|
|
{
|
|
region1Start = _sfBandIndexL[_regionAddress1[gr][ch] + 1];
|
|
region2Start = _sfBandIndexL[Math.Min(_regionAddress1[gr][ch] + _regionAddress2[gr][ch] + 2, 22)];
|
|
}
|
|
|
|
var part3end = _bitRes.BitsRead - sfBits + _part23Length[gr][ch];
|
|
|
|
int idx = 0, h = _tableSelect[gr][ch][0];
|
|
|
|
// bigvalues section
|
|
int bigValueCount = _bigValues[gr][ch] * 2;
|
|
float x, y;
|
|
while (idx < bigValueCount && idx < region1Start)
|
|
{
|
|
Huffman.Decode(_bitRes, h, out x, out y);
|
|
_samples[ch][idx] = Dequantize(idx, x, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, y, gr, ch); ++idx;
|
|
}
|
|
h = _tableSelect[gr][ch][1];
|
|
while (idx < bigValueCount && idx < region2Start)
|
|
{
|
|
Huffman.Decode(_bitRes, h, out x, out y);
|
|
_samples[ch][idx] = Dequantize(idx, x, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, y, gr, ch); ++idx;
|
|
}
|
|
h = _tableSelect[gr][ch][2];
|
|
while (idx < bigValueCount)
|
|
{
|
|
Huffman.Decode(_bitRes, h, out x, out y);
|
|
_samples[ch][idx] = Dequantize(idx, x, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, y, gr, ch); ++idx;
|
|
}
|
|
|
|
// count1 section
|
|
h = _count1TableSelect[gr][ch] + 32;
|
|
|
|
float v, w;
|
|
// - 3 to ensure that we never get an out of range exception
|
|
while (part3end > _bitRes.BitsRead && idx < SBLIMIT * SSLIMIT - 3)
|
|
{
|
|
Huffman.Decode(_bitRes, h, out x, out y, out v, out w);
|
|
_samples[ch][idx] = Dequantize(idx, v, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, w, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, x, gr, ch); ++idx;
|
|
_samples[ch][idx] = Dequantize(idx, y, gr, ch); ++idx;
|
|
}
|
|
|
|
// adjust the bit stream if we're off somehow
|
|
if (_bitRes.BitsRead > part3end)
|
|
{
|
|
_bitRes.RewindBits((int)(_bitRes.BitsRead - part3end));
|
|
|
|
idx -= 4;
|
|
if (idx < 0) idx = 0;
|
|
}
|
|
|
|
if (_bitRes.BitsRead < part3end)
|
|
{
|
|
_bitRes.SkipBits((int)(part3end - _bitRes.BitsRead));
|
|
}
|
|
|
|
// zero out the highest samples (defined as 0 in the standard)
|
|
if (idx < SBLIMIT * SSLIMIT)
|
|
{
|
|
Array.Clear(_samples[ch], idx, SBLIMIT * SSLIMIT + 3 - idx);
|
|
}
|
|
}
|
|
|
|
float Dequantize(int idx, float val, int gr, int ch)
|
|
{
|
|
if (val != 0f)
|
|
{
|
|
int cb, window;
|
|
|
|
if (_blockSplitFlag[gr][ch] && _blockType[gr][ch] == 2 && !(_mixedBlockFlag[gr][ch] && idx < _sfBandIndexL[8]))
|
|
{
|
|
// short / mixed short section
|
|
cb = _cbLookupS[idx];
|
|
window = _cbwLookupS[idx];
|
|
|
|
return val * _globalGain[gr][ch] * POW2_TAB[(int)(-2 * (_subblockGain[gr][ch][window] - (_scalefacScale[gr][ch] * _scalefac[ch][window][cb])))];
|
|
}
|
|
else
|
|
{
|
|
// long / mixed long section
|
|
cb = _cbLookupL[idx];
|
|
|
|
return val * _globalGain[gr][ch] * POW2_TAB[(int)(2 * _scalefacScale[gr][ch] * (_scalefac[ch][3][cb] + _preflag[gr][ch] * PRETAB[cb]))];
|
|
}
|
|
|
|
}
|
|
return 0f;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Stereo
|
|
|
|
#region Variables
|
|
|
|
static readonly float[][] _isRatio = {
|
|
new float[] { 0f, 0.211324865405187f, 0.366025403784439f, 0.5f, 0.633974596215561f, 0.788675134594813f, 1f },
|
|
new float[] { 1f, 0.788675134594813f, 0.633974596215561f, 0.5f, 0.366025403784439f, 0.211324865405187f, 0f }
|
|
};
|
|
|
|
static readonly float[][][] _lsfRatio = { // sfc%2, ch, isPos
|
|
new float[][]
|
|
{
|
|
new float[] { 1f, 0.840896415256f, 1f, 0.707106781190391f, 1f, 0.594603557506209f, 1f, 0.500000000005436f, 1f, 0.420448207632571f, 1f, 0.353553390599039f, 1f, 0.297301778756337f, 1f, 0.250000000005436f, 1f, 0.210224103818571f, 1f, 0.176776695301441f, 1f, 0.148650889379784f, 1f, 0.125000000004077f, 1f, 0.105112051910428f, 1f, 0.0883883476516816f, 1f, 0.0743254446907002f, 1f, 0.0625000000027179f },
|
|
new float[] { 1f, 1f, 0.840896415256f, 1f, 0.707106781190391f, 1f, 0.594603557506209f, 1f, 0.500000000005436f, 1f, 0.420448207632571f, 1f, 0.353553390599039f, 1f, 0.297301778756337f, 1f, 0.250000000005436f, 1f, 0.210224103818571f, 1f, 0.176776695301441f, 1f, 0.148650889379784f, 1f, 0.125000000004077f, 1f, 0.105112051910428f, 1f, 0.0883883476516816f, 1f, 0.0743254446907002f, 1f },
|
|
},
|
|
new float[][]
|
|
{
|
|
new float[] { 1f, 0.707106781188f, 1f, 0.500000000002054f, 1f, 0.353553390595452f, 1f, 0.250000000002054f, 1f, 0.176776695298452f, 1f, 0.125000000001541f, 1f, 0.0883883476495893f, 1f, 0.062500000001027f, 1f, 0.0441941738249762f, 1f, 0.0312500000006419f, 1f, 0.0220970869125789f, 1f, 0.0156250000003851f, 1f, 0.0110485434563348f, 1f, 0.00781250000022466f, 1f, 0.00552427172819011f, 1f, 0.00390625000012838f },
|
|
new float[] { 1f, 1f, 0.707106781188f, 1f, 0.500000000002054f, 1f, 0.353553390595452f, 1f, 0.250000000002054f, 1f, 0.176776695298452f, 1f, 0.125000000001541f, 1f, 0.0883883476495893f, 1f, 0.062500000001027f, 1f, 0.0441941738249762f, 1f, 0.0312500000006419f, 1f, 0.0220970869125789f, 1f, 0.0156250000003851f, 1f, 0.0110485434563348f, 1f, 0.00781250000022466f, 1f, 0.00552427172819011f, 1f },
|
|
},
|
|
};
|
|
|
|
#endregion
|
|
|
|
void Stereo(MpegChannelMode channelMode, int chanModeExt, int gr, bool lsf)
|
|
{
|
|
// do the stereo decoding as needed... This really only applies in two cases:
|
|
// 1) Joint Stereo and one (or both) of the extensions are enabled, or
|
|
// 2) We're doing a downmix to mono
|
|
|
|
if (channelMode == MpegChannelMode.JointStereo && chanModeExt != 0)
|
|
{
|
|
var midSide = (chanModeExt & 0x2) == 2;
|
|
|
|
if ((chanModeExt & 0x1) == 1)
|
|
{
|
|
// do the intensity stereo processing
|
|
|
|
#region Common Processing
|
|
|
|
// find the highest sample index with a value in channel 1
|
|
// - now each step only has to start from there
|
|
int lastValueIdx = -1;
|
|
for (int i = SBLIMIT * SSLIMIT - (SBLIMIT + 1); i >= 0; i--)
|
|
{
|
|
if (_samples[1][i] != 0f)
|
|
{
|
|
lastValueIdx = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// figure up which passes we'll need and for which ranges
|
|
int lEnd = -1, sStart = -1;
|
|
if (_blockSplitFlag[gr][0] && _blockType[gr][0] == 2)
|
|
{
|
|
if (_mixedBlockFlag[gr][0])
|
|
{
|
|
// 0 through 8 of long, then 3 through 12 of short
|
|
if (lastValueIdx < _sfBandIndexL[8])
|
|
{
|
|
lEnd = 8;
|
|
}
|
|
sStart = 3;
|
|
}
|
|
else
|
|
{
|
|
// 0 through 12 of short
|
|
sStart = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// 0 through 21 of long
|
|
lEnd = 21;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Long Processing
|
|
|
|
// long processing is far simpler than short... just process from the start of the scalefactor band after the last non-zero sample
|
|
// we also don't have to mess with "finding" again; it was done above
|
|
var sfb = 0;
|
|
if (lastValueIdx > -1)
|
|
{
|
|
sfb = _cbLookupL[lastValueIdx] + 1;
|
|
}
|
|
|
|
// make sure we do the mid/side processing on the lower bands (if needed)
|
|
if (sfb > 0 && sStart == -1)
|
|
{
|
|
if (midSide)
|
|
{
|
|
ApplyMidSide(0, _sfBandIndexL[sfb]);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(0, _sfBandIndexL[sfb]);
|
|
}
|
|
}
|
|
|
|
// now process the intensity bands
|
|
for (; sfb < lEnd; sfb++)
|
|
{
|
|
var i = _sfBandIndexL[sfb];
|
|
var width = _sfBandIndexL[sfb + 1] - _sfBandIndexL[sfb];
|
|
var isPos = _scalefac[1][3][sfb];
|
|
if (isPos == 7)
|
|
{
|
|
if (midSide)
|
|
{
|
|
ApplyMidSide(i, width);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(i, width);
|
|
}
|
|
}
|
|
else if (lsf)
|
|
{
|
|
ApplyLsfIStereo(i, width, isPos, _scalefacCompress[gr][0]);
|
|
}
|
|
else
|
|
{
|
|
ApplyIStereo(i, width, isPos);
|
|
}
|
|
}
|
|
|
|
if (sStart <= -1)
|
|
{
|
|
// do final long processing
|
|
var isPos = _scalefac[1][3][20];
|
|
if (isPos == 7)
|
|
{
|
|
if (midSide)
|
|
{
|
|
ApplyMidSide(_sfBandIndexL[21], 576 - _sfBandIndexL[21]);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21]);
|
|
}
|
|
}
|
|
else if (lsf)
|
|
{
|
|
ApplyLsfIStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21], isPos, _scalefacCompress[gr][0]);
|
|
}
|
|
else
|
|
{
|
|
ApplyIStereo(_sfBandIndexL[21], 576 - _sfBandIndexL[21], isPos);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Short Processing
|
|
|
|
// short processing requires that each window be looked at separately... they are interleaved, so it gets really interesting...
|
|
// on the plus side, whichever window the {lastValueIdx} is in is already found... :)
|
|
else
|
|
{
|
|
// find where each window starts intensity processing
|
|
var sSfb = new int[] { -1, -1, -1 };
|
|
int window;
|
|
if (lastValueIdx > -1)
|
|
{
|
|
sfb = _cbLookupS[lastValueIdx];
|
|
window = _cbwLookupS[lastValueIdx];
|
|
sSfb[window] = sfb;
|
|
}
|
|
else
|
|
{
|
|
sfb = 12;
|
|
window = 3; // NB: 3 is correct!
|
|
}
|
|
|
|
window = (window - 1) % 3;
|
|
for (; sfb >= sStart && window >= 0; window = (window - 1) % 3)
|
|
{
|
|
if (sSfb[window] != -1)
|
|
{
|
|
if (sSfb[0] != -1 && sSfb[1] != -1 && sSfb[2] != -1)
|
|
{
|
|
break;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
var width = _sfBandIndexS[sfb + 1] - _sfBandIndexS[sfb];
|
|
var i = _sfBandIndexS[sfb] * 3 + width * (window + 1);
|
|
|
|
while (--width >= -1)
|
|
{
|
|
if (_samples[1][--i] != 0f)
|
|
{
|
|
sSfb[window] = sfb;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (window == 0)
|
|
{
|
|
--sfb;
|
|
}
|
|
}
|
|
|
|
// now apply the intensity processing for each window & scalefactor band
|
|
sfb = sStart;
|
|
for (; sfb < 12; sfb++)
|
|
{
|
|
var width = _sfBandIndexS[sfb + 1] - _sfBandIndexS[sfb];
|
|
var i = _sfBandIndexS[sfb] * 3;
|
|
|
|
for (window = 0; window < 3; window++)
|
|
{
|
|
if (sfb > sSfb[window])
|
|
{
|
|
var isPos = _scalefac[1][window][sfb];
|
|
if (isPos == 7)
|
|
{
|
|
if (midSide)
|
|
{
|
|
ApplyMidSide(i, width);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(i, width);
|
|
}
|
|
}
|
|
else if (lsf)
|
|
{
|
|
ApplyLsfIStereo(i, width, isPos, _scalefacCompress[gr][0]);
|
|
}
|
|
else
|
|
{
|
|
ApplyIStereo(i, width, isPos);
|
|
}
|
|
}
|
|
else if (midSide)
|
|
{
|
|
ApplyMidSide(i, width);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(i, width);
|
|
}
|
|
|
|
i += width;
|
|
}
|
|
}
|
|
|
|
// do final short processing
|
|
var finalWidth = _sfBandIndexS[13] - _sfBandIndexS[12];
|
|
for (window = 0; window < 3; window++)
|
|
{
|
|
var isPos = _scalefac[1][window][11];
|
|
if (isPos == 7)
|
|
{
|
|
if (midSide)
|
|
{
|
|
ApplyMidSide(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth);
|
|
}
|
|
else
|
|
{
|
|
ApplyFullStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth);
|
|
}
|
|
}
|
|
else if (lsf)
|
|
{
|
|
ApplyLsfIStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth, isPos, _scalefacCompress[gr][0]);
|
|
}
|
|
else
|
|
{
|
|
ApplyIStereo(_sfBandIndexS[11] * 3 + finalWidth * window, finalWidth, isPos);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
else if (midSide)
|
|
{
|
|
// just do mid/side processing for everything
|
|
ApplyMidSide(0, SBLIMIT * SSLIMIT);
|
|
}
|
|
else
|
|
{
|
|
// this is a no-op most of the time
|
|
ApplyFullStereo(0, SSLIMIT * SBLIMIT);
|
|
}
|
|
}
|
|
else if (_channels != 1)
|
|
{
|
|
// this is a no-op most of the time
|
|
ApplyFullStereo(0, SSLIMIT * SBLIMIT);
|
|
}
|
|
}
|
|
|
|
void ApplyIStereo(int i, int sb, int isPos)
|
|
{
|
|
if (StereoMode == StereoMode.DownmixToMono)
|
|
{
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[0][i] /= 2f; // scale appropriately
|
|
}
|
|
}
|
|
else
|
|
{
|
|
var ratio0 = _isRatio[0][isPos];
|
|
var ratio1 = _isRatio[1][isPos];
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[1][i] = _samples[0][i] * ratio1;
|
|
_samples[0][i] *= ratio0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyLsfIStereo(int i, int sb, int isPos, int scalefacCompress)
|
|
{
|
|
var k0 = _lsfRatio[scalefacCompress % 1][isPos][0];
|
|
var k1 = _lsfRatio[scalefacCompress % 1][isPos][1];
|
|
if (StereoMode == NLayer.StereoMode.DownmixToMono)
|
|
{
|
|
var ratio = 1 / (k0 + k1);
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[0][i] *= ratio;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[1][i] = _samples[0][i] * k1;
|
|
_samples[0][i] *= k0;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyMidSide(int i, int sb)
|
|
{
|
|
if (StereoMode == StereoMode.DownmixToMono)
|
|
{
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[0][i] *= 0.707106781f; // scale appropriately
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
// apply the mid/side
|
|
var a = _samples[0][i];
|
|
var b = _samples[1][i];
|
|
_samples[0][i] = (a + b) * 0.707106781f;
|
|
_samples[1][i] = (a - b) * 0.707106781f;
|
|
}
|
|
}
|
|
}
|
|
|
|
void ApplyFullStereo(int i, int sb)
|
|
{
|
|
if (StereoMode == NLayer.StereoMode.DownmixToMono)
|
|
{
|
|
for (; sb > 0; sb--, i++)
|
|
{
|
|
_samples[0][i] = (_samples[0][i] + _samples[1][i]) / 2f;
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Reorder
|
|
|
|
#region Variables
|
|
|
|
float[] _reorderBuf = new float[SBLIMIT * SSLIMIT];
|
|
|
|
#endregion
|
|
|
|
void Reorder(float[] buf, bool mixedBlock)
|
|
{
|
|
// reorder into _reorderBuf, then copy back
|
|
int sfb = 0;
|
|
|
|
if (mixedBlock)
|
|
{
|
|
// mixed... copy the first two bands and reorder the rest
|
|
Array.Copy(buf, 0, _reorderBuf, 0, SSLIMIT * 2);
|
|
|
|
sfb = 3;
|
|
}
|
|
|
|
while (sfb < 13)
|
|
{
|
|
int sfb_start = _sfBandIndexS[sfb];
|
|
int sfb_lines = _sfBandIndexS[sfb + 1] - sfb_start;
|
|
|
|
for (int window = 0; window < 3; window++)
|
|
{
|
|
for (int freq = 0; freq < sfb_lines; freq++)
|
|
{
|
|
var src_line = sfb_start * 3 + window * sfb_lines + freq;
|
|
var des_line = (sfb_start * 3) + window + (freq * 3);
|
|
_reorderBuf[des_line] = buf[src_line];
|
|
}
|
|
}
|
|
|
|
++sfb;
|
|
}
|
|
|
|
Array.Copy(_reorderBuf, buf, SSLIMIT * SBLIMIT);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Anti-Alias
|
|
|
|
#region Variables
|
|
|
|
static readonly float[] _scs = {
|
|
0.85749292571254400f, 0.88174199731770500f, 0.94962864910273300f, 0.98331459249179000f,
|
|
0.99551781606758600f, 0.99916055817814800f, 0.99989919524444700f, 0.99999315507028000f,
|
|
};
|
|
|
|
static readonly float[] _sca = {
|
|
-0.51449575542752700f, -0.47173196856497200f, -0.31337745420390200f, -0.18191319961098100f,
|
|
-0.09457419252642070f, -0.04096558288530410f, -0.01419856857247120f, -0.00369997467376004f,
|
|
};
|
|
|
|
#endregion
|
|
|
|
void AntiAlias(float[] buf, bool mixedBlock)
|
|
{
|
|
int sblim;
|
|
if (mixedBlock)
|
|
{
|
|
sblim = 1;
|
|
}
|
|
else
|
|
{
|
|
sblim = SBLIMIT - 1;
|
|
}
|
|
|
|
for (int sb = 0, offset = 0; sb < sblim; sb++, offset += SSLIMIT)
|
|
{
|
|
for (int ss = 0, buOfs = offset + SSLIMIT - 1, bdOfs = offset + SSLIMIT; ss < 8; ss++, buOfs--, bdOfs++)
|
|
{
|
|
var bu = buf[buOfs];
|
|
var bd = buf[bdOfs];
|
|
buf[buOfs] = (bu * _scs[ss]) - (bd * _sca[ss]);
|
|
buf[bdOfs] = (bd * _scs[ss]) + (bu * _sca[ss]);
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Frequency Inversion
|
|
|
|
void FrequencyInversion(float[] buf)
|
|
{
|
|
for (int ss = 1; ss < SSLIMIT; ss += 2)
|
|
{
|
|
for (int sb = 1; sb < SBLIMIT; sb += 2)
|
|
{
|
|
buf[sb * SSLIMIT + ss] = -buf[sb * SSLIMIT + ss];
|
|
}
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Inverse Polyphase
|
|
|
|
#region Variables
|
|
|
|
float[] _polyPhase = new float[SBLIMIT];
|
|
|
|
#endregion
|
|
|
|
// Layer III interleaves the samples, so we have to make them linear again
|
|
void InversePolyphase(float[] buf, int ch, int ofs, float[] outBuf)
|
|
{
|
|
for (int ss = 0; ss < SSLIMIT; ss++, ofs += SBLIMIT)
|
|
{
|
|
for (int sb = 0; sb < SBLIMIT; sb++)
|
|
{
|
|
_polyPhase[sb] = buf[sb * SSLIMIT + ss];
|
|
}
|
|
|
|
base.InversePolyPhase(ch, _polyPhase);
|
|
Array.Copy(_polyPhase, 0, outBuf, ofs, SBLIMIT);
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
}
|