(caf7e6a2e) Replaced Concentus NuGet package with csproj (ensures correct System.Runtime references)

This commit is contained in:
Joonas Rikkonen
2019-04-29 21:11:59 +03:00
parent f10adc2612
commit 0b1b39d70a
149 changed files with 47171 additions and 70 deletions
@@ -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 = &amp;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&lt;&lt;_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