(caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references)
This commit is contained in:
@@ -0,0 +1,284 @@
|
||||
/* Copyright (c) 2006-2011 Skype Limited. All Rights Reserved
|
||||
Ported to C# by Logan Stromberg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#if !UNSAFE
|
||||
|
||||
namespace Concentus.Common
|
||||
{
|
||||
using Concentus.Celt;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
|
||||
internal static class Autocorrelation
|
||||
{
|
||||
/* Compute autocorrelation */
|
||||
internal static void silk_autocorr(
|
||||
int[] results, /* O Result (length correlationCount) */
|
||||
BoxedValueInt scale, /* O Scaling of the correlation vector */
|
||||
short[] inputData, /* I Input data to correlate */
|
||||
int inputDataSize, /* I Length of input */
|
||||
int correlationCount /* I Number of correlation taps to compute */
|
||||
)
|
||||
{
|
||||
int corrCount = Inlines.silk_min_int(inputDataSize, correlationCount);
|
||||
scale.Val = Autocorrelation._celt_autocorr(inputData, results, corrCount - 1, inputDataSize);
|
||||
}
|
||||
|
||||
internal static int _celt_autocorr(
|
||||
short[] x, /* in: [0...n-1] samples x */
|
||||
int[] ac, /* out: [0...lag-1] ac values */
|
||||
int lag,
|
||||
int n
|
||||
)
|
||||
{
|
||||
int d;
|
||||
int i, k;
|
||||
int fastN = n - lag;
|
||||
int shift;
|
||||
short[] xptr;
|
||||
short[] xx = new short[n];
|
||||
Inlines.OpusAssert(n > 0);
|
||||
xptr = x;
|
||||
|
||||
shift = 0;
|
||||
{
|
||||
int ac0;
|
||||
ac0 = 1 + (n << 7);
|
||||
if ((n & 1) != 0)
|
||||
{
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
|
||||
}
|
||||
for (i = (n & 1); i < n; i += 2)
|
||||
{
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
|
||||
}
|
||||
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
|
||||
shift = (shift) / 2;
|
||||
if (shift > 0)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
xx[i] = (short)(Inlines.PSHR32(xptr[i], shift));
|
||||
}
|
||||
xptr = xx;
|
||||
}
|
||||
else
|
||||
shift = 0;
|
||||
}
|
||||
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
|
||||
for (k = 0; k <= lag; k++)
|
||||
{
|
||||
for (i = k + fastN, d = 0; i < n; i++)
|
||||
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
|
||||
ac[k] += d;
|
||||
}
|
||||
shift = 2 * shift;
|
||||
if (shift <= 0)
|
||||
ac[0] += Inlines.SHL32((int)1, -shift);
|
||||
if (ac[0] < 268435456)
|
||||
{
|
||||
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
|
||||
for (i = 0; i <= lag; i++)
|
||||
{
|
||||
ac[i] = Inlines.SHL32(ac[i], shift2);
|
||||
}
|
||||
shift -= shift2;
|
||||
}
|
||||
else if (ac[0] >= 536870912)
|
||||
{
|
||||
int shift2 = 1;
|
||||
if (ac[0] >= 1073741824)
|
||||
shift2++;
|
||||
for (i = 0; i <= lag; i++)
|
||||
{
|
||||
ac[i] = Inlines.SHR32(ac[i], shift2);
|
||||
}
|
||||
shift += shift2;
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
internal static int _celt_autocorr(
|
||||
int[] x, /* in: [0...n-1] samples x */
|
||||
int[] ac, /* out: [0...lag-1] ac values */
|
||||
int[] window,
|
||||
int overlap,
|
||||
int lag,
|
||||
int n)
|
||||
{
|
||||
int d;
|
||||
int i, k;
|
||||
int fastN = n - lag;
|
||||
int shift;
|
||||
int[] xptr;
|
||||
int[] xx = new int[n];
|
||||
|
||||
Inlines.OpusAssert(n > 0);
|
||||
Inlines.OpusAssert(overlap >= 0);
|
||||
|
||||
if (overlap == 0)
|
||||
{
|
||||
xptr = x;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
xx[i] = x[i];
|
||||
for (i = 0; i < overlap; i++)
|
||||
{
|
||||
xx[i] = Inlines.MULT16_16_Q15(x[i], window[i]);
|
||||
xx[n - i - 1] = Inlines.MULT16_16_Q15(x[n - i - 1], window[i]);
|
||||
}
|
||||
xptr = xx;
|
||||
}
|
||||
|
||||
shift = 0;
|
||||
|
||||
int ac0;
|
||||
ac0 = 1 + (n << 7);
|
||||
if ((n & 1) != 0)
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[0], xptr[0]), 9);
|
||||
|
||||
for (i = (n & 1); i < n; i += 2)
|
||||
{
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i], xptr[i]), 9);
|
||||
ac0 += Inlines.SHR32(Inlines.MULT16_16(xptr[i + 1], xptr[i + 1]), 9);
|
||||
}
|
||||
|
||||
shift = Inlines.celt_ilog2(ac0) - 30 + 10;
|
||||
shift = (shift) / 2;
|
||||
if (shift > 0)
|
||||
{
|
||||
for (i = 0; i < n; i++)
|
||||
xx[i] = (Inlines.PSHR32(xptr[i], shift));
|
||||
xptr = xx;
|
||||
}
|
||||
else
|
||||
shift = 0;
|
||||
|
||||
CeltPitchXCorr.pitch_xcorr(xptr, xptr, ac, fastN, lag + 1);
|
||||
for (k = 0; k <= lag; k++)
|
||||
{
|
||||
for (i = k + fastN, d = 0; i < n; i++)
|
||||
d = Inlines.MAC16_16(d, xptr[i], xptr[i - k]);
|
||||
ac[k] += d;
|
||||
}
|
||||
|
||||
shift = 2 * shift;
|
||||
if (shift <= 0)
|
||||
ac[0] += Inlines.SHL32((int)1, -shift);
|
||||
if (ac[0] < 268435456)
|
||||
{
|
||||
int shift2 = 29 - Inlines.EC_ILOG((uint)ac[0]);
|
||||
for (i = 0; i <= lag; i++)
|
||||
ac[i] = Inlines.SHL32(ac[i], shift2);
|
||||
shift -= shift2;
|
||||
}
|
||||
else if (ac[0] >= 536870912)
|
||||
{
|
||||
int shift2 = 1;
|
||||
if (ac[0] >= 1073741824)
|
||||
shift2++;
|
||||
for (i = 0; i <= lag; i++)
|
||||
ac[i] = Inlines.SHR32(ac[i], shift2);
|
||||
shift += shift2;
|
||||
}
|
||||
|
||||
return shift;
|
||||
}
|
||||
|
||||
private const int QC = 10;
|
||||
private const int QS = 14;
|
||||
|
||||
/* Autocorrelations for a warped frequency axis */
|
||||
internal static void silk_warped_autocorrelation(
|
||||
int[] corr, /* O Result [order + 1] */
|
||||
BoxedValueInt scale, /* O Scaling of the correlation vector */
|
||||
short[] input, /* I Input data to correlate */
|
||||
int warping_Q16, /* I Warping coefficient */
|
||||
int length, /* I Length of input */
|
||||
int order /* I Correlation order (even) */
|
||||
)
|
||||
{
|
||||
int n, i, lsh;
|
||||
int tmp1_QS, tmp2_QS;
|
||||
int[] state_QS = new int[order + 1];// = { 0 };
|
||||
long[] corr_QC = new long[order + 1];// = { 0 };
|
||||
|
||||
/* Order must be even */
|
||||
Inlines.OpusAssert((order & 1) == 0);
|
||||
Inlines.OpusAssert(2 * QS - QC >= 0);
|
||||
|
||||
/* Loop over samples */
|
||||
for (n = 0; n < length; n++)
|
||||
{
|
||||
tmp1_QS = Inlines.silk_LSHIFT32((int)input[n], QS);
|
||||
/* Loop over allpass sections */
|
||||
for (i = 0; i < order; i += 2)
|
||||
{
|
||||
/* Output of allpass section */
|
||||
tmp2_QS = Inlines.silk_SMLAWB(state_QS[i], state_QS[i + 1] - tmp1_QS, warping_Q16);
|
||||
state_QS[i] = tmp1_QS;
|
||||
corr_QC[i] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC);
|
||||
/* Output of allpass section */
|
||||
tmp1_QS = Inlines.silk_SMLAWB(state_QS[i + 1], state_QS[i + 2] - tmp2_QS, warping_Q16);
|
||||
state_QS[i + 1] = tmp2_QS;
|
||||
corr_QC[i + 1] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp2_QS, state_QS[0]), 2 * QS - QC);
|
||||
}
|
||||
state_QS[order] = tmp1_QS;
|
||||
corr_QC[order] += Inlines.silk_RSHIFT64(Inlines.silk_SMULL(tmp1_QS, state_QS[0]), 2 * QS - QC);
|
||||
}
|
||||
|
||||
lsh = Inlines.silk_CLZ64(corr_QC[0]) - 35;
|
||||
lsh = Inlines.silk_LIMIT(lsh, -12 - QC, 30 - QC);
|
||||
scale.Val = -(QC + lsh);
|
||||
Inlines.OpusAssert(scale.Val >= -30 && scale.Val <= 12);
|
||||
if (lsh >= 0)
|
||||
{
|
||||
for (i = 0; i < order + 1; i++)
|
||||
{
|
||||
corr[i] = (int)(Inlines.silk_LSHIFT64(corr_QC[i], lsh));
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (i = 0; i < order + 1; i++)
|
||||
{
|
||||
corr[i] = (int)(Inlines.silk_RSHIFT64(corr_QC[i], -lsh));
|
||||
}
|
||||
}
|
||||
Inlines.OpusAssert(corr_QC[0] >= 0); /* If breaking, decrease QC*/
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,221 @@
|
||||
/* Copyright (c) 2016 Logan Stromberg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace Concentus.Common.CPlusPlus
|
||||
{
|
||||
using System;
|
||||
|
||||
internal static class Arrays
|
||||
{
|
||||
internal static T[][] InitTwoDimensionalArray<T>(int x, int y)
|
||||
{
|
||||
T[][] returnVal = new T[x][];
|
||||
for (int c = 0; c < x; c++)
|
||||
{
|
||||
returnVal[c] = new T[y];
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
internal static Pointer<Pointer<T>> InitTwoDimensionalArrayPointer<T>(int x, int y)
|
||||
{
|
||||
Pointer<Pointer<T>> returnVal = Pointer.Malloc<Pointer<T>>(x);
|
||||
for (int c = 0; c < x; c++)
|
||||
{
|
||||
returnVal[c] = Pointer.Malloc<T>(y);
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
internal static T[][][] InitThreeDimensionalArray<T>(int x, int y, int z)
|
||||
{
|
||||
T[][][] returnVal = new T[x][][];
|
||||
for (int c = 0; c < x; c++)
|
||||
{
|
||||
returnVal[c] = new T[y][];
|
||||
for (int a = 0; a < y; a++)
|
||||
{
|
||||
returnVal[c][a] = new T[z];
|
||||
}
|
||||
}
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
//FIXME: For the most part this method is used to zero-out arrays, which is usually already done by the runtime.
|
||||
|
||||
internal static void MemSetByte(byte[] array, byte value)
|
||||
{
|
||||
for (int c = 0; c < array.Length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemSetInt(int[] array, int value, int length)
|
||||
{
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemSetShort(short[] array, short value, int length)
|
||||
{
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemSetFloat(float[] array, float value, int length)
|
||||
{
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemSetSbyte(sbyte[] array, sbyte value, int length)
|
||||
{
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemSetWithOffset<T>(T[] array, T value, int offset, int length)
|
||||
{
|
||||
for (int c = offset; c < offset + length; c++)
|
||||
{
|
||||
array[c] = value;
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemMove<T>(T[] array, int src_idx, int dst_idx, int length)
|
||||
{
|
||||
if (src_idx == dst_idx || length == 0)
|
||||
return;
|
||||
|
||||
// Do regions overlap?
|
||||
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
|
||||
{
|
||||
// Take extra precautions
|
||||
if (dst_idx < src_idx)
|
||||
{
|
||||
// Copy forwards
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy backwards
|
||||
for (int c = length - 1; c >= 0; c--)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Memory regions cannot overlap; just do a fast copy
|
||||
Array.Copy(array, src_idx, array, dst_idx, length);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemMoveInt(int[] array, int src_idx, int dst_idx, int length)
|
||||
{
|
||||
if (src_idx == dst_idx || length == 0)
|
||||
return;
|
||||
|
||||
// Do regions overlap?
|
||||
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
|
||||
{
|
||||
// Take extra precautions
|
||||
if (dst_idx < src_idx)
|
||||
{
|
||||
// Copy forwards
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy backwards
|
||||
for (int c = length - 1; c >= 0; c--)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Memory regions cannot overlap; just do a fast copy
|
||||
Array.Copy(array, src_idx, array, dst_idx, length);
|
||||
}
|
||||
}
|
||||
|
||||
internal static void MemMoveShort(short[] array, int src_idx, int dst_idx, int length)
|
||||
{
|
||||
if (src_idx == dst_idx || length == 0)
|
||||
return;
|
||||
|
||||
// Do regions overlap?
|
||||
if (src_idx + length > dst_idx || dst_idx + length > src_idx)
|
||||
{
|
||||
// Take extra precautions
|
||||
if (dst_idx < src_idx)
|
||||
{
|
||||
// Copy forwards
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy backwards
|
||||
for (int c = length - 1; c >= 0; c--)
|
||||
{
|
||||
array[c + dst_idx] = array[c + src_idx];
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Memory regions cannot overlap; just do a fast copy
|
||||
Array.Copy(array, src_idx, array, dst_idx, length);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
/* Copyright (c) 2016 Logan Stromberg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace Concentus.Common.CPlusPlus
|
||||
{
|
||||
public class BoxedValueInt
|
||||
{
|
||||
public int Val;
|
||||
|
||||
public BoxedValueInt(int v = 0)
|
||||
{
|
||||
Val = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Val.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class BoxedValueShort
|
||||
{
|
||||
public short Val;
|
||||
|
||||
public BoxedValueShort(short v = 0)
|
||||
{
|
||||
Val = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Val.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
public class BoxedValueSbyte
|
||||
{
|
||||
public sbyte Val;
|
||||
|
||||
public BoxedValueSbyte(sbyte v = 0)
|
||||
{
|
||||
Val = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Val.ToString();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// For performance reasons, do not use this generic class if possible
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class BoxedValue<T>
|
||||
{
|
||||
public T Val;
|
||||
|
||||
public BoxedValue(T v = default(T))
|
||||
{
|
||||
Val = v;
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
return Val == null ? "null" : Val.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,563 @@
|
||||
/* Copyright (c) 2016 Logan Stromberg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace Concentus.Common.CPlusPlus
|
||||
{
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// This simulates a C++ style pointer as far as can be implemented in C#. It represents a handle
|
||||
/// to an array of objects, along with a base offset that represents the address.
|
||||
/// When you are programming in debug mode, this class also enforces memory boundaries,
|
||||
/// tracks uninitialized values, and also records all statistics of accesses to its base array.
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
public class Pointer<T>
|
||||
{
|
||||
private const bool CHECK_UNINIT_MEM = false;
|
||||
|
||||
#if DEBUG && !NET35
|
||||
private class Statistics
|
||||
{
|
||||
public Statistics(int baseOffset)
|
||||
{
|
||||
this.baseOffset = baseOffset;
|
||||
}
|
||||
|
||||
public int baseOffset;
|
||||
public int minReadIndex = int.MaxValue;
|
||||
public int maxReadIndex = int.MinValue;
|
||||
public int minWriteIndex = int.MaxValue;
|
||||
public int maxWriteIndex = int.MinValue;
|
||||
|
||||
public Tuple<int, int> ReadRange
|
||||
{
|
||||
get
|
||||
{
|
||||
if (minReadIndex == int.MaxValue || maxReadIndex == int.MinValue)
|
||||
return null;
|
||||
return new Tuple<int, int>(minReadIndex - baseOffset, maxReadIndex - baseOffset);
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<int, int> WriteRange
|
||||
{
|
||||
get
|
||||
{
|
||||
if (minWriteIndex == int.MaxValue || maxWriteIndex == int.MinValue)
|
||||
return null;
|
||||
return new Tuple<int, int>(minWriteIndex - baseOffset, maxWriteIndex - baseOffset);
|
||||
}
|
||||
}
|
||||
}
|
||||
private bool[] _initialized;
|
||||
private Statistics _statistics;
|
||||
private int _length;
|
||||
#endif
|
||||
|
||||
private T[] _array;
|
||||
private int _offset;
|
||||
|
||||
public Pointer(int capacity)
|
||||
{
|
||||
_array = new T[capacity];
|
||||
_offset = 0;
|
||||
#if DEBUG && !NET35
|
||||
_length = capacity;
|
||||
_statistics = new Statistics(0);
|
||||
_initialized = new bool[capacity];
|
||||
for (int c = 0; c < capacity; c++)
|
||||
{
|
||||
_initialized[c] = false;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public Pointer(T[] buffer)
|
||||
{
|
||||
_array = buffer;
|
||||
_offset = 0;
|
||||
#if DEBUG && !NET35
|
||||
_length = buffer.Length;
|
||||
_statistics = new Statistics(0);
|
||||
_initialized = new bool[buffer.Length];
|
||||
for (int c = 0; c < buffer.Length; c++)
|
||||
{
|
||||
_initialized[c] = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
public Pointer(T[] buffer, int absoluteOffset)
|
||||
{
|
||||
_array = buffer;
|
||||
_offset = absoluteOffset;
|
||||
#if DEBUG && !NET35
|
||||
_length = buffer.Length - absoluteOffset;
|
||||
//Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array");
|
||||
_statistics = new Statistics(absoluteOffset);
|
||||
_initialized = new bool[buffer.Length];
|
||||
for (int c = 0; c < buffer.Length; c++)
|
||||
{
|
||||
_initialized[c] = true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if DEBUG && !NET35
|
||||
private Pointer(T[] buffer, int absoluteOffset, Statistics statistics, bool[] initializedStatus)
|
||||
{
|
||||
_array = buffer;
|
||||
_offset = absoluteOffset;
|
||||
_length = buffer.Length - absoluteOffset;
|
||||
//Inlines.OpusAssert(_length >= 0, "Attempted to point past the end of an array");
|
||||
_statistics = statistics;
|
||||
_initialized = initializedStatus;
|
||||
}
|
||||
|
||||
public Tuple<int, int> ReadRange
|
||||
{
|
||||
get
|
||||
{
|
||||
return _statistics.ReadRange;
|
||||
}
|
||||
}
|
||||
|
||||
public Tuple<int, int> WriteRange
|
||||
{
|
||||
get
|
||||
{
|
||||
return _statistics.WriteRange;
|
||||
}
|
||||
}
|
||||
|
||||
public int Length
|
||||
{
|
||||
get
|
||||
{
|
||||
return _length;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public int Offset
|
||||
{
|
||||
get
|
||||
{
|
||||
return _offset;
|
||||
}
|
||||
}
|
||||
|
||||
// This should only be temporary while I migrate from pointers to arrays
|
||||
public T[] Data
|
||||
{
|
||||
get
|
||||
{
|
||||
return _array;
|
||||
}
|
||||
}
|
||||
|
||||
public T this[int index]
|
||||
{
|
||||
get
|
||||
{
|
||||
#if DEBUG && !NET35
|
||||
#pragma warning disable 162
|
||||
if (CHECK_UNINIT_MEM) Inlines.OpusAssert(_initialized[index + _offset], "Attempted to read from uninitialized memory!");
|
||||
#pragma warning restore 162
|
||||
// Inlines.OpusAssert(index < _length, "Attempted to read past the end of an array!");
|
||||
_statistics.maxReadIndex = Math.Max(_statistics.maxReadIndex, index + _offset);
|
||||
_statistics.minReadIndex = Math.Min(_statistics.minReadIndex, index + _offset);
|
||||
#endif
|
||||
return _array[index + _offset];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
#if DEBUG && !NET35
|
||||
// Inlines.OpusAssert(index < _length, "Attempted to write past the end of an array!");
|
||||
_statistics.maxWriteIndex = Math.Max(_statistics.maxWriteIndex, index + _offset);
|
||||
_statistics.minWriteIndex = Math.Min(_statistics.minWriteIndex, index + _offset);
|
||||
_initialized[index + _offset] = true;
|
||||
#endif
|
||||
_array[index + _offset] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public T this[uint index]
|
||||
{
|
||||
get
|
||||
{
|
||||
return this[(int)index];
|
||||
}
|
||||
|
||||
set
|
||||
{
|
||||
this[(int)index] = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the value currently under the pointer, and returns a new pointer with +1 offset.
|
||||
/// This method is not very efficient because it creates new pointers; this is because we must preserve
|
||||
/// the pass-by-value nature of C++ pointers when they are used as arguments to functions
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public Pointer<T> Iterate(out T returnVal)
|
||||
{
|
||||
returnVal = _array[_offset];
|
||||
return Point(1);
|
||||
}
|
||||
|
||||
#if DEBUG && !NET35
|
||||
public Pointer<T> Point(int relativeOffset)
|
||||
{
|
||||
if (relativeOffset == 0) return this;
|
||||
return new Pointer<T>(_array, _offset + relativeOffset, _statistics, _initialized);
|
||||
}
|
||||
|
||||
public Pointer<T> Point(uint relativeOffset)
|
||||
{
|
||||
if (relativeOffset == 0) return this;
|
||||
return new Pointer<T>(_array, _offset + (int)relativeOffset, _statistics, _initialized);
|
||||
}
|
||||
#else
|
||||
public Pointer<T> Point(int relativeOffset)
|
||||
{
|
||||
if (relativeOffset == 0) return this;
|
||||
return new Pointer<T>(_array, _offset + relativeOffset);
|
||||
}
|
||||
|
||||
public Pointer<T> Point(uint relativeOffset)
|
||||
{
|
||||
if (relativeOffset == 0) return this;
|
||||
return new Pointer<T>(_array, _offset + (int)relativeOffset);
|
||||
}
|
||||
#endif
|
||||
|
||||
private static string invert_endianness(string hexstring)
|
||||
{
|
||||
StringBuilder b = new StringBuilder(hexstring.Length);
|
||||
for (int c = 0; c < hexstring.Length / 2; c++)
|
||||
{
|
||||
b.Append(hexstring.Substring(hexstring.Length - ((c + 1) * 2), 2));
|
||||
}
|
||||
return b.ToString();
|
||||
}
|
||||
|
||||
private static void PrintMemCopy<E>(E[] source, int sourceOffset, int length)
|
||||
{
|
||||
if (typeof(E) == typeof(int) || typeof(E) == typeof(uint))
|
||||
{
|
||||
Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 4));
|
||||
string buf = string.Empty;
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
buf += invert_endianness(string.Format("{0:x8}", source[c + sourceOffset]));
|
||||
}
|
||||
Debug.WriteLine(buf);
|
||||
}
|
||||
else if (typeof(E) == typeof(short) || typeof(E) == typeof(ushort))
|
||||
{
|
||||
Debug.WriteLine(string.Format("memcpy of {0} bytes", length * 2));
|
||||
string buf = string.Empty;
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
buf += invert_endianness(string.Format("{0:x4}", source[c + sourceOffset]));
|
||||
}
|
||||
Debug.WriteLine(buf);
|
||||
}
|
||||
else if (typeof(E) == typeof(byte) || typeof(E) == typeof(sbyte))
|
||||
{
|
||||
Debug.WriteLine(string.Format("memcpy of {0} bytes", length));
|
||||
string buf = string.Empty;
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
buf += invert_endianness(string.Format("{0:x2}", source[c + sourceOffset]));
|
||||
}
|
||||
Debug.WriteLine(buf);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this pointer, starting at its current address, into the space of another pointer.
|
||||
/// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!!
|
||||
/// IN C# IT IS (SOURCE, DEST, LENGTH). DON'T GET SCOOPED LIKE I DID
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="length"></param>
|
||||
#if DEBUG
|
||||
public void MemCopyTo(Pointer<T> destination, int length, bool debug = false)
|
||||
{
|
||||
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
|
||||
if (debug)
|
||||
PrintMemCopy(_array, _offset, length);
|
||||
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
destination[c] = _array[c + _offset];
|
||||
}
|
||||
}
|
||||
#else
|
||||
public void MemCopyTo(Pointer<T> destination, int length)
|
||||
{
|
||||
if (destination is Pointer<T>)
|
||||
{
|
||||
// Use the fast way if we have access to the base array
|
||||
Array.Copy(_array, _offset, ((Pointer<T>)destination)._array, destination.Offset, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise do it the slow way
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
destination[c] = _array[c + _offset];
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Copies the contents of this pointer, starting at its current address, into an array.
|
||||
/// !!! IMPORTANT !!! REMEMBER THAT C++ memcpy is (DEST, SOURCE, LENGTH) !!!!
|
||||
/// </summary>
|
||||
/// <param name="destination"></param>
|
||||
/// <param name="length"></param>
|
||||
#if DEBUG
|
||||
public void MemCopyTo(T[] destination, int destOffset, int length)
|
||||
{
|
||||
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
|
||||
//PrintMemCopy(_array, _offset, length);
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
destination[c + destOffset] = _array[c + _offset];
|
||||
}
|
||||
}
|
||||
#else
|
||||
public void MemCopyTo(T[] destination, int offset, int length)
|
||||
{
|
||||
// Use the fast way if we have access to the base array
|
||||
Array.Copy(_array, _offset, destination, offset, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Loads N values from a source array into this pointer's space
|
||||
/// </summary>
|
||||
/// <param name="length"></param>
|
||||
#if DEBUG && !NET35
|
||||
public void MemCopyFrom(T[] source, int sourceOffset, int length)
|
||||
{
|
||||
Inlines.OpusAssert(length >= 0, "Cannot memcopy() with a negative length!");
|
||||
//PrintMemCopy(source, sourceOffset, length);
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
_array[c + _offset] = source[c + sourceOffset];
|
||||
_initialized[c + _offset] = true;
|
||||
}
|
||||
}
|
||||
#else
|
||||
public void MemCopyFrom(T[] source, int sourceOffset, int length)
|
||||
{
|
||||
Array.Copy(source, sourceOffset, _array, _offset, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a certain value to a range of spaces in this array
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set</param>
|
||||
/// <param name="length">The number of values to write</param>
|
||||
public void MemSet(T value, int length)
|
||||
{
|
||||
#if DEBUG
|
||||
Inlines.OpusAssert(length >= 0, "Cannot memset() with a negative length!");
|
||||
#endif
|
||||
MemSet(value, (uint)length);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assigns a certain value to a range of spaces in this array
|
||||
/// </summary>
|
||||
/// <param name="value">The value to set</param>
|
||||
/// <param name="length">The number of values to write</param>
|
||||
public void MemSet(T value, uint length)
|
||||
{
|
||||
for (int c = _offset; c < _offset + length; c++)
|
||||
{
|
||||
_array[c] = value;
|
||||
#if DEBUG && !NET35
|
||||
_initialized[c] = true;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
public void MemMoveTo(Pointer<T> other, int length)
|
||||
{
|
||||
if (_array == other._array)
|
||||
{
|
||||
// Pointers refer to the same array, perform a move
|
||||
//if (debug)
|
||||
// PrintMemCopy(_array, _offset, length);
|
||||
MemMove(other.Offset - Offset, length);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Pointers refer to different arrays (if you end up here you probably wanted to just to MemCopy())
|
||||
// Debug.WriteLine("Unnecessary memmove detected");
|
||||
MemCopyTo(other, length);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Moves regions of memory within the bounds of this pointer's array.
|
||||
/// Extra checks are done to ensure that the data is not corrupted if the copy
|
||||
/// regions overlap
|
||||
/// </summary>
|
||||
/// <param name="move_dist">The offset to send this pointer's data to</param>
|
||||
/// <param name="length">The number of values to copy</param>
|
||||
#if DEBUG && !NET35
|
||||
public void MemMove(int move_dist, int length)
|
||||
{
|
||||
Inlines.OpusAssert(length >= 0, "Cannot memmove() with a negative length!");
|
||||
if (move_dist == 0 || length == 0)
|
||||
return;
|
||||
|
||||
// Do regions overlap?
|
||||
if ((move_dist > 0 && move_dist < length) || (move_dist < 0 && 0 - move_dist > length))
|
||||
{
|
||||
// Take extra precautions
|
||||
if (move_dist < 0)
|
||||
{
|
||||
// Copy forwards
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
_array[c + _offset + move_dist] = _array[c + _offset];
|
||||
_initialized[c + _offset + move_dist] = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Copy backwards
|
||||
for (int c = length - 1; c >= 0; c--)
|
||||
{
|
||||
_array[c + _offset + move_dist] = _array[c + _offset];
|
||||
_initialized[c + _offset + move_dist] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int c = 0; c < length; c++)
|
||||
{
|
||||
_array[c + _offset + move_dist] = _array[c + _offset];
|
||||
_initialized[c + _offset + move_dist] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
public void MemMove(int move_dist, int length)
|
||||
{
|
||||
Arrays.MemMove(_array, _offset, _offset + move_dist, length);
|
||||
}
|
||||
#endif
|
||||
|
||||
/*/// <summary>
|
||||
/// Simulates pointer zooming: newPtr = &ptr[offset].
|
||||
/// Returns a pointer that is offset from this one within the same buffer.
|
||||
/// </summary>
|
||||
/// <param name="arg"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
internal static Pointer<T> operator +(Pointer<T> arg, int offset)
|
||||
{
|
||||
return new Pointer<T>(arg._array, arg._offset + offset);
|
||||
}*/
|
||||
|
||||
public override bool Equals(object obj)
|
||||
{
|
||||
if (obj == null || GetType() != obj.GetType())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Pointer<T> other = (Pointer<T>)obj;
|
||||
return other._offset == _offset &&
|
||||
other._array == _array;
|
||||
}
|
||||
|
||||
public override int GetHashCode()
|
||||
{
|
||||
return _array.GetHashCode() + _offset.GetHashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// This is a helper class which contains static methods that involve pointers
|
||||
/// </summary>
|
||||
public static class Pointer
|
||||
{
|
||||
/// <summary>
|
||||
/// Allocates a new array and returns a pointer to it
|
||||
/// </summary>
|
||||
/// <typeparam name="E"></typeparam>
|
||||
/// <param name="capacity"></param>
|
||||
/// <returns></returns>
|
||||
public static Pointer<E> Malloc<E>(int capacity)
|
||||
{
|
||||
//this returns a pointer inside of a random field, to make sure offset indexing works properly
|
||||
//E[] field = new E[capacity * 2];
|
||||
//return new Pointer<E>(field, new Random().Next(0, capacity - 1));
|
||||
return new Pointer<E>(capacity);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a pointer to an existing array
|
||||
/// </summary>
|
||||
/// <typeparam name="E"></typeparam>
|
||||
/// <param name="memory"></param>
|
||||
/// <param name="offset"></param>
|
||||
/// <returns></returns>
|
||||
public static Pointer<E> GetPointer<E>(this E[] memory, int offset = 0)
|
||||
{
|
||||
if (memory == null)
|
||||
return null;
|
||||
//if (Debugger.IsAttached && offset == memory.Length / 2)
|
||||
//{
|
||||
// // This may be a partitioned array. Signal the debugger
|
||||
// Debugger.Break();
|
||||
//}
|
||||
return new Pointer<E>(memory, offset);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,790 @@
|
||||
/* Copyright (c) 2001-2011 Timothy B. Terriberry
|
||||
Ported to C# by Logan Stromberg
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
are met:
|
||||
|
||||
- Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
- Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
|
||||
- Neither the name of Internet Society, IETF or IETF Trust, nor the
|
||||
names of specific contributors, may be used to endorse or promote
|
||||
products derived from this software without specific prior written
|
||||
permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
|
||||
OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
namespace Concentus.Common
|
||||
{
|
||||
using Concentus.Common;
|
||||
using Concentus.Common.CPlusPlus;
|
||||
using System.Diagnostics;
|
||||
|
||||
/*A range decoder.
|
||||
This is an entropy decoder based upon \cite{Mar79}, which is itself a
|
||||
rediscovery of the FIFO arithmetic code introduced by \cite{Pas76}.
|
||||
It is very similar to arithmetic encoding, except that encoding is done with
|
||||
digits in any base, instead of with bits, and so it is faster when using
|
||||
larger bases (i.e.: a byte).
|
||||
The author claims an average waste of $\frac{1}{2}\log_b(2b)$ bits, where $b$
|
||||
is the base, longer than the theoretical optimum, but to my knowledge there
|
||||
is no published justification for this claim.
|
||||
This only seems true when using near-infinite precision arithmetic so that
|
||||
the process is carried out with no rounding errors.
|
||||
|
||||
An excellent description of implementation details is available at
|
||||
http://www.arturocampos.com/ac_range.html
|
||||
A recent work \cite{MNW98} which proposes several changes to arithmetic
|
||||
encoding for efficiency actually re-discovers many of the principles
|
||||
behind range encoding, and presents a good theoretical analysis of them.
|
||||
|
||||
End of stream is handled by writing out the smallest number of bits that
|
||||
ensures that the stream will be correctly decoded regardless of the value of
|
||||
any subsequent bits.
|
||||
ec_tell() can be used to determine how many bits were needed to decode
|
||||
all the symbols thus far; other data can be packed in the remaining bits of
|
||||
the input buffer.
|
||||
@PHDTHESIS{Pas76,
|
||||
author="Richard Clark Pasco",
|
||||
title="Source coding algorithms for fast data compression",
|
||||
school="Dept. of Electrical Engineering, Stanford University",
|
||||
address="Stanford, CA",
|
||||
month=May,
|
||||
year=1976
|
||||
}
|
||||
@INPROCEEDINGS{Mar79,
|
||||
author="Martin, G.N.N.",
|
||||
title="Range encoding: an algorithm for removing redundancy from a digitised
|
||||
message",
|
||||
booktitle="Video & Data Recording Conference",
|
||||
year=1979,
|
||||
address="Southampton",
|
||||
month=Jul
|
||||
}
|
||||
@ARTICLE{MNW98,
|
||||
author="Alistair Moffat and Radford Neal and Ian H. Witten",
|
||||
title="Arithmetic Coding Revisited",
|
||||
journal="{ACM} Transactions on Information Systems",
|
||||
year=1998,
|
||||
volume=16,
|
||||
number=3,
|
||||
pages="256--294",
|
||||
month=Jul,
|
||||
URL="http://www.stanford.edu/class/ee398a/handouts/papers/Moffat98ArithmCoding.pdf"
|
||||
}*/
|
||||
internal class EntropyCoder
|
||||
{
|
||||
private const int EC_WINDOW_SIZE = ((int)sizeof(uint) * 8);
|
||||
|
||||
///*The number of bits to use for the range-coded part of uint integers.*/
|
||||
private const int EC_UINT_BITS = 8;
|
||||
|
||||
///*The resolution of fractional-precision bit usage measurements, i.e.,
|
||||
// 3 => 1/8th bits.*/
|
||||
public const int BITRES = 3;
|
||||
|
||||
/*The number of bits to output at a time.*/
|
||||
private const int EC_SYM_BITS = (8);
|
||||
|
||||
/*The total number of bits in each of the state registers.*/
|
||||
private const int EC_CODE_BITS = (32);
|
||||
|
||||
/*The maximum symbol value.*/
|
||||
private const uint EC_SYM_MAX = ((1U << EC_SYM_BITS) - 1);
|
||||
|
||||
/*Bits to shift by to move a symbol into the high-order position.*/
|
||||
private const uint EC_CODE_SHIFT = (EC_CODE_BITS - EC_SYM_BITS - 1);
|
||||
|
||||
/*Carry bit of the high-order range symbol.*/
|
||||
private const uint EC_CODE_TOP = ((1U) << (EC_CODE_BITS - 1));
|
||||
|
||||
/*Low-order bit of the high-order range symbol.*/
|
||||
private const uint EC_CODE_BOT = (EC_CODE_TOP >> EC_SYM_BITS);
|
||||
|
||||
/*The number of bits available for the last, partial symbol in the code field.*/
|
||||
private const int EC_CODE_EXTRA = ((EC_CODE_BITS - 2) % EC_SYM_BITS + 1);
|
||||
|
||||
//////////////// Coder State ////////////////////
|
||||
|
||||
/*POINTER to Buffered input/output.*/
|
||||
public byte[] buf;
|
||||
public int buf_ptr;
|
||||
|
||||
/*The size of the buffer.*/
|
||||
public uint storage;
|
||||
|
||||
/*The offset at which the last byte containing raw bits was read/written.*/
|
||||
public uint end_offs;
|
||||
|
||||
/*Bits that will be read from/written at the end.*/
|
||||
public uint end_window;
|
||||
|
||||
/*Number of valid bits in end_window.*/
|
||||
public int nend_bits;
|
||||
|
||||
/*The total number of whole bits read/written.
|
||||
This does not include partial bits currently in the range coder.*/
|
||||
public int nbits_total;
|
||||
|
||||
/*The offset at which the next range coder byte will be read/written.*/
|
||||
public uint offs;
|
||||
|
||||
/*The number of values in the current range.*/
|
||||
public uint rng;
|
||||
|
||||
/*In the decoder: the difference between the top of the current range and
|
||||
the input value, minus one.
|
||||
In the encoder: the low end of the current range.*/
|
||||
public uint val;
|
||||
|
||||
/*In the decoder: the saved normalization factor from ec_decode().
|
||||
In the encoder: the number of oustanding carry propagating symbols.*/
|
||||
public uint ext;
|
||||
|
||||
/*A buffered input/output symbol, awaiting carry propagation.*/
|
||||
public int rem;
|
||||
|
||||
/*Nonzero if an error occurred.*/
|
||||
public int error;
|
||||
|
||||
public EntropyCoder()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
buf = null;
|
||||
buf_ptr = 0;
|
||||
storage = 0;
|
||||
end_offs = 0;
|
||||
end_window = 0;
|
||||
nend_bits = 0;
|
||||
offs = 0;
|
||||
rng = 0;
|
||||
val = 0;
|
||||
ext = 0;
|
||||
rem = 0;
|
||||
error = 0;
|
||||
}
|
||||
|
||||
public void Assign(EntropyCoder other)
|
||||
{
|
||||
this.buf = other.buf;
|
||||
this.buf_ptr = other.buf_ptr;
|
||||
this.storage = other.storage;
|
||||
this.end_offs = other.end_offs;
|
||||
this.end_window = other.end_window;
|
||||
this.nend_bits = other.nend_bits;
|
||||
this.nbits_total = other.nbits_total;
|
||||
this.offs = other.offs;
|
||||
this.rng = other.rng;
|
||||
this.val = other.val;
|
||||
this.ext = other.ext;
|
||||
this.rem = other.rem;
|
||||
this.error = other.error;
|
||||
}
|
||||
|
||||
internal int read_byte()
|
||||
{
|
||||
return this.offs < this.storage ? this.buf[buf_ptr + this.offs++] : 0;
|
||||
}
|
||||
|
||||
internal int read_byte_from_end()
|
||||
{
|
||||
return this.end_offs < this.storage ?
|
||||
this.buf[buf_ptr + (this.storage - ++(this.end_offs))] : 0;
|
||||
}
|
||||
|
||||
internal int write_byte(uint _value)
|
||||
{
|
||||
if (this.offs + this.end_offs >= this.storage)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
this.buf[buf_ptr + this.offs++] = (byte)_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
internal int write_byte_at_end(uint _value)
|
||||
{
|
||||
if (this.offs + this.end_offs >= this.storage)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
this.buf[buf_ptr + (this.storage - ++(this.end_offs))] = (byte)_value;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Normalizes the contents of val and rng so that rng lies entirely in the high-order symbol.
|
||||
/// </summary>
|
||||
internal void dec_normalize()
|
||||
{
|
||||
/*If the range is too small, rescale it and input some bits.*/
|
||||
while (this.rng <= EC_CODE_BOT)
|
||||
{
|
||||
int sym;
|
||||
this.nbits_total += EC_SYM_BITS;
|
||||
this.rng <<= EC_SYM_BITS;
|
||||
|
||||
/*Use up the remaining bits from our last symbol.*/
|
||||
sym = this.rem;
|
||||
|
||||
/*Read the next value from the input.*/
|
||||
this.rem = read_byte();
|
||||
|
||||
/*Take the rest of the bits we need from this new symbol.*/
|
||||
sym = (sym << EC_SYM_BITS | this.rem) >> (EC_SYM_BITS - EC_CODE_EXTRA);
|
||||
|
||||
/*And subtract them from val, capped to be less than EC_CODE_TOP.*/
|
||||
this.val = (uint)((this.val << EC_SYM_BITS) + (EC_SYM_MAX & ~sym)) & (EC_CODE_TOP - 1);
|
||||
}
|
||||
}
|
||||
|
||||
internal void dec_init(byte[] _buf, int _buf_ptr, uint _storage)
|
||||
{
|
||||
this.buf = _buf;
|
||||
this.buf_ptr = _buf_ptr;
|
||||
this.storage = _storage;
|
||||
this.end_offs = 0;
|
||||
this.end_window = 0;
|
||||
this.nend_bits = 0;
|
||||
/*This is the offset from which ec_tell() will subtract partial bits.
|
||||
The final value after the ec_dec_normalize() call will be the same as in
|
||||
the encoder, but we have to compensate for the bits that are added there.*/
|
||||
this.nbits_total = EC_CODE_BITS + 1
|
||||
- ((EC_CODE_BITS - EC_CODE_EXTRA) / EC_SYM_BITS) * EC_SYM_BITS;
|
||||
this.offs = 0;
|
||||
this.rng = 1U << EC_CODE_EXTRA;
|
||||
this.rem = read_byte();
|
||||
this.val = this.rng - 1 - (uint)(this.rem >> (EC_SYM_BITS - EC_CODE_EXTRA));
|
||||
this.error = 0;
|
||||
/*Normalize the interval.*/
|
||||
dec_normalize();
|
||||
}
|
||||
|
||||
internal uint decode(uint _ft)
|
||||
{
|
||||
uint s;
|
||||
this.ext = this.rng / _ft;
|
||||
s = (uint)(this.val / this.ext);
|
||||
return _ft - Inlines.EC_MINI(s + 1, _ft);
|
||||
}
|
||||
|
||||
internal uint decode_bin(uint _bits)
|
||||
{
|
||||
uint s;
|
||||
this.ext = this.rng >> (int)_bits;
|
||||
s = (uint)(this.val / this.ext);
|
||||
return (1U << (int)_bits) - Inlines.EC_MINI(s + 1U, 1U << (int)_bits);
|
||||
}
|
||||
|
||||
internal void dec_update(uint _fl, uint _fh, uint _ft)
|
||||
{
|
||||
uint s;
|
||||
s = this.ext * (_ft - _fh);
|
||||
this.val -= s;
|
||||
this.rng = _fl > 0 ? this.ext * (_fh - _fl) : this.rng - s;
|
||||
dec_normalize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The probability of having a "one" is 1/(1<<_logp).
|
||||
/// </summary>
|
||||
/// <param name="_logp"></param>
|
||||
/// <returns></returns>
|
||||
internal int dec_bit_logp(uint _logp)
|
||||
{
|
||||
uint r;
|
||||
uint d;
|
||||
uint s;
|
||||
int ret;
|
||||
r = this.rng;
|
||||
d = this.val;
|
||||
s = r >> (int)_logp;
|
||||
ret = d < s ? 1 : 0;
|
||||
if (ret == 0) this.val = d - s;
|
||||
this.rng = ret != 0 ? s : r - s;
|
||||
dec_normalize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal int dec_icdf(byte[] _icdf, uint _ftb)
|
||||
{
|
||||
uint r;
|
||||
uint d;
|
||||
uint s;
|
||||
uint t;
|
||||
int ret;
|
||||
s = this.rng;
|
||||
d = this.val;
|
||||
r = s >> (int)_ftb;
|
||||
ret = -1;
|
||||
do
|
||||
{
|
||||
t = s;
|
||||
s = r * _icdf[++ret];
|
||||
}
|
||||
while (d < s);
|
||||
this.val = d - s;
|
||||
this.rng = t - s;
|
||||
dec_normalize();
|
||||
return ret;
|
||||
}
|
||||
|
||||
internal int dec_icdf(byte[] _icdf, int _icdf_offset, uint _ftb)
|
||||
{
|
||||
uint r;
|
||||
uint d;
|
||||
uint s;
|
||||
uint t;
|
||||
int ret;
|
||||
s = this.rng;
|
||||
d = this.val;
|
||||
r = s >> (int)_ftb;
|
||||
ret = _icdf_offset - 1;
|
||||
do
|
||||
{
|
||||
t = s;
|
||||
s = r * _icdf[++ret];
|
||||
}
|
||||
while (d < s);
|
||||
this.val = d - s;
|
||||
this.rng = t - s;
|
||||
dec_normalize();
|
||||
return ret - _icdf_offset;
|
||||
}
|
||||
|
||||
internal uint dec_uint(uint _ft)
|
||||
{
|
||||
uint ft;
|
||||
uint s;
|
||||
int ftb;
|
||||
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/
|
||||
Inlines.OpusAssert(_ft > 1);
|
||||
_ft--;
|
||||
ftb = Inlines.EC_ILOG(_ft);
|
||||
if (ftb > EC_UINT_BITS)
|
||||
{
|
||||
uint t;
|
||||
ftb -= EC_UINT_BITS;
|
||||
ft = (uint)(_ft >> ftb) + 1;
|
||||
s = decode(ft);
|
||||
dec_update(s, s + 1, ft);
|
||||
t = (uint)s << ftb | dec_bits((uint)ftb);
|
||||
if (t <= _ft) return t;
|
||||
this.error = 1;
|
||||
return _ft;
|
||||
}
|
||||
else {
|
||||
_ft++;
|
||||
s = decode((uint)_ft);
|
||||
dec_update(s, s + 1, (uint)_ft);
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
internal uint dec_bits(uint _bits)
|
||||
{
|
||||
uint window;
|
||||
int available;
|
||||
uint ret;
|
||||
window = this.end_window;
|
||||
available = this.nend_bits;
|
||||
if ((uint)available < _bits)
|
||||
{
|
||||
do
|
||||
{
|
||||
window |= (uint)read_byte_from_end() << available;
|
||||
available += EC_SYM_BITS;
|
||||
}
|
||||
while (available <= EC_WINDOW_SIZE - EC_SYM_BITS);
|
||||
}
|
||||
ret = (uint)window & (((uint)1 << (int)_bits) - 1U);
|
||||
window = window >> (int)_bits;
|
||||
available = available - (int)_bits;
|
||||
this.end_window = window;
|
||||
this.nend_bits = available;
|
||||
this.nbits_total = this.nbits_total + (int)_bits;
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Outputs a symbol, with a carry bit.
|
||||
/// If there is a potential to propagate a carry over several symbols, they are
|
||||
/// buffered until it can be determined whether or not an actual carry will
|
||||
/// occur.
|
||||
/// If the counter for the buffered symbols overflows, then the stream becomes
|
||||
/// undecodable.
|
||||
/// This gives a theoretical limit of a few billion symbols in a single packet on
|
||||
/// 32-bit systems.
|
||||
/// The alternative is to truncate the range in order to force a carry, but
|
||||
/// requires similar carry tracking in the decoder, needlessly slowing it down.
|
||||
/// </summary>
|
||||
/// <param name="_c"></param>
|
||||
internal void enc_carry_out(int _c)
|
||||
{
|
||||
if (_c != EC_SYM_MAX)
|
||||
{
|
||||
/*No further carry propagation possible, flush buffer.*/
|
||||
int carry;
|
||||
carry = _c >> EC_SYM_BITS;
|
||||
|
||||
/*Don't output a byte on the first write.
|
||||
This compare should be taken care of by branch-prediction thereafter.*/
|
||||
if (this.rem >= 0)
|
||||
{
|
||||
this.error |= write_byte((uint)(this.rem + carry));
|
||||
}
|
||||
|
||||
if (this.ext > 0)
|
||||
{
|
||||
uint sym;
|
||||
sym = (EC_SYM_MAX + (uint)carry) & EC_SYM_MAX;
|
||||
do this.error |= write_byte(sym);
|
||||
while (--(this.ext) > 0);
|
||||
}
|
||||
|
||||
this.rem = (int)((uint)_c & EC_SYM_MAX);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.ext++;
|
||||
}
|
||||
}
|
||||
|
||||
internal void enc_normalize()
|
||||
{
|
||||
/*If the range is too small, output some bits and rescale it.*/
|
||||
while (this.rng <= EC_CODE_BOT)
|
||||
{
|
||||
enc_carry_out((int)(this.val >> (int)EC_CODE_SHIFT));
|
||||
/*Move the next-to-high-order symbol into the high-order position.*/
|
||||
this.val = (this.val << EC_SYM_BITS) & (EC_CODE_TOP - 1);
|
||||
this.rng = this.rng << EC_SYM_BITS;
|
||||
this.nbits_total += EC_SYM_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
internal void enc_init(byte[] _buf, int buf_ptr, uint _size)
|
||||
{
|
||||
this.buf = _buf;
|
||||
this.buf_ptr = buf_ptr;
|
||||
this.end_offs = 0;
|
||||
this.end_window = 0;
|
||||
this.nend_bits = 0;
|
||||
/*This is the offset from which ec_tell() will subtract partial bits.*/
|
||||
this.nbits_total = EC_CODE_BITS + 1;
|
||||
this.offs = 0;
|
||||
this.rng = EC_CODE_TOP;
|
||||
this.rem = -1;
|
||||
this.val = 0;
|
||||
this.ext = 0;
|
||||
this.storage = _size;
|
||||
this.error = 0;
|
||||
}
|
||||
|
||||
internal void encode(uint _fl, uint _fh, uint _ft)
|
||||
{
|
||||
uint r;
|
||||
r = this.rng / _ft;
|
||||
if (_fl > 0)
|
||||
{
|
||||
this.val += this.rng - (r * (_ft - _fl));
|
||||
this.rng = (r * (_fh - _fl));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rng -= (r * (_ft - _fh));
|
||||
}
|
||||
|
||||
enc_normalize();
|
||||
}
|
||||
|
||||
internal void encode_bin(uint _fl, uint _fh, uint _bits)
|
||||
{
|
||||
uint r;
|
||||
r = this.rng >> (int)_bits;
|
||||
if (_fl > 0)
|
||||
{
|
||||
this.val += this.rng - (r * ((1U << (int)_bits) - _fl));
|
||||
this.rng = (r * (_fh - _fl));
|
||||
}
|
||||
else this.rng -= (r * ((1U << (int)_bits) - _fh));
|
||||
enc_normalize();
|
||||
}
|
||||
|
||||
/*The probability of having a "one" is 1/(1<<_logp).*/
|
||||
internal void enc_bit_logp(int _val, uint _logp)
|
||||
{
|
||||
uint r;
|
||||
uint s;
|
||||
uint l;
|
||||
r = this.rng;
|
||||
l = this.val;
|
||||
s = r >> (int)_logp;
|
||||
r -= s;
|
||||
if (_val != 0)
|
||||
{
|
||||
this.val = l + r;
|
||||
}
|
||||
|
||||
this.rng = _val != 0 ? s : r;
|
||||
enc_normalize();
|
||||
}
|
||||
|
||||
internal void enc_icdf(int _s, byte[] _icdf, uint _ftb)
|
||||
{
|
||||
uint r;
|
||||
r = this.rng >> (int)_ftb;
|
||||
if (_s > 0)
|
||||
{
|
||||
this.val += this.rng - (r * _icdf[_s - 1]);
|
||||
this.rng = (r * (uint)(_icdf[_s - 1] - _icdf[_s]));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rng -= (r * _icdf[_s]);
|
||||
}
|
||||
enc_normalize();
|
||||
}
|
||||
|
||||
internal void enc_icdf(int _s, byte[] _icdf, int icdf_ptr, uint _ftb)
|
||||
{
|
||||
uint r;
|
||||
r = this.rng >> (int)_ftb;
|
||||
if (_s > 0)
|
||||
{
|
||||
this.val += this.rng - (r * _icdf[icdf_ptr + _s - 1]);
|
||||
this.rng = (r * (uint)(_icdf[icdf_ptr + _s - 1] - _icdf[icdf_ptr + _s]));
|
||||
}
|
||||
else
|
||||
{
|
||||
this.rng -= (r * _icdf[icdf_ptr + _s]);
|
||||
}
|
||||
enc_normalize();
|
||||
}
|
||||
|
||||
internal void enc_uint(uint _fl, uint _ft)
|
||||
{
|
||||
uint ft;
|
||||
uint fl;
|
||||
int ftb;
|
||||
/*In order to optimize EC_ILOG(), it is undefined for the value 0.*/
|
||||
Inlines.OpusAssert(_ft > 1);
|
||||
_ft--;
|
||||
ftb = Inlines.EC_ILOG(_ft);
|
||||
if (ftb > EC_UINT_BITS)
|
||||
{
|
||||
ftb -= EC_UINT_BITS;
|
||||
ft = (_ft >> ftb) + 1;
|
||||
fl = (uint)(_fl >> ftb);
|
||||
encode(fl, fl + 1, ft);
|
||||
enc_bits(_fl & (((uint)1 << ftb) - 1U), (uint)ftb);
|
||||
}
|
||||
else encode(_fl, _fl + 1, _ft + 1);
|
||||
}
|
||||
|
||||
internal void enc_bits(uint _fl, uint _bits)
|
||||
{
|
||||
uint window;
|
||||
int used;
|
||||
window = this.end_window;
|
||||
used = this.nend_bits;
|
||||
Inlines.OpusAssert(_bits > 0);
|
||||
|
||||
if (used + _bits > EC_WINDOW_SIZE)
|
||||
{
|
||||
do
|
||||
{
|
||||
this.error |= write_byte_at_end((uint)window & EC_SYM_MAX);
|
||||
window >>= EC_SYM_BITS;
|
||||
used -= EC_SYM_BITS;
|
||||
}
|
||||
while (used >= EC_SYM_BITS);
|
||||
}
|
||||
|
||||
window |= (uint)_fl << used;
|
||||
used += (int)_bits;
|
||||
this.end_window = window;
|
||||
this.nend_bits = used;
|
||||
this.nbits_total += (int)_bits;
|
||||
}
|
||||
|
||||
internal void enc_patch_initial_bits(uint _val, uint _nbits)
|
||||
{
|
||||
int shift;
|
||||
uint mask;
|
||||
Inlines.OpusAssert(_nbits <= EC_SYM_BITS);
|
||||
shift = EC_SYM_BITS - (int)_nbits;
|
||||
mask = ((1U << (int)_nbits) - 1) << shift;
|
||||
|
||||
if (this.offs > 0)
|
||||
{
|
||||
/*The first byte has been finalized.*/
|
||||
this.buf[buf_ptr] = (byte)((this.buf[buf_ptr] & ~mask) | _val << shift);
|
||||
}
|
||||
else if (this.rem >= 0)
|
||||
{
|
||||
/*The first byte is still awaiting carry propagation.*/
|
||||
this.rem = (int)(((uint)this.rem & ~mask) | _val) << shift;
|
||||
}
|
||||
else if (this.rng <= (EC_CODE_TOP >> (int)_nbits))
|
||||
{
|
||||
/*The renormalization loop has never been run.*/
|
||||
this.val = (this.val & ~((uint)mask << (int)EC_CODE_SHIFT)) |
|
||||
(uint)_val << (int)(EC_CODE_SHIFT + shift);
|
||||
}
|
||||
else
|
||||
{
|
||||
/*The encoder hasn't even encoded _nbits of data yet.*/
|
||||
this.error = -1;
|
||||
}
|
||||
}
|
||||
|
||||
internal void enc_shrink(uint _size)
|
||||
{
|
||||
Inlines.OpusAssert(this.offs + this.end_offs <= _size);
|
||||
//(memmove(this.buf + _size - this.end_offs, this.buf + this.storage - this.end_offs, this.end_offs * sizeof(*(dst))))
|
||||
Arrays.MemMove<byte>(this.buf, buf_ptr + (int)_size - (int)this.end_offs, buf_ptr + (int)this.storage - (int)this.end_offs, (int)this.end_offs);
|
||||
this.storage = _size;
|
||||
}
|
||||
|
||||
internal uint range_bytes()
|
||||
{
|
||||
return this.offs;
|
||||
}
|
||||
|
||||
internal int get_error()
|
||||
{
|
||||
return this.error;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of bits "used" by the encoded or decoded symbols so far.
|
||||
/// This same number can be computed in either the encoder or the decoder, and is
|
||||
/// suitable for making coding decisions.
|
||||
/// This will always be slightly larger than the exact value (e.g., all
|
||||
/// rounding error is in the positive direction).
|
||||
/// </summary>
|
||||
/// <returns>The number of bits.</returns>
|
||||
internal int tell()
|
||||
{
|
||||
int returnVal = this.nbits_total - Inlines.EC_ILOG(this.rng);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
private static readonly uint[] correction = {35733, 38967, 42495, 46340, 50535, 55109, 60097, 65535};
|
||||
|
||||
/// <summary>
|
||||
/// This is a faster version of ec_tell_frac() that takes advantage
|
||||
/// of the low(1/8 bit) resolution to use just a linear function
|
||||
/// followed by a lookup to determine the exact transition thresholds.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
internal uint tell_frac()
|
||||
{
|
||||
int nbits;
|
||||
int r;
|
||||
int l;
|
||||
uint b;
|
||||
nbits = this.nbits_total << EntropyCoder.BITRES;
|
||||
l = Inlines.EC_ILOG(this.rng);
|
||||
r = (int)(this.rng >> (l - 16));
|
||||
b = (uint)((r >> 12) - 8);
|
||||
b += (r > correction[b] ? 1u : 0);
|
||||
l = (int)((l << 3) + b);
|
||||
return (uint)(nbits - l);
|
||||
}
|
||||
|
||||
internal void enc_done()
|
||||
{
|
||||
uint window;
|
||||
int used;
|
||||
uint msk;
|
||||
uint end;
|
||||
int l;
|
||||
/*We output the minimum number of bits that ensures that the symbols encoded
|
||||
thus far will be decoded correctly regardless of the bits that follow.*/
|
||||
l = EC_CODE_BITS - Inlines.EC_ILOG(this.rng);
|
||||
msk = (EC_CODE_TOP - 1) >> l;
|
||||
end = (this.val + msk) & ~msk;
|
||||
|
||||
if ((end | msk) >= this.val + this.rng)
|
||||
{
|
||||
l++;
|
||||
msk >>= 1;
|
||||
end = (this.val + msk) & ~msk;
|
||||
}
|
||||
|
||||
while (l > 0)
|
||||
{
|
||||
enc_carry_out((int)(end >> (int)EC_CODE_SHIFT));
|
||||
end = (end << EC_SYM_BITS) & (EC_CODE_TOP - 1);
|
||||
l -= EC_SYM_BITS;
|
||||
}
|
||||
|
||||
/*If we have a buffered byte flush it into the output buffer.*/
|
||||
if (this.rem >= 0 || this.ext > 0)
|
||||
{
|
||||
enc_carry_out(0);
|
||||
}
|
||||
|
||||
/*If we have buffered extra bits, flush them as well.*/
|
||||
window = this.end_window;
|
||||
used = this.nend_bits;
|
||||
|
||||
while (used >= EC_SYM_BITS)
|
||||
{
|
||||
this.error |= write_byte_at_end((uint)window & EC_SYM_MAX);
|
||||
window >>= EC_SYM_BITS;
|
||||
used -= EC_SYM_BITS;
|
||||
}
|
||||
|
||||
/*Clear any excess space and add any remaining extra bits to the last byte.*/
|
||||
if (this.error == 0)
|
||||
{
|
||||
Arrays.MemSetWithOffset<byte>(this.buf, 0, buf_ptr + (int)this.offs, (int)this.storage - (int)this.offs - (int)this.end_offs);
|
||||
if (used > 0)
|
||||
{
|
||||
/*If there's no range coder data at all, give up.*/
|
||||
if (this.end_offs >= this.storage)
|
||||
{
|
||||
this.error = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
l = -l;
|
||||
/*If we've busted, don't add too many extra bits to the last byte; it
|
||||
would corrupt the range coder data, and that's more important.*/
|
||||
if (this.offs + this.end_offs >= this.storage && l < used)
|
||||
{
|
||||
window = window & ((1U << l) - 1);
|
||||
this.error = -1;
|
||||
}
|
||||
|
||||
this.buf[buf_ptr + this.storage - this.end_offs - 1] |= (byte)window;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user