(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,118 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
internal static class ApplySineWindow
{
/* Apply sine window to signal vector. */
/* Window types: */
/* 1 . sine window from 0 to pi/2 */
/* 2 . sine window from pi/2 to pi */
/* Every other sample is linearly interpolated, for speed. */
/* Window length must be between 16 and 120 (incl) and a multiple of 4. */
/* Matlab code for table:
for k=16:9*4:16+2*9*4, fprintf(' %7.d,', -round(65536*pi ./ (k:4:k+8*4))); fprintf('\n'); end
*/
private static readonly short[] freq_table_Q16 = {
12111, 9804, 8235, 7100, 6239, 5565, 5022, 4575, 4202,
3885, 3612, 3375, 3167, 2984, 2820, 2674, 2542, 2422,
2313, 2214, 2123, 2038, 1961, 1889, 1822, 1760, 1702,
};
internal static void silk_apply_sine_window(
short[] px_win, /* O Pointer to windowed signal */
int px_win_ptr,
short[] px, /* I Pointer to input signal */
int px_ptr,
int win_type, /* I Selects a window type */
int length /* I Window length, multiple of 4 */
)
{
int k, f_Q16, c_Q16;
int S0_Q16, S1_Q16;
Inlines.OpusAssert(win_type == 1 || win_type == 2);
/* Length must be in a range from 16 to 120 and a multiple of 4 */
Inlines.OpusAssert(length >= 16 && length <= 120);
Inlines.OpusAssert((length & 3) == 0);
/* Frequency */
k = (length >> 2) - 4;
Inlines.OpusAssert(k >= 0 && k <= 26);
f_Q16 = (int)freq_table_Q16[k];
/* Factor used for cosine approximation */
c_Q16 = Inlines.silk_SMULWB((int)f_Q16, -f_Q16);
Inlines.OpusAssert(c_Q16 >= -32768);
/* initialize state */
if (win_type == 1)
{
/* start from 0 */
S0_Q16 = 0;
/* approximation of sin(f) */
S1_Q16 = f_Q16 + Inlines.silk_RSHIFT(length, 3);
}
else {
/* start from 1 */
S0_Q16 = ((int)1 << 16);
/* approximation of cos(f) */
S1_Q16 = ((int)1 << 16) + Inlines.silk_RSHIFT(c_Q16, 1) + Inlines.silk_RSHIFT(length, 4);
}
/* Uses the recursive equation: sin(n*f) = 2 * cos(f) * sin((n-1)*f) - sin((n-2)*f) */
/* 4 samples at a time */
for (k = 0; k < length; k += 4)
{
int pxwk = px_win_ptr + k;
int pxk = px_ptr + k;
px_win[pxwk] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk]);
px_win[pxwk + 1] = (short)Inlines.silk_SMULWB(S1_Q16, px[pxk + 1]);
S0_Q16 = Inlines.silk_SMULWB(S1_Q16, c_Q16) + Inlines.silk_LSHIFT(S1_Q16, 1) - S0_Q16 + 1;
S0_Q16 = Inlines.silk_min(S0_Q16, ((int)1 << 16));
px_win[pxwk + 2] = (short)Inlines.silk_SMULWB(Inlines.silk_RSHIFT(S0_Q16 + S1_Q16, 1), px[pxk + 2]);
px_win[pxwk + 3] = (short)Inlines.silk_SMULWB(S0_Q16, px[pxk + 3]);
S1_Q16 = Inlines.silk_SMULWB(S0_Q16, c_Q16) + Inlines.silk_LSHIFT(S0_Q16, 1) - S1_Q16;
S1_Q16 = Inlines.silk_min(S1_Q16, ((int)1 << 16));
}
}
}
}
@@ -0,0 +1,90 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class BWExpander
{
/// <summary>
/// Chirp (bw expand) LP AR filter (Fixed point implementation)
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I length of ar</param>
/// <param name="chirp_Q16">I chirp factor (typically in range (0..1) )</param>
internal static void silk_bwexpander_32(
int[] ar, /* I/O AR filter to be expanded (without leading 1) */
int d, /* I Length of ar */
int chirp_Q16 /* I Chirp factor in Q16 */
)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
for (i = 0; i < d - 1; i++)
{
ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
}
/// <summary>
/// Chirp (bw expand) LP AR filter (Fixed point implementation)
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I length of ar</param>
/// <param name="chirp_Q16">I chirp factor (typically in range (0..1) )</param>
internal static void silk_bwexpander(
short[] ar,
int d,
int chirp_Q16)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
/* NB: Dont use silk_SMULWB, instead of silk_RSHIFT_ROUND( silk_MUL(), 16 ), below. */
/* Bias in silk_SMULWB can lead to unstable filters */
for (i = 0; i < d - 1; i++)
{
ar[i] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[i]), 16);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = (short)Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, ar[d - 1]), 16);
}
}
}
@@ -0,0 +1,326 @@
/* 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.Silk
{
using Concentus.Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class BurgModified
{
/* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
private const int MAX_FRAME_SIZE = 384;
private const int QA = 25;
private const int N_BITS_HEAD_ROOM = 2;
private const int MIN_RSHIFTS = -16;
private const int MAX_RSHIFTS = (32 - QA);
/* Compute reflection coefficients from input signal */
internal static void silk_burg_modified(
BoxedValueInt res_nrg, /* O Residual energy */
BoxedValueInt res_nrg_Q, /* O Residual energy Q value */
int[] A_Q16, /* O Prediction coefficients (length order) */
short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */
int x_ptr,
int minInvGain_Q30, /* I Inverse of max prediction gain */
int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */
int nb_subfr, /* I Number of subframes stacked in x */
int D /* I Order */
)
{
int k, n, s, lz, rshifts, reached_max_gain;
int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
int x_offset;
int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC];
long C0_64;
Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE);
/* Compute autocorrelations, added over subframes */
C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
lz = Inlines.silk_CLZ64(C0_64);
rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
if (rshifts > 0)
{
C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts);
}
else {
C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts);
}
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC);
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64(
Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset + n, subfr_length - n), rshifts);
}
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
int i;
int d;
x_offset = x_ptr + s * subfr_length;
CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D);
for (n = 1; n < D + 1; n++)
{
for (i = n + subfr_length - D, d = 0; i < subfr_length; i++)
d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]);
xcorr[n - 1] += d;
}
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts);
}
}
}
Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC);
/* Initialize */
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
invGain_Q30 = (int)1 << 30;
reached_max_gain = 0;
for (n = 0; n < D; n++)
{
/* Update first row of correlation matrix (without first element) */
/* Update last row of correlation matrix (without last element, stored in reversed order) */
/* Update C * Af */
/* Update C * flipud(Af) (stored in reversed order) */
if (rshifts > -2)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], 16 - rshifts); /* Q(16-rshifts) */
x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */
tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], QA - 16); /* Q(QA-16) */
tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], QA - 16); /* Q(QA-16) */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */
Atmp_QA = Af_QA[k];
tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, x[x_offset + n - k - 1]); /* Q(QA-16) */
tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, x[x_offset + subfr_length - n + k]); /* Q(QA-16) */
}
tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */
tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, x[x_offset + n - k]); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, x[x_offset + subfr_length - n + k - 1]); /* Q( -rshift ) */
}
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)x[x_offset + n], -rshifts); /* Q( -rshifts ) */
x2 = -Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */
tmp1 = Inlines.silk_LSHIFT32((int)x[x_offset + n], 17); /* Q17 */
tmp2 = Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n - 1], 17); /* Q17 */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, x[x_offset + n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, x[x_offset + subfr_length - n + k]); /* Q( -rshifts ) */
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */
tmp1 = Inlines.silk_MLA(tmp1, x[x_offset + n - k - 1], Atmp1); /* Q17 */
tmp2 = Inlines.silk_MLA(tmp2, x[x_offset + subfr_length - n + k], Atmp1); /* Q17 */
}
tmp1 = -tmp1; /* Q17 */
tmp2 = -tmp2; /* Q17 */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1,
Inlines.silk_LSHIFT32((int)x[x_offset + n - k], -rshifts - 1)); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2,
Inlines.silk_LSHIFT32((int)x[x_offset + subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */
}
}
}
/* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
tmp1 = C_first_row[n]; /* Q( -rshifts ) */
tmp2 = C_last_row[n]; /* Q( -rshifts ) */
num = 0; /* Q( -rshifts ) */
nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */
for (k = 0; k < n; k++)
{
Atmp_QA = Af_QA[k];
lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1;
lz = Inlines.silk_min(32 - QA, lz);
Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */
tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]),
Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */
}
CAf[n + 1] = tmp1; /* Q( -rshifts ) */
CAb[n + 1] = tmp2; /* Q( -rshifts ) */
num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */
num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */
/* Calculate the next order reflection (parcor) coefficient */
if (Inlines.silk_abs(num) < nrg)
{
rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31);
}
else {
rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue;
}
/* Update inverse prediction gain */
tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2);
if (tmp1 <= minInvGain_Q30)
{
/* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */
rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */
/* Newton-Raphson iteration */
rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */
rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */
if (num < 0)
{
/* Ensure adjusted reflection coefficients has the original sign */
rc_Q31 = -rc_Q31;
}
invGain_Q30 = minInvGain_Q30;
reached_max_gain = 1;
}
else {
invGain_Q30 = tmp1;
}
/* Update the AR coefficients */
for (k = 0; k < (n + 1) >> 1; k++)
{
tmp1 = Af_QA[k]; /* QA */
tmp2 = Af_QA[n - k - 1]; /* QA */
Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */
Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */
}
Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */
if (reached_max_gain != 0)
{
/* Reached max prediction gain; set remaining coefficients to zero and exit loop */
for (k = n + 1; k < D; k++)
{
Af_QA[k] = 0;
}
break;
}
/* Update C * Af and C * Ab */
for (k = 0; k <= n + 1; k++)
{
tmp1 = CAf[k]; /* Q( -rshifts ) */
tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */
CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */
CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */
}
}
if (reached_max_gain != 0)
{
for (k = 0; k < D; k++)
{
/* Scale coefficients */
A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16);
}
/* Subtract energy of preceding samples from C0 */
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts);
}
}
else {
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts);
}
}
/* Approximate residual energy */
res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2);
res_nrg_Q.Val = 0 - rshifts;
}
else {
/* Return residual energy */
nrg = CAf[0]; /* Q( -rshifts ) */
tmp1 = (int)1 << 16; /* Q16 */
for (k = 0; k < D; k++)
{
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */
nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */
tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */
A_Q16[k] = -Atmp1;
}
res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */
res_nrg_Q.Val = -rshifts;
}
}
}
}
#endif
@@ -0,0 +1,336 @@
/* 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.Silk
{
using Concentus.Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class BurgModified
{
/* subfr_length * nb_subfr = ( 0.005 * 16000 + 16 ) * 4 = 384 */
private const int MAX_FRAME_SIZE = 384;
private const int QA = 25;
private const int N_BITS_HEAD_ROOM = 2;
private const int MIN_RSHIFTS = -16;
private const int MAX_RSHIFTS = (32 - QA);
/* Compute reflection coefficients from input signal */
internal static unsafe void silk_burg_modified(
BoxedValueInt res_nrg, /* O Residual energy */
BoxedValueInt res_nrg_Q, /* O Residual energy Q value */
int[] A_Q16, /* O Prediction coefficients (length order) */
short[] x, /* I Input signal, length: nb_subfr * ( D + subfr_length ) */
int x_ptr,
int minInvGain_Q30, /* I Inverse of max prediction gain */
int subfr_length, /* I Input signal subframe length (incl. D preceding samples) */
int nb_subfr, /* I Number of subframes stacked in x */
int D /* I Order */
)
{
int k, n, s, lz, rshifts, reached_max_gain;
int C0, num, nrg, rc_Q31, invGain_Q30, Atmp_QA, Atmp1, tmp1, tmp2, x1, x2;
int x_offset;
int[] C_first_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] C_last_row = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] Af_QA = new int[SilkConstants.SILK_MAX_ORDER_LPC];
int[] CAf = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] CAb = new int[SilkConstants.SILK_MAX_ORDER_LPC + 1];
int[] xcorr = new int[SilkConstants.SILK_MAX_ORDER_LPC];
long C0_64;
Inlines.OpusAssert(subfr_length * nb_subfr <= MAX_FRAME_SIZE);
fixed (short* px_base = x)
{
short* px = px_base + x_ptr;
/* Compute autocorrelations, added over subframes */
C0_64 = Inlines.silk_inner_prod16_aligned_64(x, x_ptr, x, x_ptr, subfr_length * nb_subfr);
lz = Inlines.silk_CLZ64(C0_64);
rshifts = 32 + 1 + N_BITS_HEAD_ROOM - lz;
if (rshifts > MAX_RSHIFTS) rshifts = MAX_RSHIFTS;
if (rshifts < MIN_RSHIFTS) rshifts = MIN_RSHIFTS;
if (rshifts > 0)
{
C0 = (int)Inlines.silk_RSHIFT64(C0_64, rshifts);
}
else
{
C0 = Inlines.silk_LSHIFT32((int)C0_64, -rshifts);
}
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
Arrays.MemSetInt(C_first_row, 0, SilkConstants.SILK_MAX_ORDER_LPC);
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += (int)Inlines.silk_RSHIFT64(
Inlines.silk_inner_prod16_aligned_64(px2, px2 + n, subfr_length - n), rshifts);
}
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
int i;
int d;
x_offset = x_ptr + s * subfr_length;
CeltPitchXCorr.pitch_xcorr(x, x_offset, x, x_offset + 1, xcorr, subfr_length - D, D);
for (n = 1; n < D + 1; n++)
{
for (i = n + subfr_length - D, d = 0; i < subfr_length; i++)
d = Inlines.MAC16_16(d, x[x_offset + i], x[x_offset + i - n]);
xcorr[n - 1] += d;
}
for (n = 1; n < D + 1; n++)
{
C_first_row[n - 1] += Inlines.silk_LSHIFT32(xcorr[n - 1], -rshifts);
}
}
}
Array.Copy(C_first_row, C_last_row, SilkConstants.SILK_MAX_ORDER_LPC);
/* Initialize */
CAb[0] = CAf[0] = C0 + Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0) + 1; /* Q(-rshifts) */
invGain_Q30 = (int)1 << 30;
reached_max_gain = 0;
for (n = 0; n < D; n++)
{
/* Update first row of correlation matrix (without first element) */
/* Update last row of correlation matrix (without last element, stored in reversed order) */
/* Update C * Af */
/* Update C * flipud(Af) (stored in reversed order) */
if (rshifts > -2)
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)px2[n], 16 - rshifts); /* Q(16-rshifts) */
x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 16 - rshifts); /* Q(16-rshifts) */
tmp1 = Inlines.silk_LSHIFT32((int)px2[n], QA - 16); /* Q(QA-16) */
tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], QA - 16); /* Q(QA-16) */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_SMLAWB(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_SMLAWB(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */
Atmp_QA = Af_QA[k];
tmp1 = Inlines.silk_SMLAWB(tmp1, Atmp_QA, px2[n - k - 1]); /* Q(QA-16) */
tmp2 = Inlines.silk_SMLAWB(tmp2, Atmp_QA, px2[subfr_length - n + k]); /* Q(QA-16) */
}
tmp1 = Inlines.silk_LSHIFT32(-tmp1, 32 - QA - rshifts); /* Q(16-rshifts) */
tmp2 = Inlines.silk_LSHIFT32(-tmp2, 32 - QA - rshifts); /* Q(16-rshifts) */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWB(CAf[k], tmp1, px2[n - k]); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWB(CAb[k], tmp2, px2[subfr_length - n + k - 1]); /* Q( -rshift ) */
}
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
short* px2 = px + s * subfr_length;
x1 = -Inlines.silk_LSHIFT32((int)px2[n], -rshifts); /* Q( -rshifts ) */
x2 = -Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], -rshifts); /* Q( -rshifts ) */
tmp1 = Inlines.silk_LSHIFT32((int)px2[n], 17); /* Q17 */
tmp2 = Inlines.silk_LSHIFT32((int)px2[subfr_length - n - 1], 17); /* Q17 */
for (k = 0; k < n; k++)
{
C_first_row[k] = Inlines.silk_MLA(C_first_row[k], x1, px2[n - k - 1]); /* Q( -rshifts ) */
C_last_row[k] = Inlines.silk_MLA(C_last_row[k], x2, px2[subfr_length - n + k]); /* Q( -rshifts ) */
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 17); /* Q17 */
tmp1 = Inlines.silk_MLA(tmp1, px2[n - k - 1], Atmp1); /* Q17 */
tmp2 = Inlines.silk_MLA(tmp2, px2[subfr_length - n + k], Atmp1); /* Q17 */
}
tmp1 = -tmp1; /* Q17 */
tmp2 = -tmp2; /* Q17 */
for (k = 0; k <= n; k++)
{
CAf[k] = Inlines.silk_SMLAWW(CAf[k], tmp1,
Inlines.silk_LSHIFT32((int)px2[n - k], -rshifts - 1)); /* Q( -rshift ) */
CAb[k] = Inlines.silk_SMLAWW(CAb[k], tmp2,
Inlines.silk_LSHIFT32((int)px2[subfr_length - n + k - 1], -rshifts - 1)); /* Q( -rshift ) */
}
}
}
/* Calculate nominator and denominator for the next order reflection (parcor) coefficient */
tmp1 = C_first_row[n]; /* Q( -rshifts ) */
tmp2 = C_last_row[n]; /* Q( -rshifts ) */
num = 0; /* Q( -rshifts ) */
nrg = Inlines.silk_ADD32(CAb[0], CAf[0]); /* Q( 1-rshifts ) */
for (k = 0; k < n; k++)
{
Atmp_QA = Af_QA[k];
lz = Inlines.silk_CLZ32(Inlines.silk_abs(Atmp_QA)) - 1;
lz = Inlines.silk_min(32 - QA, lz);
Atmp1 = Inlines.silk_LSHIFT32(Atmp_QA, lz); /* Q( QA + lz ) */
tmp1 = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(C_last_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
tmp2 = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(C_first_row[n - k - 1], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
num = Inlines.silk_ADD_LSHIFT32(num, Inlines.silk_SMMUL(CAb[n - k], Atmp1), 32 - QA - lz); /* Q( -rshifts ) */
nrg = Inlines.silk_ADD_LSHIFT32(nrg, Inlines.silk_SMMUL(Inlines.silk_ADD32(CAb[k + 1], CAf[k + 1]),
Atmp1), 32 - QA - lz); /* Q( 1-rshifts ) */
}
CAf[n + 1] = tmp1; /* Q( -rshifts ) */
CAb[n + 1] = tmp2; /* Q( -rshifts ) */
num = Inlines.silk_ADD32(num, tmp2); /* Q( -rshifts ) */
num = Inlines.silk_LSHIFT32(-num, 1); /* Q( 1-rshifts ) */
/* Calculate the next order reflection (parcor) coefficient */
if (Inlines.silk_abs(num) < nrg)
{
rc_Q31 = Inlines.silk_DIV32_varQ(num, nrg, 31);
}
else
{
rc_Q31 = (num > 0) ? int.MaxValue : int.MinValue;
}
/* Update inverse prediction gain */
tmp1 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
tmp1 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, tmp1), 2);
if (tmp1 <= minInvGain_Q30)
{
/* Max prediction gain exceeded; set reflection coefficient such that max prediction gain is exactly hit */
tmp2 = ((int)1 << 30) - Inlines.silk_DIV32_varQ(minInvGain_Q30, invGain_Q30, 30); /* Q30 */
rc_Q31 = Inlines.silk_SQRT_APPROX(tmp2); /* Q15 */
/* Newton-Raphson iteration */
rc_Q31 = Inlines.silk_RSHIFT32(rc_Q31 + Inlines.silk_DIV32(tmp2, rc_Q31), 1); /* Q15 */
rc_Q31 = Inlines.silk_LSHIFT32(rc_Q31, 16); /* Q31 */
if (num < 0)
{
/* Ensure adjusted reflection coefficients has the original sign */
rc_Q31 = -rc_Q31;
}
invGain_Q30 = minInvGain_Q30;
reached_max_gain = 1;
}
else
{
invGain_Q30 = tmp1;
}
/* Update the AR coefficients */
for (k = 0; k < (n + 1) >> 1; k++)
{
tmp1 = Af_QA[k]; /* QA */
tmp2 = Af_QA[n - k - 1]; /* QA */
Af_QA[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* QA */
Af_QA[n - k - 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* QA */
}
Af_QA[n] = Inlines.silk_RSHIFT32(rc_Q31, 31 - QA); /* QA */
if (reached_max_gain != 0)
{
/* Reached max prediction gain; set remaining coefficients to zero and exit loop */
for (k = n + 1; k < D; k++)
{
Af_QA[k] = 0;
}
break;
}
/* Update C * Af and C * Ab */
for (k = 0; k <= n + 1; k++)
{
tmp1 = CAf[k]; /* Q( -rshifts ) */
tmp2 = CAb[n - k + 1]; /* Q( -rshifts ) */
CAf[k] = Inlines.silk_ADD_LSHIFT32(tmp1, Inlines.silk_SMMUL(tmp2, rc_Q31), 1); /* Q( -rshifts ) */
CAb[n - k + 1] = Inlines.silk_ADD_LSHIFT32(tmp2, Inlines.silk_SMMUL(tmp1, rc_Q31), 1); /* Q( -rshifts ) */
}
}
if (reached_max_gain != 0)
{
for (k = 0; k < D; k++)
{
/* Scale coefficients */
A_Q16[k] = -Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16);
}
/* Subtract energy of preceding samples from C0 */
if (rshifts > 0)
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= (int)Inlines.silk_RSHIFT64(Inlines.silk_inner_prod16_aligned_64(x, x_offset, x, x_offset, D), rshifts);
}
}
else
{
for (s = 0; s < nb_subfr; s++)
{
x_offset = x_ptr + s * subfr_length;
C0 -= Inlines.silk_LSHIFT32(Inlines.silk_inner_prod_self(x, x_offset, D), -rshifts);
}
}
/* Approximate residual energy */
res_nrg.Val = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, C0), 2);
res_nrg_Q.Val = 0 - rshifts;
}
else
{
/* Return residual energy */
nrg = CAf[0]; /* Q( -rshifts ) */
tmp1 = (int)1 << 16; /* Q16 */
for (k = 0; k < D; k++)
{
Atmp1 = Inlines.silk_RSHIFT_ROUND(Af_QA[k], QA - 16); /* Q16 */
nrg = Inlines.silk_SMLAWW(nrg, CAf[k + 1], Atmp1); /* Q( -rshifts ) */
tmp1 = Inlines.silk_SMLAWW(tmp1, Atmp1, Atmp1); /* Q16 */
A_Q16[k] = -Atmp1;
}
res_nrg.Val = Inlines.silk_SMLAWW(nrg, Inlines.silk_SMMUL(((int)((TuningParameters.FIND_LPC_COND_FAC) * ((long)1 << (32)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LPC_COND_FAC, 32)*/, C0), -tmp1);/* Q( -rshifts ) */
res_nrg_Q.Val = -rshifts;
}
}
}
}
}
#endif
@@ -0,0 +1,232 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
/// <summary>
/// Comfort noise generation and estimation
/// </summary>
internal static class CNG
{
/// <summary>
/// Generates excitation for CNG LPC synthesis
/// </summary>
/// <param name="exc_Q10">O CNG excitation signal Q10</param>
/// <param name="exc_buf_Q14">I Random samples buffer Q10</param>
/// <param name="Gain_Q16">I Gain to apply</param>
/// <param name="length">I Length</param>
/// <param name="rand_seed">I/O Seed to random index generator</param>
internal static void silk_CNG_exc(
int[] exc_Q10,
int exc_Q10_ptr,
int[] exc_buf_Q14,
int Gain_Q16,
int length,
ref int rand_seed)
{
int seed;
int i, idx, exc_mask;
exc_mask = SilkConstants.CNG_BUF_MASK_MAX;
while (exc_mask > length)
{
exc_mask = Inlines.silk_RSHIFT(exc_mask, 1);
}
seed = rand_seed;
for (i = exc_Q10_ptr; i < exc_Q10_ptr + length; i++)
{
seed = Inlines.silk_RAND(seed);
idx = (int)(Inlines.silk_RSHIFT(seed, 24) & exc_mask);
Inlines.OpusAssert(idx >= 0);
Inlines.OpusAssert(idx <= SilkConstants.CNG_BUF_MASK_MAX);
exc_Q10[i] = (short)Inlines.silk_SAT16(Inlines.silk_SMULWW(exc_buf_Q14[idx], Gain_Q16 >> 4));
}
rand_seed = seed;
}
/// <summary>
/// Resets CNG state
/// </summary>
/// <param name="psDec">I/O Decoder state</param>
internal static void silk_CNG_Reset(SilkChannelDecoder psDec)
{
int i, NLSF_step_Q15, NLSF_acc_Q15;
NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, (short)(psDec.LPC_order + 1));
NLSF_acc_Q15 = 0;
for (i = 0; i < psDec.LPC_order; i++)
{
NLSF_acc_Q15 += NLSF_step_Q15;
psDec.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15);
}
psDec.sCNG.CNG_smth_Gain_Q16 = 0;
psDec.sCNG.rand_seed = 3176576;
}
/// <summary>
/// Updates CNG estimate, and applies the CNG when packet was lost
/// </summary>
/// <param name="psDec">I/O Decoder state</param>
/// <param name="psDecCtrl">I/O Decoder control</param>
/// <param name="frame">I/O Signal</param>
/// <param name="length">I Length of residual</param>
internal static void silk_CNG(
SilkChannelDecoder psDec,
SilkDecoderControl psDecCtrl,
short[] frame,
int frame_ptr,
int length)
{
int i, subfr;
int sum_Q6, max_Gain_Q16, gain_Q16;
short[] A_Q12 = new short[psDec.LPC_order];
CNGState psCNG = psDec.sCNG;
if (psDec.fs_kHz != psCNG.fs_kHz)
{
/* Reset state */
silk_CNG_Reset(psDec);
psCNG.fs_kHz = psDec.fs_kHz;
}
if (psDec.lossCnt == 0 && psDec.prevSignalType == SilkConstants.TYPE_NO_VOICE_ACTIVITY)
{
/* Update CNG parameters */
/* Smoothing of LSF's */
for (i = 0; i < psDec.LPC_order; i++)
{
psCNG.CNG_smth_NLSF_Q15[i] += (short)(Inlines.silk_SMULWB((int)psDec.prevNLSF_Q15[i] - (int)psCNG.CNG_smth_NLSF_Q15[i], SilkConstants.CNG_NLSF_SMTH_Q16));
}
/* Find the subframe with the highest gain */
max_Gain_Q16 = 0;
subfr = 0;
for (i = 0; i < psDec.nb_subfr; i++)
{
if (psDecCtrl.Gains_Q16[i] > max_Gain_Q16)
{
max_Gain_Q16 = psDecCtrl.Gains_Q16[i];
subfr = i;
}
}
/* Update CNG excitation buffer with excitation from this subframe */
Arrays.MemMoveInt(psCNG.CNG_exc_buf_Q14, 0, psDec.subfr_length, (psDec.nb_subfr - 1) * psDec.subfr_length);
/* Smooth gains */
for (i = 0; i < psDec.nb_subfr; i++)
{
psCNG.CNG_smth_Gain_Q16 += Inlines.silk_SMULWB(psDecCtrl.Gains_Q16[i] - psCNG.CNG_smth_Gain_Q16, SilkConstants.CNG_GAIN_SMTH_Q16);
}
}
/* Add CNG when packet is lost or during DTX */
if (psDec.lossCnt != 0)
{
int[] CNG_sig_Q10 = new int[length + SilkConstants.MAX_LPC_ORDER];
/* Generate CNG excitation */
gain_Q16 = Inlines.silk_SMULWW(psDec.sPLC.randScale_Q14, psDec.sPLC.prevGain_Q16[1]);
if (gain_Q16 >= (1 << 21) || psCNG.CNG_smth_Gain_Q16 > (1 << 23))
{
gain_Q16 = Inlines.silk_SMULTT(gain_Q16, gain_Q16);
gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULTT(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 16);
}
else
{
gain_Q16 = Inlines.silk_SMULWW(gain_Q16, gain_Q16);
gain_Q16 = Inlines.silk_SUB_LSHIFT32(Inlines.silk_SMULWW(psCNG.CNG_smth_Gain_Q16, psCNG.CNG_smth_Gain_Q16), gain_Q16, 5);
gain_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_SQRT_APPROX(gain_Q16), 8);
}
silk_CNG_exc(CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER, psCNG.CNG_exc_buf_Q14, gain_Q16, length, ref psCNG.rand_seed);
/* Convert CNG NLSF to filter representation */
NLSF.silk_NLSF2A(A_Q12, psCNG.CNG_smth_NLSF_Q15, psDec.LPC_order);
/* Generate CNG signal, by synthesis filtering */
Array.Copy(psCNG.CNG_synth_state, CNG_sig_Q10, SilkConstants.MAX_LPC_ORDER);
for (i = 0; i < length; i++)
{
int lpci = SilkConstants.MAX_LPC_ORDER + i;
Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
sum_Q6 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 1], A_Q12[0]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 2], A_Q12[1]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 3], A_Q12[2]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 4], A_Q12[3]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 5], A_Q12[4]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 6], A_Q12[5]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 7], A_Q12[6]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 8], A_Q12[7]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 9], A_Q12[8]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 10], A_Q12[9]);
if (psDec.LPC_order == 16)
{
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 11], A_Q12[10]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 12], A_Q12[11]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 13], A_Q12[12]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 14], A_Q12[13]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 15], A_Q12[14]);
sum_Q6 = Inlines.silk_SMLAWB(sum_Q6, CNG_sig_Q10[lpci - 16], A_Q12[15]);
}
/* Update states */
CNG_sig_Q10[lpci] = Inlines.silk_ADD_LSHIFT(CNG_sig_Q10[lpci], sum_Q6, 4);
frame[frame_ptr + i] = Inlines.silk_ADD_SAT16(frame[frame_ptr + i], (short)(Inlines.silk_RSHIFT_ROUND(CNG_sig_Q10[lpci], 10)));
}
Array.Copy(CNG_sig_Q10, length, psCNG.CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER);
}
else
{
Arrays.MemSetInt(psCNG.CNG_synth_state, 0, psDec.LPC_order);
}
}
}
}
@@ -0,0 +1,150 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class CodeSigns
{
private static int silk_enc_map(int a)
{
return (Inlines.silk_RSHIFT((a), 15) + 1);
}
private static int silk_dec_map(int a)
{
return (Inlines.silk_LSHIFT((a), 1) - 1);
}
/// <summary>
/// Encodes signs of excitation
/// </summary>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="pulses">I pulse signal</param>
/// <param name="length">I length of input</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I Quantization offset type</param>
/// <param name="sum_pulses">I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS]</param>
internal static void silk_encode_signs(
EntropyCoder psRangeEnc,
sbyte[] pulses,
int length,
int signalType,
int quantOffsetType,
int[] sum_pulses)
{
int i, j, p;
byte[] icdf = new byte[2];
int q_ptr;
byte[] sign_icdf = Tables.silk_sign_iCDF;
int icdf_ptr;
icdf[1] = 0;
q_ptr = 0;
i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
icdf_ptr = i;
length = Inlines.silk_RSHIFT(length + (SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2), SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
for (i = 0; i < length; i++)
{
p = sum_pulses[i];
if (p > 0)
{
icdf[0] = sign_icdf[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)];
for (j = q_ptr; j < q_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++)
{
if (pulses[j] != 0)
{
psRangeEnc.enc_icdf( silk_enc_map(pulses[j]), icdf, 8);
}
}
}
q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
}
/// <summary>
/// Decodes signs of excitation
/// </summary>
/// <param name="psRangeDec">I/O Compressor data structure</param>
/// <param name="pulses">I/O pulse signal</param>
/// <param name="length">I length of input</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I Quantization offset type</param>
/// <param name="sum_pulses">I Sum of absolute pulses per block [MAX_NB_SHELL_BLOCKS]</param>
internal static void silk_decode_signs(
EntropyCoder psRangeDec,
short[] pulses,
int length,
int signalType,
int quantOffsetType,
int[] sum_pulses)
{
int i, j, p;
byte[] icdf = new byte[2];
int q_ptr;
byte[] icdf_table = Tables.silk_sign_iCDF;
int icdf_ptr;
icdf[1] = 0;
q_ptr = 0;
i = Inlines.silk_SMULBB(7, Inlines.silk_ADD_LSHIFT(quantOffsetType, signalType, 1));
icdf_ptr = i;
length = Inlines.silk_RSHIFT(length + SilkConstants.SHELL_CODEC_FRAME_LENGTH / 2, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
for (i = 0; i < length; i++)
{
p = sum_pulses[i];
if (p > 0)
{
icdf[0] = icdf_table[icdf_ptr + Inlines.silk_min(p & 0x1F, 6)];
for (j = 0; j < SilkConstants.SHELL_CODEC_FRAME_LENGTH; j++)
{
if (pulses[q_ptr + j] > 0)
{
/* attach sign */
pulses[q_ptr + j] *= (short)(silk_dec_map(psRangeDec.dec_icdf(icdf, 8)));
}
}
}
q_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
}
}
}
@@ -0,0 +1,186 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
/**********************************************************************
* Correlation Matrix Computations for LS estimate.
**********************************************************************/
internal static class CorrelateMatrix
{
/* Calculates correlation vector X'*t */
internal static void silk_corrVector(
short[] x, /* I x vector [L + order - 1] used to form data matrix X */
int x_ptr,
short[] t, /* I Target vector [L] */
int t_ptr,
int L, /* I Length of vectors */
int order, /* I Max lag for correlation */
int[] Xt, /* O Pointer to X'*t correlation vector [order] */
int rshifts /* I Right shifts of correlations */
)
{
int lag, i;
int ptr1;
int ptr2;
int inner_prod;
ptr1 = x_ptr + order - 1; /* Points to first sample of column 0 of X: X[:,0] */
ptr2 = t_ptr;
/* Calculate X'*t */
if (rshifts > 0)
{
/* Right shifting used */
for (lag = 0; lag < order; lag++)
{
inner_prod = 0;
for (i = 0; i < L; i++)
{
inner_prod += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], t[ptr2 + i]), rshifts);
}
Xt[lag] = inner_prod; /* X[:,lag]'*t */
ptr1--; /* Go to next column of X */
}
}
else {
Inlines.OpusAssert(rshifts == 0);
for (lag = 0; lag < order; lag++)
{
Xt[lag] = Inlines.silk_inner_prod(x, ptr1, t, ptr2, L); /* X[:,lag]'*t */
ptr1--; /* Go to next column of X */
}
}
}
/* Calculates correlation matrix X'*X */
internal static void silk_corrMatrix(
short[] x, /* I x vector [L + order - 1] used to form data matrix X */
int x_ptr,
int L, /* I Length of vectors */
int order, /* I Max lag for correlation */
int head_room, /* I Desired headroom */
int[] XX, /* O Pointer to X'*X correlation matrix [ order x order ] */
int XX_ptr,
BoxedValueInt rshifts /* I/O Right shifts of correlations */
)
{
int i, j, lag, head_room_rshifts;
int energy, rshifts_local;
int ptr1, ptr2;
/* Calculate energy to find shift used to fit in 32 bits */
SumSqrShift.silk_sum_sqr_shift(out energy, out rshifts_local, x, x_ptr, L + order - 1);
/* Add shifts to get the desired head room */
head_room_rshifts = Inlines.silk_max(head_room - Inlines.silk_CLZ32(energy), 0);
energy = Inlines.silk_RSHIFT32(energy, head_room_rshifts);
rshifts_local += head_room_rshifts;
/* Calculate energy of first column (0) of X: X[:,0]'*X[:,0] */
/* Remove contribution of first order - 1 samples */
for (i = x_ptr; i < x_ptr + order - 1; i++)
{
energy -= Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[i], x[i]), rshifts_local);
}
if (rshifts_local < rshifts.Val)
{
/* Adjust energy */
energy = Inlines.silk_RSHIFT32(energy, rshifts.Val - rshifts_local);
rshifts_local = rshifts.Val;
}
/* Calculate energy of remaining columns of X: X[:,j]'*X[:,j] */
/* Fill out the diagonal of the correlation matrix */
Inlines.MatrixSet(XX, XX_ptr, 0, 0, order, energy);
ptr1 = x_ptr + order - 1; /* First sample of column 0 of X */
for (j = 1; j < order; j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr1 + L - j]), rshifts_local));
energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr1 - j]), rshifts_local));
Inlines.MatrixSet(XX, XX_ptr, j, j, order, energy);
}
ptr2 = x_ptr + order - 2; /* First sample of column 1 of X */
/* Calculate the remaining elements of the correlation matrix */
if (rshifts_local > 0)
{
/* Right shifting used */
for (lag = 1; lag < order; lag++)
{
/* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
energy = 0;
for (i = 0; i < L; i++)
{
energy += Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + i], x[ptr2 + i]), rshifts_local);
}
/* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
Inlines.MatrixSet(XX, XX_ptr, lag, 0, order, energy);
Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
for (j = 1; j < (order - lag); j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]), rshifts_local));
energy = Inlines.silk_ADD32(energy, Inlines.silk_RSHIFT32(Inlines.silk_SMULBB(x[ptr1 - j], x[ptr2 - j]), rshifts_local));
Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
}
ptr2--; /* Update pointer to first sample of next column (lag) in X */
}
}
else {
for (lag = 1; lag < order; lag++)
{
/* Inner product of column 0 and column lag: X[:,0]'*X[:,lag] */
energy = Inlines.silk_inner_prod(x, ptr1, x, ptr2, L);
Inlines.MatrixSet(XX, XX_ptr, lag, 0, order,energy);
Inlines.MatrixSet(XX, XX_ptr, 0, lag, order, energy);
/* Calculate remaining off diagonal: X[:,j]'*X[:,j + lag] */
for (j = 1; j < (order - lag); j++)
{
energy = Inlines.silk_SUB32(energy, Inlines.silk_SMULBB(x[ptr1 + L - j], x[ptr2 + L - j]));
energy = Inlines.silk_SMLABB(energy, x[ptr1 - j], x[ptr2 - j]);
Inlines.MatrixSet(XX, XX_ptr, lag + j, j, order, energy);
Inlines.MatrixSet(XX, XX_ptr, j, lag + j, order, energy);
}
ptr2--;/* Update pointer to first sample of next column (lag) in X */
}
}
rshifts.Val = rshifts_local;
}
}
}
@@ -0,0 +1,461 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class DecodeAPI
{
/// <summary>
/// Reset decoder state
/// </summary>
/// <param name="decState">I/O Stat</param>
/// <returns>Returns error code</returns>
internal static int silk_InitDecoder(SilkDecoder decState)
{
/* Reset decoder */
decState.Reset();
int n, ret = SilkError.SILK_NO_ERROR;
SilkChannelDecoder[] channel_states = decState.channel_state;
for (n = 0; n < SilkConstants.DECODER_NUM_CHANNELS; n++)
{
ret = channel_states[n].silk_init_decoder();
}
decState.sStereo.Reset();
/* Not strictly needed, but it's cleaner that way */
decState.prev_decode_only_middle = 0;
return ret;
}
/* Decode a frame */
internal static int silk_Decode( /* O Returns error code */
SilkDecoder psDec, /* I/O State */
DecControlState decControl, /* I/O Control Structure */
int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */
int newPacketFlag, /* I Indicates first decoder call for this packet */
EntropyCoder psRangeDec, /* I/O Compressor data structure */
short[] samplesOut, /* O Decoded output speech vector */
int samplesOut_ptr,
out int nSamplesOut /* O Number of samples decoded */
)
{
int i, n, decode_only_middle = 0, ret = SilkError.SILK_NO_ERROR;
int LBRR_symbol;
BoxedValueInt nSamplesOutDec = new BoxedValueInt();
short[] samplesOut_tmp;
int[] samplesOut_tmp_ptrs = new int[2];
short[] samplesOut1_tmp_storage1;
short[] samplesOut1_tmp_storage2;
short[] samplesOut2_tmp;
int[] MS_pred_Q13 = new int[] { 0, 0 };
short[] resample_out;
int resample_out_ptr;
SilkChannelDecoder[] channel_state = psDec.channel_state;
int has_side;
int stereo_to_mono;
int delay_stack_alloc;
nSamplesOut = 0;
Inlines.OpusAssert(decControl.nChannelsInternal == 1 || decControl.nChannelsInternal == 2);
/**********************************/
/* Test if first frame in payload */
/**********************************/
if (newPacketFlag != 0)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
channel_state[n].nFramesDecoded = 0; /* Used to count frames in packet */
}
}
/* If Mono . Stereo transition in bitstream: init state of second channel */
if (decControl.nChannelsInternal > psDec.nChannelsInternal)
{
ret += channel_state[1].silk_init_decoder();
}
stereo_to_mono = (decControl.nChannelsInternal == 1 && psDec.nChannelsInternal == 2 &&
(decControl.internalSampleRate == 1000 * channel_state[0].fs_kHz)) ? 1 : 0;
if (channel_state[0].nFramesDecoded == 0)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
int fs_kHz_dec;
if (decControl.payloadSize_ms == 0)
{
/* Assuming packet loss, use 10 ms */
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 2;
}
else if (decControl.payloadSize_ms == 10)
{
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 2;
}
else if (decControl.payloadSize_ms == 20)
{
channel_state[n].nFramesPerPacket = 1;
channel_state[n].nb_subfr = 4;
}
else if (decControl.payloadSize_ms == 40)
{
channel_state[n].nFramesPerPacket = 2;
channel_state[n].nb_subfr = 4;
}
else if (decControl.payloadSize_ms == 60)
{
channel_state[n].nFramesPerPacket = 3;
channel_state[n].nb_subfr = 4;
}
else {
Inlines.OpusAssert(false);
return SilkError.SILK_DEC_INVALID_FRAME_SIZE;
}
fs_kHz_dec = (decControl.internalSampleRate >> 10) + 1;
if (fs_kHz_dec != 8 && fs_kHz_dec != 12 && fs_kHz_dec != 16)
{
Inlines.OpusAssert(false);
return SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
}
ret += channel_state[n].silk_decoder_set_fs(fs_kHz_dec, decControl.API_sampleRate);
}
}
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2 && (psDec.nChannelsAPI == 1 || psDec.nChannelsInternal == 1))
{
Arrays.MemSetShort(psDec.sStereo.pred_prev_Q13, 0, 2);
Arrays.MemSetShort(psDec.sStereo.sSide, 0, 2);
channel_state[1].resampler_state.Assign(channel_state[0].resampler_state);
}
psDec.nChannelsAPI = decControl.nChannelsAPI;
psDec.nChannelsInternal = decControl.nChannelsInternal;
if (decControl.API_sampleRate > (int)SilkConstants.MAX_API_FS_KHZ * 1000 || decControl.API_sampleRate < 8000)
{
ret = SilkError.SILK_DEC_INVALID_SAMPLING_FREQUENCY;
return (ret);
}
if (lostFlag != DecoderAPIFlag.FLAG_PACKET_LOST && channel_state[0].nFramesDecoded == 0)
{
/* First decoder call for this payload */
/* Decode VAD flags and LBRR flag */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
for (i = 0; i < channel_state[n].nFramesPerPacket; i++)
{
channel_state[n].VAD_flags[i] = psRangeDec.dec_bit_logp(1);
}
channel_state[n].LBRR_flag = psRangeDec.dec_bit_logp(1);
}
/* Decode LBRR flags */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
Arrays.MemSetInt(channel_state[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
if (channel_state[n].LBRR_flag != 0)
{
if (channel_state[n].nFramesPerPacket == 1)
{
channel_state[n].LBRR_flags[0] = 1;
}
else {
LBRR_symbol = psRangeDec.dec_icdf(Tables.silk_LBRR_flags_iCDF_ptr[channel_state[n].nFramesPerPacket - 2], 8) + 1;
for (i = 0; i < channel_state[n].nFramesPerPacket; i++)
{
channel_state[n].LBRR_flags[i] = Inlines.silk_RSHIFT(LBRR_symbol, i) & 1;
}
}
}
}
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL)
{
/* Regular decoding: skip all LBRR data */
for (i = 0; i < channel_state[0].nFramesPerPacket; i++)
{
for (n = 0; n < decControl.nChannelsInternal; n++)
{
if (channel_state[n].LBRR_flags[i] != 0)
{
short[] pulses = new short[SilkConstants.MAX_FRAME_LENGTH];
int condCoding;
if (decControl.nChannelsInternal == 2 && n == 0)
{
Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13);
if (channel_state[1].LBRR_flags[i] == 0)
{
BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle);
Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed);
decode_only_middle = decodeOnlyMiddleBoxed.Val;
}
}
/* Use conditional coding if previous frame available */
if (i > 0 && (channel_state[n].LBRR_flags[i - 1] != 0))
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
else
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
DecodeIndices.silk_decode_indices(channel_state[n], psRangeDec, i, 1, condCoding);
DecodePulses.silk_decode_pulses(psRangeDec, pulses, channel_state[n].indices.signalType,
channel_state[n].indices.quantOffsetType, channel_state[n].frame_length);
}
}
}
}
}
/* Get MS predictor index */
if (decControl.nChannelsInternal == 2)
{
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL ||
(lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[0].LBRR_flags[channel_state[0].nFramesDecoded] == 1))
{
Stereo.silk_stereo_decode_pred(psRangeDec, MS_pred_Q13);
/* For LBRR data, decode mid-only flag only if side-channel's LBRR flag is false */
if ((lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL && channel_state[1].VAD_flags[channel_state[0].nFramesDecoded] == 0) ||
(lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && channel_state[1].LBRR_flags[channel_state[0].nFramesDecoded] == 0))
{
BoxedValueInt decodeOnlyMiddleBoxed = new BoxedValueInt(decode_only_middle);
Stereo.silk_stereo_decode_mid_only(psRangeDec, decodeOnlyMiddleBoxed);
decode_only_middle = decodeOnlyMiddleBoxed.Val;
}
else
{
decode_only_middle = 0;
}
}
else
{
for (n = 0; n < 2; n++)
{
MS_pred_Q13[n] = psDec.sStereo.pred_prev_Q13[n];
}
}
}
/* Reset side channel decoder prediction memory for first frame with side coding */
if (decControl.nChannelsInternal == 2 && decode_only_middle == 0 && psDec.prev_decode_only_middle == 1)
{
Arrays.MemSetShort(psDec.channel_state[1].outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH);
Arrays.MemSetInt(psDec.channel_state[1].sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
psDec.channel_state[1].lagPrev = 100;
psDec.channel_state[1].LastGainIndex = 10;
psDec.channel_state[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY;
psDec.channel_state[1].first_frame_after_reset = 1;
}
/* Check if the temp buffer fits into the output PCM buffer. If it fits,
we can delay allocating the temp buffer until after the SILK peak stack
usage. We need to use a < and not a <= because of the two extra samples. */
delay_stack_alloc = (decControl.internalSampleRate * decControl.nChannelsInternal
< decControl.API_sampleRate * decControl.nChannelsAPI) ? 1 : 0;
if (delay_stack_alloc != 0)
{
samplesOut_tmp = samplesOut;
samplesOut_tmp_ptrs[0] = samplesOut_ptr;
samplesOut_tmp_ptrs[1] = samplesOut_ptr + channel_state[0].frame_length + 2;
}
else
{
samplesOut1_tmp_storage1 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
samplesOut_tmp = samplesOut1_tmp_storage1;
samplesOut_tmp_ptrs[0] = 0;
samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2;
}
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL)
{
has_side = (decode_only_middle == 0) ? 1 : 0;
}
else
{
has_side = (psDec.prev_decode_only_middle == 0
|| (decControl.nChannelsInternal == 2 &&
lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR &&
channel_state[1].LBRR_flags[channel_state[1].nFramesDecoded] == 1)) ? 1 : 0;
}
/* Call decoder for one frame */
for (n = 0; n < decControl.nChannelsInternal; n++)
{
if (n == 0 || (has_side != 0))
{
int FrameIndex;
int condCoding;
FrameIndex = channel_state[0].nFramesDecoded - n;
/* Use independent coding if no previous frame available */
if (FrameIndex <= 0)
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
else if (lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR)
{
condCoding = (channel_state[n].LBRR_flags[FrameIndex - 1] != 0) ? SilkConstants.CODE_CONDITIONALLY : SilkConstants.CODE_INDEPENDENTLY;
}
else if (n > 0 && (psDec.prev_decode_only_middle != 0))
{
/* If we skipped a side frame in this packet, we don't
need LTP scaling; the LTP state is well-defined. */
condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING;
}
else
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
ret += channel_state[n].silk_decode_frame(psRangeDec, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec, lostFlag, condCoding);
}
else
{
Arrays.MemSetWithOffset<short>(samplesOut_tmp, 0, samplesOut_tmp_ptrs[n] + 2, nSamplesOutDec.Val);
}
channel_state[n].nFramesDecoded++;
}
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 2)
{
/* Convert Mid/Side to Left/Right */
Stereo.silk_stereo_MS_to_LR(psDec.sStereo, samplesOut_tmp, samplesOut_tmp_ptrs[0], samplesOut_tmp, samplesOut_tmp_ptrs[1], MS_pred_Q13, channel_state[0].fs_kHz, nSamplesOutDec.Val);
}
else
{
/* Buffering */
Array.Copy(psDec.sStereo.sMid, 0, samplesOut_tmp, samplesOut_tmp_ptrs[0], 2);
Array.Copy(samplesOut_tmp, samplesOut_tmp_ptrs[0] + nSamplesOutDec.Val, psDec.sStereo.sMid, 0, 2);
}
/* Number of output samples */
nSamplesOut = Inlines.silk_DIV32(nSamplesOutDec.Val * decControl.API_sampleRate, Inlines.silk_SMULBB(channel_state[0].fs_kHz, 1000));
/* Set up pointers to temp buffers */
if (decControl.nChannelsAPI == 2)
{
samplesOut2_tmp = new short[nSamplesOut];
resample_out = samplesOut2_tmp;
resample_out_ptr = 0;
}
else {
resample_out = samplesOut;
resample_out_ptr = samplesOut_ptr;
}
if (delay_stack_alloc != 0)
{
samplesOut1_tmp_storage2 = new short[decControl.nChannelsInternal * (channel_state[0].frame_length + 2)];
Array.Copy(samplesOut, samplesOut_ptr, samplesOut1_tmp_storage2, 0, decControl.nChannelsInternal * (channel_state[0].frame_length + 2));
samplesOut_tmp = samplesOut1_tmp_storage2;
samplesOut_tmp_ptrs[0] = 0;
samplesOut_tmp_ptrs[1] = channel_state[0].frame_length + 2;
}
for (n = 0; n < Inlines.silk_min(decControl.nChannelsAPI, decControl.nChannelsInternal); n++)
{
/* Resample decoded signal to API_sampleRate */
ret += Resampler.silk_resampler(channel_state[n].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[n] + 1, nSamplesOutDec.Val);
/* Interleave if stereo output and stereo stream */
if (decControl.nChannelsAPI == 2)
{
int nptr = samplesOut_ptr + n;
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[nptr + 2 * i] = resample_out[resample_out_ptr + i];
}
}
}
/* Create two channel output from mono stream */
if (decControl.nChannelsAPI == 2 && decControl.nChannelsInternal == 1)
{
if (stereo_to_mono != 0)
{
/* Resample right channel for newly collapsed stereo just in case
we weren't doing collapsing when switching to mono */
ret += Resampler.silk_resampler(channel_state[1].resampler_state, resample_out, resample_out_ptr, samplesOut_tmp, samplesOut_tmp_ptrs[0] + 1, nSamplesOutDec.Val);
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[samplesOut_ptr + 1 + 2 * i] = resample_out[resample_out_ptr + i];
}
}
else {
for (i = 0; i < nSamplesOut; i++)
{
samplesOut[samplesOut_ptr + 1 + 2 * i] = samplesOut[samplesOut_ptr + 2 * i];
}
}
}
/* Export pitch lag, measured at 48 kHz sampling rate */
if (channel_state[0].prevSignalType == SilkConstants.TYPE_VOICED)
{
int[] mult_tab = { 6, 4, 3 };
decControl.prevPitchLag = channel_state[0].lagPrev * mult_tab[(channel_state[0].fs_kHz - 8) >> 2];
}
else
{
decControl.prevPitchLag = 0;
}
if (lostFlag == DecoderAPIFlag.FLAG_PACKET_LOST)
{
/* On packet loss, remove the gain clamping to prevent having the energy "bounce back"
if we lose packets when the energy is going down */
for (i = 0; i < psDec.nChannelsInternal; i++)
psDec.channel_state[i].LastGainIndex = 10;
}
else
{
psDec.prev_decode_only_middle = decode_only_middle;
}
return ret;
}
}
}
@@ -0,0 +1,278 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class DecodeCore
{
/**********************************************************/
/* Core decoder. Performs inverse NSQ operation LTP + LPC */
/**********************************************************/
internal static void silk_decode_core(
SilkChannelDecoder psDec, /* I/O Decoder state */
SilkDecoderControl psDecCtrl, /* I Decoder control */
short[] xq, /* O Decoded speech */
int xq_ptr,
short[] pulses /* I Pulse signal [MAX_FRAME_LENGTH] */
)
{
int i, k, lag = 0, start_idx, sLTP_buf_idx, NLSF_interpolation_flag, signalType;
short[] A_Q12;
short[] B_Q14 = psDecCtrl.LTPCoef_Q14;
int B_Q14_ptr;
int pxq;
short[] sLTP;
int[] sLTP_Q15;
int LTP_pred_Q13, LPC_pred_Q10, Gain_Q10, inv_gain_Q31, gain_adj_Q16, rand_seed, offset_Q10;
int pred_lag_ptr;
int pexc_Q14;
int[] pres_Q14;
int pres_Q14_ptr;
int[] res_Q14;
int[] sLPC_Q14;
Inlines.OpusAssert(psDec.prev_gain_Q16 != 0);
sLTP= new short[psDec.ltp_mem_length];
sLTP_Q15 = new int[psDec.ltp_mem_length + psDec.frame_length];
res_Q14 = new int[psDec.subfr_length];
sLPC_Q14 = new int[psDec.subfr_length + SilkConstants.MAX_LPC_ORDER];
offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psDec.indices.signalType >> 1][psDec.indices.quantOffsetType];
if (psDec.indices.NLSFInterpCoef_Q2 < 1 << 2)
{
NLSF_interpolation_flag = 1;
}
else {
NLSF_interpolation_flag = 0;
}
/* Decode excitation */
rand_seed = psDec.indices.Seed;
for (i = 0; i < psDec.frame_length; i++)
{
rand_seed = Inlines.silk_RAND(rand_seed);
psDec.exc_Q14[i] = Inlines.silk_LSHIFT((int)pulses[i], 14);
if (psDec.exc_Q14[i] > 0)
{
psDec.exc_Q14[i] -= SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
}
else
if (psDec.exc_Q14[i] < 0)
{
psDec.exc_Q14[i] += SilkConstants.QUANT_LEVEL_ADJUST_Q10 << 4;
}
psDec.exc_Q14[i] += offset_Q10 << 4;
if (rand_seed < 0)
{
psDec.exc_Q14[i] = -psDec.exc_Q14[i];
}
rand_seed = Inlines.silk_ADD32_ovflw(rand_seed, pulses[i]);
}
/* Copy LPC state */
Array.Copy(psDec.sLPC_Q14_buf, sLPC_Q14, SilkConstants.MAX_LPC_ORDER);
pexc_Q14 = 0;
pxq = xq_ptr;
sLTP_buf_idx = psDec.ltp_mem_length;
/* Loop over subframes */
for (k = 0; k < psDec.nb_subfr; k++)
{
pres_Q14 = res_Q14;
pres_Q14_ptr = 0;
A_Q12 = psDecCtrl.PredCoef_Q12[k >> 1];
B_Q14_ptr = k * SilkConstants.LTP_ORDER;
signalType = psDec.indices.signalType;
Gain_Q10 = Inlines.silk_RSHIFT(psDecCtrl.Gains_Q16[k], 6);
inv_gain_Q31 = Inlines.silk_INVERSE32_varQ(psDecCtrl.Gains_Q16[k], 47);
/* Calculate gain adjustment factor */
if (psDecCtrl.Gains_Q16[k] != psDec.prev_gain_Q16)
{
gain_adj_Q16 = Inlines.silk_DIV32_varQ(psDec.prev_gain_Q16, psDecCtrl.Gains_Q16[k], 16);
/* Scale short term state */
for (i = 0; i < SilkConstants.MAX_LPC_ORDER; i++)
{
sLPC_Q14[i] = Inlines.silk_SMULWW(gain_adj_Q16, sLPC_Q14[i]);
}
}
else {
gain_adj_Q16 = (int)1 << 16;
}
/* Save inv_gain */
Inlines.OpusAssert(inv_gain_Q31 != 0);
psDec.prev_gain_Q16 = psDecCtrl.Gains_Q16[k];
/* Avoid abrupt transition from voiced PLC to unvoiced normal decoding */
if (psDec.lossCnt != 0 && psDec.prevSignalType == SilkConstants.TYPE_VOICED &&
psDec.indices.signalType != SilkConstants.TYPE_VOICED && k < SilkConstants.MAX_NB_SUBFR / 2)
{
Arrays.MemSetWithOffset<short>(B_Q14, 0, B_Q14_ptr, SilkConstants.LTP_ORDER);
B_Q14[B_Q14_ptr + (SilkConstants.LTP_ORDER / 2)] = (short)(((int)((0.25f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.25f, 14)*/);
signalType = SilkConstants.TYPE_VOICED;
psDecCtrl.pitchL[k] = psDec.lagPrev;
}
if (signalType == SilkConstants.TYPE_VOICED)
{
/* Voiced */
lag = psDecCtrl.pitchL[k];
/* Re-whitening */
if (k == 0 || (k == 2 && (NLSF_interpolation_flag != 0)))
{
/* Rewhiten with new A coefs */
start_idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2;
Inlines.OpusAssert(start_idx > 0);
if (k == 2)
{
Array.Copy(xq, xq_ptr, psDec.outBuf, psDec.ltp_mem_length, 2 * psDec.subfr_length);
}
Filters.silk_LPC_analysis_filter(sLTP, start_idx, psDec.outBuf, (start_idx + k * psDec.subfr_length),
A_Q12, 0, psDec.ltp_mem_length - start_idx, psDec.LPC_order);
/* After rewhitening the LTP state is unscaled */
if (k == 0)
{
/* Do LTP downscaling to reduce inter-packet dependency */
inv_gain_Q31 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(inv_gain_Q31, psDecCtrl.LTP_scale_Q14), 2);
}
for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
{
sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWB(inv_gain_Q31, sLTP[psDec.ltp_mem_length - i - 1]);
}
}
else {
/* Update LTP state when Gain changes */
if (gain_adj_Q16 != (int)1 << 16)
{
for (i = 0; i < lag + SilkConstants.LTP_ORDER / 2; i++)
{
sLTP_Q15[sLTP_buf_idx - i - 1] = Inlines.silk_SMULWW(gain_adj_Q16, sLTP_Q15[sLTP_buf_idx - i - 1]);
}
}
}
}
/* Long-term prediction */
if (signalType == SilkConstants.TYPE_VOICED)
{
/* Set up pointer */
pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2;
for (i = 0; i < psDec.subfr_length; i++)
{
/* Unrolled loop */
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
LTP_pred_Q13 = 2;
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr], B_Q14[B_Q14_ptr]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 1], B_Q14[B_Q14_ptr + 1]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 2], B_Q14[B_Q14_ptr + 2]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 3], B_Q14[B_Q14_ptr + 3]);
LTP_pred_Q13 = Inlines.silk_SMLAWB(LTP_pred_Q13, sLTP_Q15[pred_lag_ptr - 4], B_Q14[B_Q14_ptr + 4]);
pred_lag_ptr += 1;
/* Generate LPC excitation */
pres_Q14[pres_Q14_ptr + i] = Inlines.silk_ADD_LSHIFT32(psDec.exc_Q14[pexc_Q14 + i], LTP_pred_Q13, 1);
/* Update states */
sLTP_Q15[sLTP_buf_idx] = Inlines.silk_LSHIFT(pres_Q14[pres_Q14_ptr + i], 1);
sLTP_buf_idx++;
}
}
else {
pres_Q14 = psDec.exc_Q14;
pres_Q14_ptr = pexc_Q14;
}
for (i = 0; i < psDec.subfr_length; i++)
{
/* Short-term prediction */
Inlines.OpusAssert(psDec.LPC_order == 10 || psDec.LPC_order == 16);
/* Avoids introducing a bias because silk_SMLAWB() always rounds to -inf */
LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 1], A_Q12[0]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 2], A_Q12[1]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 3], A_Q12[2]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 4], A_Q12[3]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 5], A_Q12[4]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 6], A_Q12[5]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 7], A_Q12[6]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 8], A_Q12[7]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 9], A_Q12[8]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 10], A_Q12[9]);
if (psDec.LPC_order == 16)
{
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 11], A_Q12[10]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 12], A_Q12[11]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 13], A_Q12[12]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 14], A_Q12[13]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 15], A_Q12[14]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i - 16], A_Q12[15]);
}
/* Add prediction to LPC excitation */
sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i] = Inlines.silk_ADD_LSHIFT32(pres_Q14[pres_Q14_ptr + i], LPC_pred_Q10, 4);
/* Scale with gain */
xq[pxq + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLPC_Q14[SilkConstants.MAX_LPC_ORDER + i], Gain_Q10), 8));
}
/* DEBUG_STORE_DATA( dec.pcm, pxq, psDec.subfr_length * sizeof( short ) ) */
/* Update LPC filter state */
Array.Copy(sLPC_Q14, psDec.subfr_length, sLPC_Q14, 0, SilkConstants.MAX_LPC_ORDER);
pexc_Q14 += psDec.subfr_length;
pxq += psDec.subfr_length;
}
/* Save LPC state */
Array.Copy(sLPC_Q14, 0, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
}
}
}
@@ -0,0 +1,179 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodeIndices
{
/* Decode side-information parameters from payload */
internal static void silk_decode_indices(
SilkChannelDecoder psDec, /* I/O State */
EntropyCoder psRangeDec, /* I/O Compressor data structure */
int FrameIndex, /* I Frame number */
int decode_LBRR, /* I Flag indicating LBRR data is being decoded */
int condCoding /* I The type of conditional coding to use */
)
{
int i, k, Ix;
int decode_absolute_lagIndex, delta_lagIndex;
short[] ec_ix = new short[psDec.LPC_order];
byte[] pred_Q8 = new byte[psDec.LPC_order];
/*******************************************/
/* Decode signal type and quantizer offset */
/*******************************************/
if (decode_LBRR != 0 || psDec.VAD_flags[FrameIndex] != 0)
{
Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_VAD_iCDF, 8) + 2;
}
else {
Ix = psRangeDec.dec_icdf(Tables.silk_type_offset_no_VAD_iCDF, 8);
}
psDec.indices.signalType = (sbyte)Inlines.silk_RSHIFT(Ix, 1);
psDec.indices.quantOffsetType = (sbyte)(Ix & 1);
/****************/
/* Decode gains */
/****************/
/* First subframe */
if (condCoding == SilkConstants.CODE_CONDITIONALLY)
{
/* Conditional coding */
psDec.indices.GainsIndices[0] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
}
else {
/* Independent coding, in two stages: MSB bits followed by 3 LSBs */
psDec.indices.GainsIndices[0] = (sbyte)Inlines.silk_LSHIFT(psRangeDec.dec_icdf(Tables.silk_gain_iCDF[psDec.indices.signalType], 8), 3);
psDec.indices.GainsIndices[0] += (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform8_iCDF, 8);
}
/* Remaining subframes */
for (i = 1; i < psDec.nb_subfr; i++)
{
psDec.indices.GainsIndices[i] = (sbyte)psRangeDec.dec_icdf(Tables.silk_delta_gain_iCDF, 8);
}
/**********************/
/* Decode LSF Indices */
/**********************/
psDec.indices.NLSFIndices[0] = (sbyte)psRangeDec.dec_icdf(psDec.psNLSF_CB.CB1_iCDF, (psDec.indices.signalType >> 1) * psDec.psNLSF_CB.nVectors, 8);
NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psDec.psNLSF_CB, psDec.indices.NLSFIndices[0]);
Inlines.OpusAssert(psDec.psNLSF_CB.order == psDec.LPC_order);
for (i = 0; i < psDec.psNLSF_CB.order; i++)
{
Ix = psRangeDec.dec_icdf(psDec.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8);
if (Ix == 0)
{
Ix -= psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
}
else if (Ix == 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
Ix += psRangeDec.dec_icdf(Tables.silk_NLSF_EXT_iCDF, 8);
}
psDec.indices.NLSFIndices[i + 1] = (sbyte)(Ix - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE);
}
/* Decode LSF interpolation factor */
if (psDec.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
psDec.indices.NLSFInterpCoef_Q2 = (sbyte)psRangeDec.dec_icdf(Tables.silk_NLSF_interpolation_factor_iCDF, 8);
}
else {
psDec.indices.NLSFInterpCoef_Q2 = 4;
}
if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Decode pitch lags */
/*********************/
/* Get lag index */
decode_absolute_lagIndex = 1;
if (condCoding == SilkConstants.CODE_CONDITIONALLY && psDec.ec_prevSignalType == SilkConstants.TYPE_VOICED)
{
/* Decode Delta index */
delta_lagIndex = (short)psRangeDec.dec_icdf(Tables.silk_pitch_delta_iCDF, 8);
if (delta_lagIndex > 0)
{
delta_lagIndex = delta_lagIndex - 9;
psDec.indices.lagIndex = (short)(psDec.ec_prevLagIndex + delta_lagIndex);
decode_absolute_lagIndex = 0;
}
}
if (decode_absolute_lagIndex != 0)
{
/* Absolute decoding */
psDec.indices.lagIndex = (short)(psRangeDec.dec_icdf(Tables.silk_pitch_lag_iCDF, 8) * Inlines.silk_RSHIFT(psDec.fs_kHz, 1));
psDec.indices.lagIndex += (short)psRangeDec.dec_icdf(psDec.pitch_lag_low_bits_iCDF, 8);
}
psDec.ec_prevLagIndex = psDec.indices.lagIndex;
/* Get countour index */
psDec.indices.contourIndex = (sbyte)psRangeDec.dec_icdf(psDec.pitch_contour_iCDF, 8);
/********************/
/* Decode LTP gains */
/********************/
/* Decode PERIndex value */
psDec.indices.PERIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_per_index_iCDF, 8);
for (k = 0; k < psDec.nb_subfr; k++)
{
psDec.indices.LTPIndex[k] = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTP_gain_iCDF_ptrs[psDec.indices.PERIndex], 8);
}
/**********************/
/* Decode LTP scaling */
/**********************/
if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
{
psDec.indices.LTP_scaleIndex = (sbyte)psRangeDec.dec_icdf(Tables.silk_LTPscale_iCDF, 8);
}
else {
psDec.indices.LTP_scaleIndex = 0;
}
}
psDec.ec_prevSignalType = psDec.indices.signalType;
/***************/
/* Decode seed */
/***************/
psDec.indices.Seed = (sbyte)psRangeDec.dec_icdf(Tables.silk_uniform4_iCDF, 8);
}
}
}
@@ -0,0 +1,141 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal class DecodeParameters
{
/* Decode parameters from payload */
internal static void silk_decode_parameters(
SilkChannelDecoder psDec, /* I/O State */
SilkDecoderControl psDecCtrl, /* I/O Decoder control */
int condCoding /* I The type of conditional coding to use */
)
{
int i, k, Ix;
short[] pNLSF_Q15 = new short[psDec.LPC_order];
short[] pNLSF0_Q15 = new short[psDec.LPC_order];
sbyte[][] cbk_ptr_Q7;
/* Dequant Gains */
BoxedValueSbyte boxedLastGainIndex = new BoxedValueSbyte(psDec.LastGainIndex);
GainQuantization.silk_gains_dequant(psDecCtrl.Gains_Q16, psDec.indices.GainsIndices,
boxedLastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psDec.nb_subfr);
psDec.LastGainIndex = boxedLastGainIndex.Val;
/****************/
/* Decode NLSFs */
/****************/
NLSF.silk_NLSF_decode(pNLSF_Q15, psDec.indices.NLSFIndices, psDec.psNLSF_CB);
/* Convert NLSF parameters to AR prediction filter coefficients */
NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[1], pNLSF_Q15, psDec.LPC_order);
/* If just reset, e.g., because internal Fs changed, do not allow interpolation */
/* improves the case of packet loss in the first frame after a switch */
if (psDec.first_frame_after_reset == 1)
{
psDec.indices.NLSFInterpCoef_Q2 = 4;
}
if (psDec.indices.NLSFInterpCoef_Q2 < 4)
{
/* Calculation of the interpolated NLSF0 vector from the interpolation factor, */
/* the previous NLSF1, and the current NLSF1 */
for (i = 0; i < psDec.LPC_order; i++)
{
pNLSF0_Q15[i] = (short)(psDec.prevNLSF_Q15[i] + Inlines.silk_RSHIFT(Inlines.silk_MUL(psDec.indices.NLSFInterpCoef_Q2,
pNLSF_Q15[i] - psDec.prevNLSF_Q15[i]), 2));
}
/* Convert NLSF parameters to AR prediction filter coefficients */
NLSF.silk_NLSF2A(psDecCtrl.PredCoef_Q12[0], pNLSF0_Q15, psDec.LPC_order);
}
else
{
/* Copy LPC coefficients for first half from second half */
Array.Copy(psDecCtrl.PredCoef_Q12[1], psDecCtrl.PredCoef_Q12[0], psDec.LPC_order);
}
Array.Copy(pNLSF_Q15, psDec.prevNLSF_Q15, psDec.LPC_order);
/* After a packet loss do BWE of LPC coefs */
if (psDec.lossCnt != 0)
{
BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[0], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
BWExpander.silk_bwexpander(psDecCtrl.PredCoef_Q12[1], psDec.LPC_order, SilkConstants.BWE_AFTER_LOSS_Q16);
}
if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Decode pitch lags */
/*********************/
/* Decode pitch values */
DecodePitch.silk_decode_pitch(psDec.indices.lagIndex, psDec.indices.contourIndex, psDecCtrl.pitchL, psDec.fs_kHz, psDec.nb_subfr);
/* Decode Codebook Index */
cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[psDec.indices.PERIndex]; /* set pointer to start of codebook */
for (k = 0; k < psDec.nb_subfr; k++)
{
Ix = psDec.indices.LTPIndex[k];
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
psDecCtrl.LTPCoef_Q14[k * SilkConstants.LTP_ORDER + i] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[Ix][i], 7));
}
}
/**********************/
/* Decode LTP scaling */
/**********************/
Ix = psDec.indices.LTP_scaleIndex;
psDecCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[Ix];
}
else
{
Arrays.MemSetInt(psDecCtrl.pitchL, 0, psDec.nb_subfr);
Arrays.MemSetShort(psDecCtrl.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * psDec.nb_subfr);
psDec.indices.PERIndex = 0;
psDecCtrl.LTP_scale_Q14 = 0;
}
}
}
}
@@ -0,0 +1,90 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodePitch
{
internal static void silk_decode_pitch(
short lagIndex, /* I */
sbyte contourIndex, /* O */
int[] pitch_lags, /* O 4 pitch values */
int Fs_kHz, /* I sampling frequency (kHz) */
int nb_subfr /* I number of sub frames */
)
{
int lag, k, min_lag, max_lag;
sbyte[][] Lag_CB_ptr;
if (Fs_kHz == 8)
{
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_CB_ptr = Tables.silk_CB_lags_stage2;
}
else
{
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms;
}
}
else
{
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_CB_ptr = Tables.silk_CB_lags_stage3;
}
else
{
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms;
}
}
min_lag = Inlines.silk_SMULBB(SilkConstants.PE_MIN_LAG_MS, Fs_kHz);
max_lag = Inlines.silk_SMULBB(SilkConstants.PE_MAX_LAG_MS, Fs_kHz);
lag = min_lag + lagIndex;
for (k = 0; k < nb_subfr; k++)
{
pitch_lags[k] = lag + Lag_CB_ptr[k][contourIndex];
pitch_lags[k] = Inlines.silk_LIMIT(pitch_lags[k], min_lag, max_lag);
}
}
}
}
@@ -0,0 +1,136 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class DecodePulses
{
/*********************************************/
/* Decode quantization indices of excitation */
/*********************************************/
internal static void silk_decode_pulses(
EntropyCoder psRangeDec, /* I/O Compressor data structure */
short[] pulses, /* O Excitation signal */
int signalType, /* I Sigtype */
int quantOffsetType, /* I quantOffsetType */
int frame_length /* I Frame length */
)
{
int i, j, k, iter, abs_q, nLS, RateLevelIndex;
int[] sum_pulses = new int[SilkConstants.MAX_NB_SHELL_BLOCKS];
int[] nLshifts = new int[SilkConstants.MAX_NB_SHELL_BLOCKS];
int pulses_ptr;
/*********************/
/* Decode rate level */
/*********************/
RateLevelIndex = psRangeDec.dec_icdf(Tables.silk_rate_levels_iCDF[signalType >> 1], 8);
/* Calculate number of shell blocks */
Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH);
iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length)
{
Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */
iter++;
}
/***************************************************/
/* Sum-Weighted-Pulses Decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
nLshifts[i] = 0;
sum_pulses[i] = psRangeDec.dec_icdf(Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
/* LSB indication */
while (sum_pulses[i] == SilkConstants.SILK_MAX_PULSES + 1)
{
nLshifts[i]++;
/* When we've already got 10 LSBs, we shift the table to not allow (SILK_MAX_PULSES + 1) */
sum_pulses[i] = psRangeDec.dec_icdf(
Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], (nLshifts[i] == 10 ? 1 : 0), 8);
}
}
/***************************************************/
/* Shell decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (sum_pulses[i] > 0)
{
ShellCoder.silk_shell_decoder(pulses, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), psRangeDec, sum_pulses[i]);
}
else
{
Arrays.MemSetWithOffset<short>(pulses, 0, Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH), SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
}
/***************************************************/
/* LSB Decoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (nLshifts[i] > 0)
{
nLS = nLshifts[i];
pulses_ptr = Inlines.silk_SMULBB(i, SilkConstants.SHELL_CODEC_FRAME_LENGTH);
for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_q = pulses[pulses_ptr + k];
for (j = 0; j < nLS; j++)
{
abs_q = Inlines.silk_LSHIFT(abs_q, 1);
abs_q += psRangeDec.dec_icdf(Tables.silk_lsb_iCDF, 8);
}
pulses[pulses_ptr + k] = (short)(abs_q);
}
/* Mark the number of pulses non-zero for sign decoding. */
sum_pulses[i] |= nLS << 5;
}
}
/****************************************/
/* Decode and add signs to pulse signal */
/****************************************/
CodeSigns.silk_decode_signs(psRangeDec, pulses, frame_length, signalType, quantOffsetType, sum_pulses);
}
}
}
@@ -0,0 +1,740 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class EncodeAPI
{
/// <summary>
/// Init or Reset encoder
/// </summary>
/// <param name="encState">I/O State</param>
/// <param name="encStatus">O Encoder Status</param>
/// <returns>O Returns error code</returns>
internal static int silk_InitEncoder(SilkEncoder encState, EncControlState encStatus)
{
int ret = SilkError.SILK_NO_ERROR;
/* Reset encoder */
encState.Reset();
for (int n = 0; n < SilkConstants.ENCODER_NUM_CHANNELS; n++)
{
ret += SilkEncoder.silk_init_encoder(encState.state_Fxx[n]);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
encState.nChannelsAPI = 1;
encState.nChannelsInternal = 1;
/* Read control structure */
ret += silk_QueryEncoder(encState, encStatus);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
return ret;
}
/// <summary>
/// Read control structure from encode
/// </summary>
/// <param name="encState">I State</param>
/// <param name="encStatus">O Encoder Status</param>
/// <returns>Returns error code</returns>
internal static int silk_QueryEncoder(SilkEncoder encState, EncControlState encStatus)
{
int ret = SilkError.SILK_NO_ERROR;
SilkChannelEncoder state_Fxx = encState.state_Fxx[0];
encStatus.Reset();
encStatus.nChannelsAPI = encState.nChannelsAPI;
encStatus.nChannelsInternal = encState.nChannelsInternal;
encStatus.API_sampleRate = state_Fxx.API_fs_Hz;
encStatus.maxInternalSampleRate = state_Fxx.maxInternal_fs_Hz;
encStatus.minInternalSampleRate = state_Fxx.minInternal_fs_Hz;
encStatus.desiredInternalSampleRate = state_Fxx.desiredInternal_fs_Hz;
encStatus.payloadSize_ms = state_Fxx.PacketSize_ms;
encStatus.bitRate = state_Fxx.TargetRate_bps;
encStatus.packetLossPercentage = state_Fxx.PacketLoss_perc;
encStatus.complexity = state_Fxx.Complexity;
encStatus.useInBandFEC = state_Fxx.useInBandFEC;
encStatus.useDTX = state_Fxx.useDTX;
encStatus.useCBR = state_Fxx.useCBR;
encStatus.internalSampleRate = Inlines.silk_SMULBB(state_Fxx.fs_kHz, 1000);
encStatus.allowBandwidthSwitch = state_Fxx.allow_bandwidth_switch;
encStatus.inWBmodeWithoutVariableLP = (state_Fxx.fs_kHz == 16 && state_Fxx.sLP.mode == 0) ? 1 : 0;
return ret;
}
/// <summary>
/// Encode frame with Silk
/// Note: if prefillFlag is set, the input must contain 10 ms of audio, irrespective of what
/// encControl.payloadSize_ms is set to
/// </summary>
/// <param name="psEnc">I/O State</param>
/// <param name="encControl">I Control status</param>
/// <param name="samplesIn">I Speech sample input vector</param>
/// <param name="nSamplesIn">I Number of samples in input vector</param>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="nBytesOut">I/O Number of bytes in payload (input: Max bytes)</param>
/// <param name="prefillFlag">I Flag to indicate prefilling buffers no coding</param>
/// <returns>error code</returns>
internal static int silk_Encode(
SilkEncoder psEnc,
EncControlState encControl,
short[] samplesIn,
int nSamplesIn,
EntropyCoder psRangeEnc,
BoxedValueInt nBytesOut,
int prefillFlag)
{
int ret = SilkError.SILK_NO_ERROR;
int n, i, nBits, flags, tmp_payloadSize_ms = 0, tmp_complexity = 0;
int nSamplesToBuffer, nSamplesToBufferMax, nBlocksOf10ms;
int nSamplesFromInput = 0, nSamplesFromInputMax;
int speech_act_thr_for_switch_Q8;
int TargetRate_bps, channelRate_bps, LBRR_symbol, sum;
int[] MStargetRates_bps = new int[2];
short[] buf;
int transition, curr_block, tot_blocks;
nBytesOut.Val = 0;
if (encControl.reducedDependency != 0)
{
psEnc.state_Fxx[0].first_frame_after_reset = 1;
psEnc.state_Fxx[1].first_frame_after_reset = 1;
}
psEnc.state_Fxx[0].nFramesEncoded = psEnc.state_Fxx[1].nFramesEncoded = 0;
/* Check values in encoder control structure */
ret += encControl.check_control_input();
if (ret != SilkError.SILK_NO_ERROR)
{
Inlines.OpusAssert(false);
return ret;
}
encControl.switchReady = 0;
if (encControl.nChannelsInternal > psEnc.nChannelsInternal)
{
/* Mono . Stereo transition: init state of second channel and stereo state */
ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[1]);
Arrays.MemSetShort(psEnc.sStereo.pred_prev_Q13, 0, 2);
Arrays.MemSetShort(psEnc.sStereo.sSide, 0, 2);
psEnc.sStereo.mid_side_amp_Q0[0] = 0;
psEnc.sStereo.mid_side_amp_Q0[1] = 1;
psEnc.sStereo.mid_side_amp_Q0[2] = 0;
psEnc.sStereo.mid_side_amp_Q0[3] = 1;
psEnc.sStereo.width_prev_Q14 = 0;
psEnc.sStereo.smth_width_Q14 = (short)(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/);
if (psEnc.nChannelsAPI == 2)
{
psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state);
Array.Copy(psEnc.state_Fxx[0].In_HP_State, psEnc.state_Fxx[1].In_HP_State, 2);
}
}
transition = ((encControl.payloadSize_ms != psEnc.state_Fxx[0].PacketSize_ms) || (psEnc.nChannelsInternal != encControl.nChannelsInternal)) ? 1 : 0;
psEnc.nChannelsAPI = encControl.nChannelsAPI;
psEnc.nChannelsInternal = encControl.nChannelsInternal;
nBlocksOf10ms = Inlines.silk_DIV32(100 * nSamplesIn, encControl.API_sampleRate);
tot_blocks = (nBlocksOf10ms > 1) ? nBlocksOf10ms >> 1 : 1;
curr_block = 0;
if (prefillFlag != 0)
{
/* Only accept input length of 10 ms */
if (nBlocksOf10ms != 1)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
/* Reset Encoder */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
ret += SilkEncoder.silk_init_encoder(psEnc.state_Fxx[n]);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
tmp_payloadSize_ms = encControl.payloadSize_ms;
encControl.payloadSize_ms = 10;
tmp_complexity = encControl.complexity;
encControl.complexity = 0;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].prefillFlag = 1;
}
}
else
{
/* Only accept input lengths that are a multiple of 10 ms */
if (nBlocksOf10ms * encControl.API_sampleRate != 100 * nSamplesIn || nSamplesIn < 0)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
/* Make sure no more than one packet can be produced */
if (1000 * (int)nSamplesIn > encControl.payloadSize_ms * encControl.API_sampleRate)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES;
}
}
TargetRate_bps = Inlines.silk_RSHIFT32(encControl.bitRate, encControl.nChannelsInternal - 1);
for (n = 0; n < encControl.nChannelsInternal; n++)
{
/* Force the side channel to the same rate as the mid */
int force_fs_kHz = (n == 1) ? psEnc.state_Fxx[0].fs_kHz : 0;
ret += psEnc.state_Fxx[n].silk_control_encoder(encControl, TargetRate_bps, psEnc.allowBandwidthSwitch, n, force_fs_kHz);
if (ret != SilkError.SILK_NO_ERROR)
{
Inlines.OpusAssert(false);
return ret;
}
if (psEnc.state_Fxx[n].first_frame_after_reset != 0 || transition != 0)
{
for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++)
{
psEnc.state_Fxx[n].LBRR_flags[i] = 0;
}
}
psEnc.state_Fxx[n].inDTX = psEnc.state_Fxx[n].useDTX;
}
Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[0].fs_kHz == psEnc.state_Fxx[1].fs_kHz);
/* Input buffering/resampling and encoding */
nSamplesToBufferMax = 10 * nBlocksOf10ms * psEnc.state_Fxx[0].fs_kHz;
nSamplesFromInputMax =
Inlines.silk_DIV32_16(nSamplesToBufferMax *
psEnc.state_Fxx[0].API_fs_Hz,
(short)(psEnc.state_Fxx[0].fs_kHz * 1000));
buf = new short[nSamplesFromInputMax];
int samplesIn_ptr = 0;
while (true)
{
nSamplesToBuffer = psEnc.state_Fxx[0].frame_length - psEnc.state_Fxx[0].inputBufIx;
nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, nSamplesToBufferMax);
nSamplesFromInput = Inlines.silk_DIV32_16(nSamplesToBuffer * psEnc.state_Fxx[0].API_fs_Hz, psEnc.state_Fxx[0].fs_kHz * 1000);
/* Resample and write to buffer */
if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 2)
{
int id = psEnc.state_Fxx[0].nFramesEncoded;
for (n = 0; n < nSamplesFromInput; n++)
{
buf[n] = samplesIn[samplesIn_ptr + (2 * n)];
}
/* Making sure to start both resamplers from the same state when switching from mono to stereo */
if (psEnc.nPrevChannelsInternal == 1 && id == 0)
{
//silk_memcpy(&psEnc.state_Fxx[1].resampler_state, &psEnc.state_Fxx[0].resampler_state, sizeof(psEnc.state_Fxx[1].resampler_state));
psEnc.state_Fxx[1].resampler_state.Assign(psEnc.state_Fxx[0].resampler_state);
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
nSamplesToBuffer = psEnc.state_Fxx[1].frame_length - psEnc.state_Fxx[1].inputBufIx;
nSamplesToBuffer = Inlines.silk_min(nSamplesToBuffer, 10 * nBlocksOf10ms * psEnc.state_Fxx[1].fs_kHz);
for (n = 0; n < nSamplesFromInput; n++)
{
buf[n] = samplesIn[samplesIn_ptr + (2 * n) + 1];
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[1].resampler_state,
psEnc.state_Fxx[1].inputBuf,
psEnc.state_Fxx[1].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[1].inputBufIx += nSamplesToBuffer;
}
else if (encControl.nChannelsAPI == 2 && encControl.nChannelsInternal == 1)
{
/* Combine left and right channels before resampling */
for (n = 0; n < nSamplesFromInput; n++)
{
sum = samplesIn[samplesIn_ptr + (2 * n)] + samplesIn[samplesIn_ptr + (2 * n) + 1];
buf[n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1);
}
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
/* On the first mono frame, average the results for the two resampler states */
if (psEnc.nPrevChannelsInternal == 2 && psEnc.state_Fxx[0].nFramesEncoded == 0)
{
ret += Resampler.silk_resampler(
psEnc.state_Fxx[1].resampler_state,
psEnc.state_Fxx[1].inputBuf,
psEnc.state_Fxx[1].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
for (n = 0; n < psEnc.state_Fxx[0].frame_length; n++)
{
psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2] =
(short)(Inlines.silk_RSHIFT(psEnc.state_Fxx[0].inputBuf[psEnc.state_Fxx[0].inputBufIx + n + 2]
+ psEnc.state_Fxx[1].inputBuf[psEnc.state_Fxx[1].inputBufIx + n + 2], 1));
}
}
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
}
else
{
Inlines.OpusAssert(encControl.nChannelsAPI == 1 && encControl.nChannelsInternal == 1);
Array.Copy(samplesIn, samplesIn_ptr, buf, 0, nSamplesFromInput);
ret += Resampler.silk_resampler(
psEnc.state_Fxx[0].resampler_state,
psEnc.state_Fxx[0].inputBuf,
psEnc.state_Fxx[0].inputBufIx + 2,
buf,
0,
nSamplesFromInput);
psEnc.state_Fxx[0].inputBufIx += nSamplesToBuffer;
}
samplesIn_ptr += (nSamplesFromInput * encControl.nChannelsAPI);
nSamplesIn -= nSamplesFromInput;
/* Default */
psEnc.allowBandwidthSwitch = 0;
/* Silk encoder */
if (psEnc.state_Fxx[0].inputBufIx >= psEnc.state_Fxx[0].frame_length)
{
/* Enough data in input buffer, so encode */
Inlines.OpusAssert(psEnc.state_Fxx[0].inputBufIx == psEnc.state_Fxx[0].frame_length);
Inlines.OpusAssert(encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inputBufIx == psEnc.state_Fxx[1].frame_length);
/* Deal with LBRR data */
if (psEnc.state_Fxx[0].nFramesEncoded == 0 && prefillFlag == 0)
{
/* Create space at start of payload for VAD and FEC flags */
byte[] iCDF = { 0, 0 };
iCDF[0] = (byte)(256 - Inlines.silk_RSHIFT(256, (psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal));
psRangeEnc.enc_icdf(0, iCDF, 8);
/* Encode any LBRR data from previous packet */
/* Encode LBRR flags */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
LBRR_symbol = 0;
for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++)
{
LBRR_symbol |= Inlines.silk_LSHIFT(psEnc.state_Fxx[n].LBRR_flags[i], i);
}
psEnc.state_Fxx[n].LBRR_flag = (sbyte)(LBRR_symbol > 0 ? 1 : 0);
if (LBRR_symbol != 0 && psEnc.state_Fxx[n].nFramesPerPacket > 1)
{
psRangeEnc.enc_icdf( LBRR_symbol - 1, Tables.silk_LBRR_flags_iCDF_ptr[psEnc.state_Fxx[n].nFramesPerPacket - 2], 8);
}
}
/* Code LBRR indices and excitation signals */
for (i = 0; i < psEnc.state_Fxx[0].nFramesPerPacket; i++)
{
for (n = 0; n < encControl.nChannelsInternal; n++)
{
if (psEnc.state_Fxx[n].LBRR_flags[i] != 0)
{
int condCoding;
if (encControl.nChannelsInternal == 2 && n == 0)
{
Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[i]);
/* For LBRR data there's no need to code the mid-only flag if the side-channel LBRR flag is set */
if (psEnc.state_Fxx[1].LBRR_flags[i] == 0)
{
Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[i]);
}
}
/* Use conditional coding if previous frame available */
if (i > 0 && psEnc.state_Fxx[n].LBRR_flags[i - 1] != 0)
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
else
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
EncodeIndices.silk_encode_indices(psEnc.state_Fxx[n], psRangeEnc, i, 1, condCoding);
EncodePulses.silk_encode_pulses(psRangeEnc, psEnc.state_Fxx[n].indices_LBRR[i].signalType, psEnc.state_Fxx[n].indices_LBRR[i].quantOffsetType,
psEnc.state_Fxx[n].pulses_LBRR[i], psEnc.state_Fxx[n].frame_length);
}
}
}
/* Reset LBRR flags */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
Arrays.MemSetInt(psEnc.state_Fxx[n].LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
}
psEnc.nBitsUsedLBRR = psRangeEnc.tell();
}
HPVariableCutoff.silk_HP_variable_cutoff(psEnc.state_Fxx);
/* Total target bits for packet */
nBits = Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000);
/* Subtract bits used for LBRR */
if (prefillFlag == 0)
{
nBits -= psEnc.nBitsUsedLBRR;
}
/* Divide by number of uncoded frames left in packet */
nBits = Inlines.silk_DIV32_16(nBits, psEnc.state_Fxx[0].nFramesPerPacket);
/* Convert to bits/second */
if (encControl.payloadSize_ms == 10)
{
TargetRate_bps = Inlines.silk_SMULBB(nBits, 100);
}
else
{
TargetRate_bps = Inlines.silk_SMULBB(nBits, 50);
}
/* Subtract fraction of bits in excess of target in previous frames and packets */
TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(psEnc.nBitsExceeded, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS);
if (prefillFlag == 0 && psEnc.state_Fxx[0].nFramesEncoded > 0)
{
/* Compare actual vs target bits so far in this packet */
int bitsBalance = psRangeEnc.tell() - psEnc.nBitsUsedLBRR - nBits * psEnc.state_Fxx[0].nFramesEncoded;
TargetRate_bps -= Inlines.silk_DIV32_16(Inlines.silk_MUL(bitsBalance, 1000), TuningParameters.BITRESERVOIR_DECAY_TIME_MS);
}
/* Never exceed input bitrate */
TargetRate_bps = Inlines.silk_LIMIT(TargetRate_bps, encControl.bitRate, 5000);
/* Convert Left/Right to Mid/Side */
if (encControl.nChannelsInternal == 2)
{
BoxedValueSbyte midOnlyFlagBoxed = new BoxedValueSbyte(psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]);
Stereo.silk_stereo_LR_to_MS(psEnc.sStereo,
psEnc.state_Fxx[0].inputBuf,
2,
psEnc.state_Fxx[1].inputBuf,
2,
psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded],
midOnlyFlagBoxed,
MStargetRates_bps,
TargetRate_bps,
psEnc.state_Fxx[0].speech_activity_Q8,
encControl.toMono,
psEnc.state_Fxx[0].fs_kHz,
psEnc.state_Fxx[0].frame_length);
psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded] = midOnlyFlagBoxed.Val;
if (midOnlyFlagBoxed.Val == 0)
{
/* Reset side channel encoder memory for first frame with side coding */
if (psEnc.prev_decode_only_middle == 1)
{
psEnc.state_Fxx[1].sShape.Reset();
psEnc.state_Fxx[1].sPrefilt.Reset();
psEnc.state_Fxx[1].sNSQ.Reset();
Arrays.MemSetShort(psEnc.state_Fxx[1].prev_NLSFq_Q15, 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetInt(psEnc.state_Fxx[1].sLP.In_LP_State, 0, 2);
psEnc.state_Fxx[1].prevLag = 100;
psEnc.state_Fxx[1].sNSQ.lagPrev = 100;
psEnc.state_Fxx[1].sShape.LastGainIndex = 10;
psEnc.state_Fxx[1].prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY;
psEnc.state_Fxx[1].sNSQ.prev_gain_Q16 = 65536;
psEnc.state_Fxx[1].first_frame_after_reset = 1;
}
psEnc.state_Fxx[1].silk_encode_do_VAD();
}
else
{
psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] = 0;
}
if (prefillFlag == 0)
{
Stereo.silk_stereo_encode_pred(psRangeEnc, psEnc.sStereo.predIx[psEnc.state_Fxx[0].nFramesEncoded]);
if (psEnc.state_Fxx[1].VAD_flags[psEnc.state_Fxx[0].nFramesEncoded] == 0)
{
Stereo.silk_stereo_encode_mid_only(psRangeEnc, psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded]);
}
}
}
else
{
/* Buffering */
Array.Copy(psEnc.sStereo.sMid, psEnc.state_Fxx[0].inputBuf, 2);
Array.Copy(psEnc.state_Fxx[0].inputBuf, psEnc.state_Fxx[0].frame_length, psEnc.sStereo.sMid, 0, 2);
}
psEnc.state_Fxx[0].silk_encode_do_VAD();
/* Encode */
for (n = 0; n < encControl.nChannelsInternal; n++)
{
int maxBits, useCBR;
/* Handling rate constraints */
maxBits = encControl.maxBits;
if (tot_blocks == 2 && curr_block == 0)
{
maxBits = maxBits * 3 / 5;
}
else if (tot_blocks == 3)
{
if (curr_block == 0)
{
maxBits = maxBits * 2 / 5;
}
else if (curr_block == 1)
{
maxBits = maxBits * 3 / 4;
}
}
useCBR = (encControl.useCBR != 0 && curr_block == tot_blocks - 1) ? 1 : 0;
if (encControl.nChannelsInternal == 1)
{
channelRate_bps = TargetRate_bps;
}
else
{
channelRate_bps = MStargetRates_bps[n];
if (n == 0 && MStargetRates_bps[1] > 0)
{
useCBR = 0;
/* Give mid up to 1/2 of the max bits for that frame */
maxBits -= encControl.maxBits / (tot_blocks * 2);
}
}
if (channelRate_bps > 0)
{
int condCoding;
psEnc.state_Fxx[n].silk_control_SNR(channelRate_bps);
/* Use independent coding if no previous frame available */
if (psEnc.state_Fxx[0].nFramesEncoded - n <= 0)
{
condCoding = SilkConstants.CODE_INDEPENDENTLY;
}
else if (n > 0 && psEnc.prev_decode_only_middle != 0)
{
/* If we skipped a side frame in this packet, we don't
need LTP scaling; the LTP state is well-defined. */
condCoding = SilkConstants.CODE_INDEPENDENTLY_NO_LTP_SCALING;
}
else
{
condCoding = SilkConstants.CODE_CONDITIONALLY;
}
ret += psEnc.state_Fxx[n].silk_encode_frame(nBytesOut, psRangeEnc, condCoding, maxBits, useCBR);
Inlines.OpusAssert(ret == SilkError.SILK_NO_ERROR);
}
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].inputBufIx = 0;
psEnc.state_Fxx[n].nFramesEncoded++;
}
psEnc.prev_decode_only_middle = psEnc.sStereo.mid_only_flags[psEnc.state_Fxx[0].nFramesEncoded - 1];
/* Insert VAD and FEC flags at beginning of bitstream */
if (nBytesOut.Val > 0 && psEnc.state_Fxx[0].nFramesEncoded == psEnc.state_Fxx[0].nFramesPerPacket)
{
flags = 0;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
for (i = 0; i < psEnc.state_Fxx[n].nFramesPerPacket; i++)
{
flags = Inlines.silk_LSHIFT(flags, 1);
flags |= (int)psEnc.state_Fxx[n].VAD_flags[i];
}
flags = Inlines.silk_LSHIFT(flags, 1);
flags |= (int)psEnc.state_Fxx[n].LBRR_flag;
}
if (prefillFlag == 0)
{
psRangeEnc.enc_patch_initial_bits((uint)flags, (uint)((psEnc.state_Fxx[0].nFramesPerPacket + 1) * encControl.nChannelsInternal));
}
/* Return zero bytes if all channels DTXed */
if (psEnc.state_Fxx[0].inDTX != 0 && (encControl.nChannelsInternal == 1 || psEnc.state_Fxx[1].inDTX != 0))
{
nBytesOut.Val = 0;
}
psEnc.nBitsExceeded += nBytesOut.Val * 8;
psEnc.nBitsExceeded -= Inlines.silk_DIV32_16(Inlines.silk_MUL(encControl.bitRate, encControl.payloadSize_ms), 1000);
psEnc.nBitsExceeded = Inlines.silk_LIMIT(psEnc.nBitsExceeded, 0, 10000);
/* Update flag indicating if bandwidth switching is allowed */
speech_act_thr_for_switch_Q8 = Inlines.silk_SMLAWB(((int)((TuningParameters.SPEECH_ACTIVITY_DTX_THRES) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPEECH_ACTIVITY_DTX_THRES, 8)*/,
((int)(((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS) * ((long)1 << (16 + 8)) + 0.5))/*Inlines.SILK_CONST((1 - TuningParameters.SPEECH_ACTIVITY_DTX_THRES) / TuningParameters.MAX_BANDWIDTH_SWITCH_DELAY_MS, 16 + 8)*/,
psEnc.timeSinceSwitchAllowed_ms);
if (psEnc.state_Fxx[0].speech_activity_Q8 < speech_act_thr_for_switch_Q8)
{
psEnc.allowBandwidthSwitch = 1;
psEnc.timeSinceSwitchAllowed_ms = 0;
}
else
{
psEnc.allowBandwidthSwitch = 0;
psEnc.timeSinceSwitchAllowed_ms += encControl.payloadSize_ms;
}
}
if (nSamplesIn == 0)
{
break;
}
}
else
{
break;
}
curr_block++;
}
psEnc.nPrevChannelsInternal = encControl.nChannelsInternal;
encControl.allowBandwidthSwitch = psEnc.allowBandwidthSwitch;
encControl.inWBmodeWithoutVariableLP = (psEnc.state_Fxx[0].fs_kHz == 16 && psEnc.state_Fxx[0].sLP.mode == 0) ? 1 : 0;
encControl.internalSampleRate = Inlines.silk_SMULBB(psEnc.state_Fxx[0].fs_kHz, 1000);
encControl.stereoWidth_Q14 = encControl.toMono != 0 ? 0 : psEnc.sStereo.smth_width_Q14;
if (prefillFlag != 0)
{
encControl.payloadSize_ms = tmp_payloadSize_ms;
encControl.complexity = tmp_complexity;
for (n = 0; n < encControl.nChannelsInternal; n++)
{
psEnc.state_Fxx[n].controlled_since_last_payload = 0;
psEnc.state_Fxx[n].prefillFlag = 0;
}
}
return ret;
}
}
}
//────────────────────▄▄▄▄▄▄▄▄▄▄────────────────────
//─────────────▄▄▄▄████▓▓▓▓▓▓▓▓▓██▄▄────────────────
//─────────▄▄██▓▓▓▓▓▓▓▓▓▓▓▓▓▓▓▒▒▒▒▒░▀▀▄─────────────
//────────▀▀▀▀▀█████▓▓▓▓▒▒▒▒▒▒▒▒▒▒░░░░░▀▄───────────
//────────────▄▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░▀▄─────────
//─────▄▀▀▄─▄█▓▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░█▄▀▀▄─────
//────█░░░▄█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░█░░░░░█────
//───█░░░█▓▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░▄▀░░░▄░░█───
//──█░░░█▓▓▒▒▒▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░█░░░░░█░░█──
//──█░░█▓▒▒▄▄▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░░░░░░░░░██░░░░░░█░█──
//─█░░█▓▄▄▀█▒▒▒▒▒▒▒▒▒▒▒▒░░░░░░▄▄▀█░░░░▄▀█░░░░░░█░░█─
//─█░██▀░▄▀▒▒▒▒▒▒▒▒▒▒▒▒▄▄▄▄▀▀▀░▄▀░░░▄▀░░░░░░░░░█░░█─
//─██▀░░█▒▒▒▒▒▒▒▒▒▄▄▀▀▀░░░░░░▄▀░░▄▄▀░░░░░░░░░░░░░░█─
//──█░░█▒▒▒▒▒▒▒▄▀▀░░░░░░░░░░▀▀▀▀▀░░░░░░░░░░▄▄░░░░░█─
//───██▒▒▒▒▒▒▄▀██▄▄░░░░░█░░░░░░░░░░░░░▄▄▄█▀░░░░░░█──
//────█▒▒▒▒▒█─▓██████▄▄█░░░█░░░░▄▄▄████▓─█▀▀░░░░█───
//───█▒▒▒▒▄█─▓█──████▓─█░░░░████████──█▓─█▄░░░▄▀────
//───█▒▒▄▀█──▓█▒▓████▓─█░░░░█─▓█████▓▒█▓─█░░█▀──────
//──█▒▄▀█░█──▓███─███▓─█░░░░█─▓████─██▓──█░█────────
//──█▀──█░█──▓▓██████▓─█░░░░█─▓██████▓▓──█░█────────
//──────█░░█──▓▓████▓─█░░░░░░█─▓████▓▓──█░░█────────
//──────█░░▀▄───▓▓▓▓──█░░░░░░█──▓▓▓▓───▄▀░░█────────
//───────█░░▀▄───────█░░░░░░░░█───────▄▀░░█─────────
//───────█░░░░▀▄▄▄▄▄▀░░▄▀▀▀▀▄░░▀▄▄▄▄▄▀░░░░█─────────
//────────█░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░█──────────
//─────────█░░░░░░░░░░▀▄░░░░▄▀░░░░░░░░░░█───────────
//──────────▀▄░░░░░░░░░░░░░░░░░░░░░░░░▄▀────────────
//────────────▀▄░░░░░░▀▄░░░░▄▀░░░░░░▄▀──────────────
//──────────────▀▄▄░░░░░▀▀▀▀░░░░░▄▄▀────────────────
//─────────────────▀▀▄▄▄▄▄▄▄▄▄▄▀▀───────────────────
//──────────────────────────────────────────────────
//───────█───█───█─████──███──███──█───█─████─█─────
//──────█─█──█───█─█────█────█───█─██─██─█────█─────
//─────█▄▄▄█─█─█─█─██────██──█───█─█─█─█─██───█─────
//─────█───█──█─█──█───────█─█───█─█───█─█──────────
//─────█───█──█─█──████─███───███──█───█─████─█─────
//──────────────────────────────────────────────────
//──────────────────────────────────────────────────
@@ -0,0 +1,223 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class EncodeIndices
{
/// <summary>
/// Encode side-information parameters to payload
/// </summary>
/// <param name="psEncC">I/O Encoder state</param>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="FrameIndex">I Frame number</param>
/// <param name="encode_LBRR">I Flag indicating LBRR data is being encoded</param>
/// <param name="condCoding">I The type of conditional coding to use</param>
internal static void silk_encode_indices(
SilkChannelEncoder psEncC,
EntropyCoder psRangeEnc,
int FrameIndex,
int encode_LBRR,
int condCoding)
{
int i, k, typeOffset;
int encode_absolute_lagIndex, delta_lagIndex;
short[] ec_ix = new short[SilkConstants.MAX_LPC_ORDER];
byte[] pred_Q8 = new byte[SilkConstants.MAX_LPC_ORDER];
SideInfoIndices psIndices;
if (encode_LBRR != 0)
{
psIndices = psEncC.indices_LBRR[FrameIndex];
}
else {
psIndices = psEncC.indices;
}
/*******************************************/
/* Encode signal type and quantizer offset */
/*******************************************/
typeOffset = 2 * psIndices.signalType + psIndices.quantOffsetType;
Inlines.OpusAssert(typeOffset >= 0 && typeOffset < 6);
Inlines.OpusAssert(encode_LBRR == 0 || typeOffset >= 2);
if (encode_LBRR != 0 || typeOffset >= 2)
{
psRangeEnc.enc_icdf( typeOffset - 2, Tables.silk_type_offset_VAD_iCDF, 8);
}
else
{
psRangeEnc.enc_icdf( typeOffset, Tables.silk_type_offset_no_VAD_iCDF, 8);
}
/****************/
/* Encode gains */
/****************/
/* first subframe */
if (condCoding == SilkConstants.CODE_CONDITIONALLY)
{
/* conditional coding */
Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1);
psRangeEnc.enc_icdf( psIndices.GainsIndices[0], Tables.silk_delta_gain_iCDF, 8);
}
else
{
/* independent coding, in two stages: MSB bits followed by 3 LSBs */
Inlines.OpusAssert(psIndices.GainsIndices[0] >= 0 && psIndices.GainsIndices[0] < SilkConstants.N_LEVELS_QGAIN);
psRangeEnc.enc_icdf( Inlines.silk_RSHIFT(psIndices.GainsIndices[0], 3), Tables.silk_gain_iCDF[psIndices.signalType], 8);
psRangeEnc.enc_icdf( psIndices.GainsIndices[0] & 7, Tables.silk_uniform8_iCDF, 8);
}
/* remaining subframes */
for (i = 1; i < psEncC.nb_subfr; i++)
{
Inlines.OpusAssert(psIndices.GainsIndices[i] >= 0 && psIndices.GainsIndices[i] < SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.MIN_DELTA_GAIN_QUANT + 1);
psRangeEnc.enc_icdf( psIndices.GainsIndices[i], Tables.silk_delta_gain_iCDF, 8);
}
/****************/
/* Encode NLSFs */
/****************/
psRangeEnc.enc_icdf( psIndices.NLSFIndices[0], psEncC.psNLSF_CB.CB1_iCDF, ((psIndices.signalType >> 1) * psEncC.psNLSF_CB.nVectors), 8);
NLSF.silk_NLSF_unpack(ec_ix, pred_Q8, psEncC.psNLSF_CB, psIndices.NLSFIndices[0]);
Inlines.OpusAssert(psEncC.psNLSF_CB.order == psEncC.predictLPCOrder);
for (i = 0; i < psEncC.psNLSF_CB.order; i++)
{
if (psIndices.NLSFIndices[i + 1] >= SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
psRangeEnc.enc_icdf( 2 * SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, (ec_ix[i]), 8);
psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8);
}
else if (psIndices.NLSFIndices[i + 1] <= 0 - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE)
{
psRangeEnc.enc_icdf( 0, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8);
psRangeEnc.enc_icdf( -psIndices.NLSFIndices[i + 1] - SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, Tables.silk_NLSF_EXT_iCDF, 8);
}
else
{
psRangeEnc.enc_icdf( psIndices.NLSFIndices[i + 1] + SilkConstants.NLSF_QUANT_MAX_AMPLITUDE, psEncC.psNLSF_CB.ec_iCDF, ec_ix[i], 8);
}
}
/* Encode NLSF interpolation factor */
if (psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
Inlines.OpusAssert(psIndices.NLSFInterpCoef_Q2 >= 0 && psIndices.NLSFInterpCoef_Q2 < 5);
psRangeEnc.enc_icdf( psIndices.NLSFInterpCoef_Q2, Tables.silk_NLSF_interpolation_factor_iCDF, 8);
}
if (psIndices.signalType == SilkConstants.TYPE_VOICED)
{
/*********************/
/* Encode pitch lags */
/*********************/
/* lag index */
encode_absolute_lagIndex = 1;
if (condCoding == SilkConstants.CODE_CONDITIONALLY && psEncC.ec_prevSignalType == SilkConstants.TYPE_VOICED)
{
/* Delta Encoding */
delta_lagIndex = psIndices.lagIndex - psEncC.ec_prevLagIndex;
if (delta_lagIndex < -8 || delta_lagIndex > 11)
{
delta_lagIndex = 0;
}
else
{
delta_lagIndex = delta_lagIndex + 9;
encode_absolute_lagIndex = 0; /* Only use delta */
}
Inlines.OpusAssert(delta_lagIndex >= 0 && delta_lagIndex < 21);
psRangeEnc.enc_icdf( delta_lagIndex, Tables.silk_pitch_delta_iCDF, 8);
}
if (encode_absolute_lagIndex != 0)
{
/* Absolute encoding */
int pitch_high_bits, pitch_low_bits;
pitch_high_bits = Inlines.silk_DIV32_16(psIndices.lagIndex, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1));
pitch_low_bits = psIndices.lagIndex - Inlines.silk_SMULBB(pitch_high_bits, Inlines.silk_RSHIFT(psEncC.fs_kHz, 1));
Inlines.OpusAssert(pitch_low_bits < psEncC.fs_kHz / 2);
Inlines.OpusAssert(pitch_high_bits < 32);
psRangeEnc.enc_icdf( pitch_high_bits, Tables.silk_pitch_lag_iCDF, 8);
psRangeEnc.enc_icdf( pitch_low_bits, psEncC.pitch_lag_low_bits_iCDF, 8);
}
psEncC.ec_prevLagIndex = psIndices.lagIndex;
/* Countour index */
Inlines.OpusAssert(psIndices.contourIndex >= 0);
Inlines.OpusAssert((psIndices.contourIndex < 34 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 11 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 4) || (psIndices.contourIndex < 12 && psEncC.fs_kHz > 8 && psEncC.nb_subfr == 2) || (psIndices.contourIndex < 3 && psEncC.fs_kHz == 8 && psEncC.nb_subfr == 2));
psRangeEnc.enc_icdf( psIndices.contourIndex, psEncC.pitch_contour_iCDF, 8);
/********************/
/* Encode LTP gains */
/********************/
/* PERIndex value */
Inlines.OpusAssert(psIndices.PERIndex >= 0 && psIndices.PERIndex < 3);
psRangeEnc.enc_icdf( psIndices.PERIndex, Tables.silk_LTP_per_index_iCDF, 8);
/* Codebook Indices */
for (k = 0; k < psEncC.nb_subfr; k++)
{
Inlines.OpusAssert(psIndices.LTPIndex[k] >= 0 && psIndices.LTPIndex[k] < (8 << psIndices.PERIndex));
psRangeEnc.enc_icdf( psIndices.LTPIndex[k], Tables.silk_LTP_gain_iCDF_ptrs[psIndices.PERIndex], 8);
}
/**********************/
/* Encode LTP scaling */
/**********************/
if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
{
Inlines.OpusAssert(psIndices.LTP_scaleIndex >= 0 && psIndices.LTP_scaleIndex < 3);
psRangeEnc.enc_icdf( psIndices.LTP_scaleIndex, Tables.silk_LTPscale_iCDF, 8);
}
Inlines.OpusAssert(condCoding == 0 || psIndices.LTP_scaleIndex == 0);
}
psEncC.ec_prevSignalType = psIndices.signalType;
/***************/
/* Encode seed */
/***************/
Inlines.OpusAssert(psIndices.Seed >= 0 && psIndices.Seed < 4);
psRangeEnc.enc_icdf( psIndices.Seed, Tables.silk_uniform4_iCDF, 8);
}
}
}
@@ -0,0 +1,278 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class EncodePulses
{
/// <summary>
///
/// </summary>
/// <param name="pulses_comb">(O)</param>
/// <param name="pulses_in">(I)</param>
/// <param name="max_pulses"> I max value for sum of pulses</param>
/// <param name="len">I number of output values</param>
/// <returns>return ok</returns>
internal static int combine_and_check(
int[] pulses_comb,
int pulses_comb_ptr,
int[] pulses_in,
int pulses_in_ptr,
int max_pulses,
int len)
{
for (int k = 0; k < len; k++)
{
int k2p = 2 * k + pulses_in_ptr;
int sum = pulses_in[k2p] + pulses_in[k2p + 1];
if (sum > max_pulses)
{
return 1;
}
pulses_comb[pulses_comb_ptr + k] = sum;
}
return 0;
}
/// <summary>
///
/// </summary>
/// <param name="pulses_comb">(O)</param>
/// <param name="pulses_in">(I)</param>
/// <param name="max_pulses"> I max value for sum of pulses</param>
/// <param name="len">I number of output values</param>
/// <returns>return ok</returns>
internal static int combine_and_check(
int[] pulses_comb,
int[] pulses_in,
int max_pulses,
int len)
{
for (int k = 0; k < len; k++)
{
int sum = pulses_in[2 * k] + pulses_in[2 * k + 1];
if (sum > max_pulses)
{
return 1;
}
pulses_comb[k] = sum;
}
return 0;
}
/// <summary>
/// Encode quantization indices of excitation
/// </summary>
/// <param name="psRangeEnc">I/O compressor data structure</param>
/// <param name="signalType">I Signal type</param>
/// <param name="quantOffsetType">I quantOffsetType</param>
/// <param name="pulses">I quantization indices</param>
/// <param name="frame_length">I Frame length</param>
internal static void silk_encode_pulses(
EntropyCoder psRangeEnc,
int signalType,
int quantOffsetType,
sbyte[] pulses,
int frame_length)
{
int i, k, j, iter, bit, nLS, scale_down, RateLevelIndex = 0;
int abs_q, minSumBits_Q5, sumBits_Q5;
int[] abs_pulses;
int[] sum_pulses;
int[] nRshifts;
int[] pulses_comb = new int[8];
int abs_pulses_ptr;
int pulses_ptr;
byte[] nBits_ptr;
Arrays.MemSetInt(pulses_comb, 0, 8);
/****************************/
/* Prepare for shell coding */
/****************************/
/* Calculate number of shell blocks */
Inlines.OpusAssert(1 << SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH == SilkConstants.SHELL_CODEC_FRAME_LENGTH);
iter = Inlines.silk_RSHIFT(frame_length, SilkConstants.LOG2_SHELL_CODEC_FRAME_LENGTH);
if (iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH < frame_length)
{
Inlines.OpusAssert(frame_length == 12 * 10); /* Make sure only happens for 10 ms @ 12 kHz */
iter++;
Arrays.MemSetWithOffset<sbyte>(pulses, 0, frame_length, SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
/* Take the absolute value of the pulses */
abs_pulses = new int[iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH];
Inlines.OpusAssert((SilkConstants.SHELL_CODEC_FRAME_LENGTH & 3) == 0);
// unrolled loop
for (i = 0; i < iter * SilkConstants.SHELL_CODEC_FRAME_LENGTH; i += 4)
{
abs_pulses[i + 0] = (int)Inlines.silk_abs(pulses[i + 0]);
abs_pulses[i + 1] = (int)Inlines.silk_abs(pulses[i + 1]);
abs_pulses[i + 2] = (int)Inlines.silk_abs(pulses[i + 2]);
abs_pulses[i + 3] = (int)Inlines.silk_abs(pulses[i + 3]);
}
/* Calc sum pulses per shell code frame */
sum_pulses = new int[iter];
nRshifts = new int[iter];
abs_pulses_ptr = 0;
for (i = 0; i < iter; i++)
{
nRshifts[i] = 0;
while (true)
{
/* 1+1 . 2 */
scale_down = combine_and_check(pulses_comb, 0, abs_pulses, abs_pulses_ptr, Tables.silk_max_pulses_table[0], 8);
/* 2+2 . 4 */
scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[1], 4);
/* 4+4 . 8 */
scale_down += combine_and_check(pulses_comb, pulses_comb, Tables.silk_max_pulses_table[2], 2);
/* 8+8 . 16 */
scale_down += combine_and_check(sum_pulses, i, pulses_comb, 0, Tables.silk_max_pulses_table[3], 1);
if (scale_down != 0)
{
/* We need to downscale the quantization signal */
nRshifts[i]++;
for (k = abs_pulses_ptr; k < abs_pulses_ptr + SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_pulses[k] = Inlines.silk_RSHIFT(abs_pulses[k], 1);
}
}
else
{
/* Jump out of while(1) loop and go to next shell coding frame */
break;
}
}
abs_pulses_ptr += SilkConstants.SHELL_CODEC_FRAME_LENGTH;
}
/**************/
/* Rate level */
/**************/
/* find rate level that leads to fewest bits for coding of pulses per block info */
minSumBits_Q5 = int.MaxValue;
for (k = 0; k < SilkConstants.N_RATE_LEVELS - 1; k++)
{
nBits_ptr = Tables.silk_pulses_per_block_BITS_Q5[k];
sumBits_Q5 = Tables.silk_rate_levels_BITS_Q5[signalType >> 1][k];
for (i = 0; i < iter; i++)
{
if (nRshifts[i] > 0)
{
sumBits_Q5 += nBits_ptr[SilkConstants.SILK_MAX_PULSES + 1];
}
else {
sumBits_Q5 += nBits_ptr[sum_pulses[i]];
}
}
if (sumBits_Q5 < minSumBits_Q5)
{
minSumBits_Q5 = sumBits_Q5;
RateLevelIndex = k;
}
}
psRangeEnc.enc_icdf( RateLevelIndex, Tables.silk_rate_levels_iCDF[signalType >> 1], 8);
/***************************************************/
/* Sum-Weighted-Pulses Encoding */
/***************************************************/
for (i = 0; i < iter; i++)
{
if (nRshifts[i] == 0)
{
psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
}
else
{
psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[RateLevelIndex], 8);
for (k = 0; k < nRshifts[i] - 1; k++)
{
psRangeEnc.enc_icdf( SilkConstants.SILK_MAX_PULSES + 1, Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8);
}
psRangeEnc.enc_icdf( sum_pulses[i], Tables.silk_pulses_per_block_iCDF[SilkConstants.N_RATE_LEVELS - 1], 8);
}
}
/******************/
/* Shell Encoding */
/******************/
for (i = 0; i < iter; i++)
{
if (sum_pulses[i] > 0)
{
ShellCoder.silk_shell_encoder(psRangeEnc, abs_pulses, i * SilkConstants.SHELL_CODEC_FRAME_LENGTH);
}
}
/****************/
/* LSB Encoding */
/****************/
for (i = 0; i < iter; i++)
{
if (nRshifts[i] > 0)
{
pulses_ptr = i * SilkConstants.SHELL_CODEC_FRAME_LENGTH;
nLS = nRshifts[i] - 1;
for (k = 0; k < SilkConstants.SHELL_CODEC_FRAME_LENGTH; k++)
{
abs_q = (sbyte)Inlines.silk_abs(pulses[pulses_ptr + k]);
for (j = nLS; j > 0; j--)
{
bit = Inlines.silk_RSHIFT(abs_q, j) & 1;
psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8);
}
bit = abs_q & 1;
psRangeEnc.enc_icdf( bit, Tables.silk_lsb_iCDF, 8);
}
}
}
/****************/
/* Encode signs */
/****************/
CodeSigns.silk_encode_signs(psRangeEnc, pulses, frame_length, signalType, quantOffsetType, sum_pulses);
}
}
}
@@ -0,0 +1,41 @@
/* 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.
*/
namespace Concentus.Silk.Enums
{
internal static class DecoderAPIFlag
{
public const int FLAG_DECODE_NORMAL = 0;
public const int FLAG_PACKET_LOST = 1;
public const int FLAG_DECODE_LBRR = 2;
}
}
@@ -0,0 +1,91 @@
/* 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.
*/
namespace Concentus.Silk.Enums
{
/// <summary>
/// Represents error messages from a silk encoder/decoder
/// </summary>
internal static class SilkError
{
internal static int SILK_NO_ERROR = 0;
// Encoder error messages
/* Input length is not a multiple of 10 ms, or length is longer than the packet length */
internal static int SILK_ENC_INPUT_INVALID_NO_OF_SAMPLES = -101;
/* Sampling frequency not 8000, 12000 or 16000 Hertz */
internal static int SILK_ENC_FS_NOT_SUPPORTED = -102;
/* Packet size not 10, 20, 40, or 60 ms */
internal static int SILK_ENC_PACKET_SIZE_NOT_SUPPORTED = -103;
/* Allocated payload buffer too short */
internal static int SILK_ENC_PAYLOAD_BUF_TOO_SHORT = -104;
/* Loss rate not between 0 and 100 percent */
internal static int SILK_ENC_INVALID_LOSS_RATE = -105;
/* Complexity setting not valid, use 0...10 */
internal static int SILK_ENC_INVALID_COMPLEXITY_SETTING = -106;
/* Inband FEC setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_INBAND_FEC_SETTING = -107;
/* DTX setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_DTX_SETTING = -108;
/* CBR setting not valid, use 0 or 1 */
internal static int SILK_ENC_INVALID_CBR_SETTING = -109;
/* Internal encoder error */
internal static int SILK_ENC_INTERNAL_ERROR = -110;
/* Internal encoder error */
internal static int SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR = -111;
// Decoder error messages
/* Output sampling frequency lower than internal decoded sampling frequency */
internal static int SILK_DEC_INVALID_SAMPLING_FREQUENCY = -200;
/* Payload size exceeded the maximum allowed 1024 bytes */
internal static int SILK_DEC_PAYLOAD_TOO_LARGE = -201;
/* Payload has bit errors */
internal static int SILK_DEC_PAYLOAD_ERROR = -202;
/* Payload has bit errors */
internal static int SILK_DEC_INVALID_FRAME_SIZE = -203;
}
}
@@ -0,0 +1,714 @@
/* 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.
*/
namespace Concentus.Silk
{
using Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class Filters
{
internal static void silk_warped_LPC_analysis_filter(
int[] state, /* I/O State [order + 1] */
int[] res_Q2, /* O Residual signal [length] */
short[] coef_Q13, /* I Coefficients [order] */
int coef_Q13_ptr,
short[] input, /* I Input signal [length] */
int input_ptr,
short lambda_Q16, /* I Warping factor */
int length, /* I Length of input signal */
int order /* I Filter order (even) */
)
{
int n, i;
int acc_Q11, tmp1, tmp2;
/* Order must be even */
Inlines.OpusAssert((order & 1) == 0);
for (n = 0; n < length; n++)
{
/* Output of lowpass section */
tmp2 = Inlines.silk_SMLAWB(state[0], state[1], lambda_Q16);
state[0] = Inlines.silk_LSHIFT(input[input_ptr + n], 14);
/* Output of allpass section */
tmp1 = Inlines.silk_SMLAWB(state[1], state[2] - tmp2, lambda_Q16);
state[1] = tmp2;
acc_Q11 = Inlines.silk_RSHIFT(order, 1);
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr]);
/* Loop over allpass sections */
for (i = 2; i < order; i += 2)
{
/* Output of allpass section */
tmp2 = Inlines.silk_SMLAWB(state[i], state[i + 1] - tmp1, lambda_Q16);
state[i] = tmp1;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + i - 1]);
/* Output of allpass section */
tmp1 = Inlines.silk_SMLAWB(state[i + 1], state[i + 2] - tmp2, lambda_Q16);
state[i + 1] = tmp2;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp2, coef_Q13[coef_Q13_ptr + i]);
}
state[order] = tmp1;
acc_Q11 = Inlines.silk_SMLAWB(acc_Q11, tmp1, coef_Q13[coef_Q13_ptr + order - 1]);
res_Q2[n] = Inlines.silk_LSHIFT((int)input[input_ptr + n], 2) - Inlines.silk_RSHIFT_ROUND(acc_Q11, 9);
}
}
internal static void silk_prefilter(
SilkChannelEncoder psEnc, /* I/O Encoder state */
SilkEncoderControl psEncCtrl, /* I Encoder control */
int[] xw_Q3, /* O Weighted signal */
short[] x, /* I Speech signal */
int x_ptr
)
{
SilkPrefilterState P = psEnc.sPrefilt;
int j, k, lag;
int tmp_32;
int AR1_shp_Q13;
int px;
int pxw_Q3;
int HarmShapeGain_Q12, Tilt_Q14;
int HarmShapeFIRPacked_Q12, LF_shp_Q14;
int[] x_filt_Q12;
int[] st_res_Q2;
short[] B_Q10 = new short[2];
/* Set up pointers */
px = x_ptr;
pxw_Q3 = 0;
lag = P.lagPrev;
x_filt_Q12 = new int[psEnc.subfr_length];
st_res_Q2 = new int[psEnc.subfr_length];
for (k = 0; k < psEnc.nb_subfr; k++)
{
/* Update Variables that change per sub frame */
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
lag = psEncCtrl.pitchL[k];
}
/* Noise shape parameters */
HarmShapeGain_Q12 = Inlines.silk_SMULWB((int)psEncCtrl.HarmShapeGain_Q14[k], 16384 - psEncCtrl.HarmBoost_Q14[k]);
Inlines.OpusAssert(HarmShapeGain_Q12 >= 0);
HarmShapeFIRPacked_Q12 = Inlines.silk_RSHIFT(HarmShapeGain_Q12, 2);
HarmShapeFIRPacked_Q12 |= Inlines.silk_LSHIFT((int)Inlines.silk_RSHIFT(HarmShapeGain_Q12, 1), 16);
Tilt_Q14 = psEncCtrl.Tilt_Q14[k];
LF_shp_Q14 = psEncCtrl.LF_shp_Q14[k];
AR1_shp_Q13 = k * SilkConstants.MAX_SHAPE_LPC_ORDER;
/* Short term FIR filtering*/
silk_warped_LPC_analysis_filter(P.sAR_shp, st_res_Q2, psEncCtrl.AR1_Q13, AR1_shp_Q13, x, px,
(short)(psEnc.warping_Q16), psEnc.subfr_length, psEnc.shapingLPCOrder);
/* Reduce (mainly) low frequencies during harmonic emphasis */
B_Q10[0] = (short)(Inlines.silk_RSHIFT_ROUND(psEncCtrl.GainsPre_Q14[k], 4));
tmp_32 = Inlines.silk_SMLABB(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/, psEncCtrl.HarmBoost_Q14[k], HarmShapeGain_Q12); /* Q26 */
tmp_32 = Inlines.silk_SMLABB(tmp_32, psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/); /* Q26 */
tmp_32 = Inlines.silk_SMULWB(tmp_32, -psEncCtrl.GainsPre_Q14[k]); /* Q24 */
tmp_32 = Inlines.silk_RSHIFT_ROUND(tmp_32, 14); /* Q10 */
B_Q10[1] = (short)(Inlines.silk_SAT16(tmp_32));
x_filt_Q12[0] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[0], B_Q10[0]), P.sHarmHP_Q2, B_Q10[1]);
for (j = 1; j < psEnc.subfr_length; j++)
{
x_filt_Q12[j] = Inlines.silk_MLA(Inlines.silk_MUL(st_res_Q2[j], B_Q10[0]), st_res_Q2[j - 1], B_Q10[1]);
}
P.sHarmHP_Q2 = st_res_Q2[psEnc.subfr_length - 1];
silk_prefilt(P, x_filt_Q12, xw_Q3, pxw_Q3, HarmShapeFIRPacked_Q12, Tilt_Q14, LF_shp_Q14, lag, psEnc.subfr_length);
px += psEnc.subfr_length;
pxw_Q3 += psEnc.subfr_length;
}
P.lagPrev = psEncCtrl.pitchL[psEnc.nb_subfr - 1];
}
/* Prefilter for finding Quantizer input signal */
static void silk_prefilt(
SilkPrefilterState P, /* I/O state */
int[] st_res_Q12, /* I short term residual signal */
int[] xw_Q3, /* O prefiltered signal */
int xw_Q3_ptr,
int HarmShapeFIRPacked_Q12, /* I Harmonic shaping coeficients */
int Tilt_Q14, /* I Tilt shaping coeficient */
int LF_shp_Q14, /* I Low-frequancy shaping coeficients */
int lag, /* I Lag for harmonic shaping */
int length /* I Length of signals */
)
{
int i, idx, LTP_shp_buf_idx;
int n_LTP_Q12, n_Tilt_Q10, n_LF_Q10;
int sLF_MA_shp_Q12, sLF_AR_shp_Q12;
short[] LTP_shp_buf;
/* To speed up use temp variables instead of using the struct */
LTP_shp_buf = P.sLTP_shp;
LTP_shp_buf_idx = P.sLTP_shp_buf_idx;
sLF_AR_shp_Q12 = P.sLF_AR_shp_Q12;
sLF_MA_shp_Q12 = P.sLF_MA_shp_Q12;
for (i = 0; i < length; i++)
{
if (lag > 0)
{
/* unrolled loop */
Inlines.OpusAssert(SilkConstants.HARM_SHAPE_FIR_TAPS == 3);
idx = lag + LTP_shp_buf_idx;
n_LTP_Q12 = Inlines.silk_SMULBB(LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 - 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
n_LTP_Q12 = Inlines.silk_SMLABT(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
n_LTP_Q12 = Inlines.silk_SMLABB(n_LTP_Q12, LTP_shp_buf[(idx - SilkConstants.HARM_SHAPE_FIR_TAPS / 2 + 1) & SilkConstants.LTP_MASK], HarmShapeFIRPacked_Q12);
}
else {
n_LTP_Q12 = 0;
}
n_Tilt_Q10 = Inlines.silk_SMULWB(sLF_AR_shp_Q12, Tilt_Q14);
n_LF_Q10 = Inlines.silk_SMLAWB(Inlines.silk_SMULWT(sLF_AR_shp_Q12, LF_shp_Q14), sLF_MA_shp_Q12, LF_shp_Q14);
sLF_AR_shp_Q12 = Inlines.silk_SUB32(st_res_Q12[i], Inlines.silk_LSHIFT(n_Tilt_Q10, 2));
sLF_MA_shp_Q12 = Inlines.silk_SUB32(sLF_AR_shp_Q12, Inlines.silk_LSHIFT(n_LF_Q10, 2));
LTP_shp_buf_idx = (LTP_shp_buf_idx - 1) & SilkConstants.LTP_MASK;
LTP_shp_buf[LTP_shp_buf_idx] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sLF_MA_shp_Q12, 12));
xw_Q3[xw_Q3_ptr + i] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(sLF_MA_shp_Q12, n_LTP_Q12), 9);
}
/* Copy temp variable back to state */
P.sLF_AR_shp_Q12 = sLF_AR_shp_Q12;
P.sLF_MA_shp_Q12 = sLF_MA_shp_Q12;
P.sLTP_shp_buf_idx = LTP_shp_buf_idx;
}
#if !UNSAFE
/// <summary>
/// Second order ARMA filter, alternative implementation
/// </summary>
/// <param name="input">I input signal</param>
/// <param name="B_Q28">I MA coefficients [3]</param>
/// <param name="A_Q28">I AR coefficients [2]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="output">O output signal</param>
/// <param name="len">I signal length (must be even)</param>
/// <param name="stride">I Operate on interleaved signal if > 1</param>
internal static void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
for (k = 0; k < len; k++)
{
/* S[ 0 ], S[ 1 ]: Q12 */
inval = input[input_ptr + k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[0], B_Q28[0], inval), 2);
S[0] = S[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
S[0] = Inlines.silk_SMLAWB(S[0], out32_Q14, A0_U_Q28);
S[0] = Inlines.silk_SMLAWB(S[0], B_Q28[1], inval);
S[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
S[1] = Inlines.silk_SMLAWB(S[1], out32_Q14, A1_U_Q28);
S[1] = Inlines.silk_SMLAWB(S[1], B_Q28[2], inval);
/* Scale back to Q0 and saturate */
output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
internal static void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
int S_ptr,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
for (k = 0; k < len; k++)
{
int s1 = S_ptr + 1;
/* S[ 0 ], S[ 1 ]: Q12 */
inval = input[input_ptr + k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(S[S_ptr], B_Q28[0], inval), 2);
S[S_ptr] = S[s1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], out32_Q14, A0_U_Q28);
S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr], B_Q28[1], inval);
S[s1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
S[s1] = Inlines.silk_SMLAWB(S[s1], out32_Q14, A1_U_Q28);
S[s1] = Inlines.silk_SMLAWB(S[s1], B_Q28[2], inval);
/* Scale back to Q0 and saturate */
output[output_ptr + k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
#else
/// <summary>
/// Second order ARMA filter, alternative implementation
/// </summary>
/// <param name="input">I input signal</param>
/// <param name="B_Q28">I MA coefficients [3]</param>
/// <param name="A_Q28">I AR coefficients [2]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="output">O output signal</param>
/// <param name="len">I signal length (must be even)</param>
/// <param name="stride">I Operate on interleaved signal if > 1</param>
internal static unsafe void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
int S_ptr,
short[] output,
int output_ptr,
int len,
int stride)
{
/* DIRECT FORM II TRANSPOSED (uses 2 element state vector) */
int k;
int inval, A0_U_Q28, A0_L_Q28, A1_U_Q28, A1_L_Q28, out32_Q14;
/* Negate A_Q28 values and split in two parts */
A0_L_Q28 = (-A_Q28[0]) & 0x00003FFF; /* lower part */
A0_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[0], 14); /* upper part */
A1_L_Q28 = (-A_Q28[1]) & 0x00003FFF; /* lower part */
A1_U_Q28 = Inlines.silk_RSHIFT(-A_Q28[1], 14); /* upper part */
fixed (short* pinput_base = input, poutput_base = output)
{
fixed (int* pS_base = S, pBQ28 = B_Q28)
{
int* pS = pS_base + S_ptr;
short* pinput = pinput_base + input_ptr;
short* poutput = poutput_base + output_ptr;
for (k = 0; k < len; k++)
{
/* S[ 0 ], S[ 1 ]: Q12 */
inval = pinput[k * stride];
out32_Q14 = Inlines.silk_LSHIFT(Inlines.silk_SMLAWB(pS[0], pBQ28[0], inval), 2);
pS[0] = pS[1] + Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A0_L_Q28), 14);
pS[0] = Inlines.silk_SMLAWB(pS[0], out32_Q14, A0_U_Q28);
pS[0] = Inlines.silk_SMLAWB(pS[0], pBQ28[1], inval);
pS[1] = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWB(out32_Q14, A1_L_Q28), 14);
pS[1] = Inlines.silk_SMLAWB(pS[1], out32_Q14, A1_U_Q28);
pS[1] = Inlines.silk_SMLAWB(pS[1], pBQ28[2], inval);
/* Scale back to Q0 and saturate */
poutput[k * stride] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(out32_Q14 + (1 << 14) - 1, 14));
}
}
}
}
internal static unsafe void silk_biquad_alt(
short[] input,
int input_ptr,
int[] B_Q28,
int[] A_Q28,
int[] S,
short[] output,
int output_ptr,
int len,
int stride)
{
silk_biquad_alt(input, input_ptr, B_Q28, A_Q28, S, 0, output, output_ptr, len, stride);
}
#endif
/* Coefficients for 2-band filter bank based on first-order allpass filters */
private readonly static short A_fb1_20 = 5394 << 1;
private readonly static short A_fb1_21 = -24290; /* (opus_int16)(20623 << 1) */
/// <summary>
/// Split signal into two decimated bands using first-order allpass filters
/// </summary>
/// <param name="input">I Input signal [N]</param>
/// <param name="S">I/O State vector [2]</param>
/// <param name="outL">O Low band [N/2]</param>
/// <param name="outH">O High band [N/2]</param>
/// <param name="N">I Number of input samples</param>
internal static void silk_ana_filt_bank_1(
short[] input,
int input_ptr,
int[] S,
short[] outL,
short[] outH,
int outH_ptr,
int N)
{
int k, N2 = Inlines.silk_RSHIFT(N, 1);
int in32, X, Y, out_1, out_2;
/* Internal variables and state are in Q10 format */
for (k = 0; k < N2; k++)
{
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k], 10);
/* All-pass section for even input sample */
Y = Inlines.silk_SUB32(in32, S[0]);
X = Inlines.silk_SMLAWB(Y, Y, A_fb1_21);
out_1 = Inlines.silk_ADD32(S[0], X);
S[0] = Inlines.silk_ADD32(in32, X);
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[input_ptr + 2 * k + 1], 10);
/* All-pass section for odd input sample, and add to output of previous section */
Y = Inlines.silk_SUB32(in32, S[1]);
X = Inlines.silk_SMULWB(Y, A_fb1_20);
out_2 = Inlines.silk_ADD32(S[1], X);
S[1] = Inlines.silk_ADD32(in32, X);
/* Add/subtract, convert back to int16 and store to output */
outL[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD32(out_2, out_1), 11));
outH[outH_ptr + k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SUB32(out_2, out_1), 11));
}
}
/// <summary>
/// Chirp (bandwidth expand) LP AR filter
/// </summary>
/// <param name="ar">I/O AR filter to be expanded (without leading 1)</param>
/// <param name="d">I Length of ar</param>
/// <param name="chirp_Q16">I Chirp factor in Q16</param>
internal static void silk_bwexpander_32(int[] ar, int d, int chirp_Q16)
{
int i;
int chirp_minus_one_Q16 = chirp_Q16 - 65536;
for (i = 0; i < d - 1; i++)
{
ar[i] = Inlines.silk_SMULWW(chirp_Q16, ar[i]);
chirp_Q16 += Inlines.silk_RSHIFT_ROUND(Inlines.silk_MUL(chirp_Q16, chirp_minus_one_Q16), 16);
}
ar[d - 1] = Inlines.silk_SMULWW(chirp_Q16, ar[d - 1]);
}
/// <summary>
/// Elliptic/Cauer filters designed with 0.1 dB passband ripple,
/// 80 dB minimum stopband attenuation, and
/// [0.95 : 0.15 : 0.35] normalized cut off frequencies.
/// Helper function, interpolates the filter taps
/// </summary>
/// <param name="B_Q28">order [TRANSITION_NB]</param>
/// <param name="A_Q28">order [TRANSITION_NA]</param>
/// <param name="ind"></param>
/// <param name="fac_Q16"></param>
internal static void silk_LP_interpolate_filter_taps(
int[] B_Q28,
int[] A_Q28,
int ind,
int fac_Q16)
{
int nb, na;
if (ind < SilkConstants.TRANSITION_INT_NUM - 1)
{
if (fac_Q16 > 0)
{
if (fac_Q16 < 32768)
{
/* fac_Q16 is in range of a 16-bit int */
/* Piece-wise linear interpolation of B and A */
for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++)
{
B_Q28[nb] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_B_Q28[ind][nb],
Tables.silk_Transition_LP_B_Q28[ind + 1][nb] -
Tables.silk_Transition_LP_B_Q28[ind][nb],
fac_Q16);
}
for (na = 0; na < SilkConstants.TRANSITION_NA; na++)
{
A_Q28[na] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_A_Q28[ind][na],
Tables.silk_Transition_LP_A_Q28[ind + 1][na] -
Tables.silk_Transition_LP_A_Q28[ind][na],
fac_Q16);
}
}
else
{
/* ( fac_Q16 - ( 1 << 16 ) ) is in range of a 16-bit int */
Inlines.OpusAssert(fac_Q16 - (1 << 16) == Inlines.silk_SAT16(fac_Q16 - (1 << 16)));
/* Piece-wise linear interpolation of B and A */
for (nb = 0; nb < SilkConstants.TRANSITION_NB; nb++)
{
B_Q28[nb] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_B_Q28[ind + 1][nb],
Tables.silk_Transition_LP_B_Q28[ind + 1][nb] -
Tables.silk_Transition_LP_B_Q28[ind][nb],
fac_Q16 - ((int)1 << 16));
}
for (na = 0; na < SilkConstants.TRANSITION_NA; na++)
{
A_Q28[na] = Inlines.silk_SMLAWB(
Tables.silk_Transition_LP_A_Q28[ind + 1][na],
Tables.silk_Transition_LP_A_Q28[ind + 1][na] -
Tables.silk_Transition_LP_A_Q28[ind][na],
fac_Q16 - ((int)1 << 16));
}
}
}
else
{
Array.Copy(Tables.silk_Transition_LP_B_Q28[ind], 0, B_Q28, 0, SilkConstants.TRANSITION_NB);
Array.Copy(Tables.silk_Transition_LP_A_Q28[ind], 0, A_Q28, 0, SilkConstants.TRANSITION_NA);
}
}
else
{
Array.Copy(Tables.silk_Transition_LP_B_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, B_Q28, 0, SilkConstants.TRANSITION_NB);
Array.Copy(Tables.silk_Transition_LP_A_Q28[SilkConstants.TRANSITION_INT_NUM - 1], 0, A_Q28, 0, SilkConstants.TRANSITION_NA);
}
}
/// <summary>
/// LPC analysis filter
/// NB! State is kept internally and the
/// filter always starts with zero state
/// first d output samples are set to zero
/// </summary>
/// <param name="output">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="B">I MA prediction coefficients, Q12 [order]</param>
/// <param name="len">I Signal length</param>
/// <param name="d">I Filter order</param>
internal static void silk_LPC_analysis_filter(
short[] output,
int output_ptr,
short[] input,
int input_ptr,
short[] B,
int B_ptr,
int len,
int d)
{
int j;
short[] mem = new short[SilkConstants.SILK_MAX_ORDER_LPC];
short[] num = new short[SilkConstants.SILK_MAX_ORDER_LPC];
Inlines.OpusAssert(d >= 6);
Inlines.OpusAssert((d & 1) == 0);
Inlines.OpusAssert(d <= len);
Inlines.OpusAssert(d <= SilkConstants.SILK_MAX_ORDER_LPC);
for (j = 0; j < d; j++)
{
num[j] = (short)(0 - B[B_ptr + j]);
}
for (j = 0; j < d; j++)
{
mem[j] = input[input_ptr + d - j - 1];
}
#if UNSAFE
unsafe
{
fixed (short* pinput_base = input, poutput_base = output)
{
short* pinput = pinput_base + input_ptr + d;
short* poutput = poutput_base + output_ptr + d;
Kernels.celt_fir(pinput, num, poutput, len - d, d, mem);
}
}
#else
Kernels.celt_fir(input, input_ptr + d, num, output, output_ptr + d, len - d, d, mem);
#endif
for (j = output_ptr; j < output_ptr + d; j++)
{
output[j] = 0;
}
}
private const int QA = 24;
private static readonly int A_LIMIT = ((int)((0.99975f) * ((long)1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/;
/// <summary>
/// Compute inverse of LPC prediction gain, and
/// test if LPC coefficients are stable (all poles within unit circle)
/// </summary>
/// <param name="A_QA">Prediction coefficients, order [2][SILK_MAX_ORDER_LPC]</param>
/// <param name="order">Prediction order</param>
/// <returns>inverse prediction gain in energy domain, Q30</returns>
internal static int LPC_inverse_pred_gain_QA(
int[][] A_QA,
int order)
{
int k, n, mult2Q;
int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA;
int[] Aold_QA, Anew_QA;
Anew_QA = A_QA[order & 1];
invGain_Q30 = (int)1 << 30;
for (k = order - 1; k > 0; k--)
{
/* Check for stability */
if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA);
/* rc_mult1_Q30 range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); /* reduce A_LIMIT if fails */
Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30));
/* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */
mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30));
rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30);
/* Update inverse gain */
/* invGain_Q30 range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= (1 << 30));
/* Swap pointers */
Aold_QA = Anew_QA;
Anew_QA = A_QA[k & 1];
/* Update AR coefficient */
for (n = 0; n < k; n++)
{
tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31);
Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q);
}
}
/* Check for stability */
if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA);
/* Range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
/* Update inverse gain */
/* Range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= 1 << 30);
return invGain_Q30;
}
/// <summary>
/// For input in Q12 domain
/// </summary>
/// <param name="A_Q12">Prediction coefficients, Q12 [order]</param>
/// <param name="order">I Prediction order</param>
/// <returns>inverse prediction gain in energy domain, Q30</returns>
internal static int silk_LPC_inverse_pred_gain(short[] A_Q12, int order)
{
int k;
int[][] Atmp_QA = new int[2][];
Atmp_QA[0] = new int[order];
Atmp_QA[1] = new int[order];
int[] Anew_QA;
int DC_resp = 0;
Anew_QA = Atmp_QA[order & 1];
/* Increase Q domain of the AR coefficients */
for (k = 0; k < order; k++)
{
DC_resp += (int)A_Q12[k];
Anew_QA[k] = Inlines.silk_LSHIFT32((int)A_Q12[k], QA - 12);
}
/* If the DC is unstable, we don't even need to do the full calculations */
if (DC_resp >= 4096)
{
return 0;
}
return LPC_inverse_pred_gain_QA(Atmp_QA, order);
}
}
}
@@ -0,0 +1,184 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class FindLPC
{
/* Finds LPC vector from correlations, and converts to NLSF */
internal static void silk_find_LPC(
SilkChannelEncoder psEncC, /* I/O Encoder state */
short[] NLSF_Q15, /* O NLSFs */
short[] x, /* I Input signal */
int minInvGain_Q30 /* I Inverse of max prediction gain */
)
{
int k, subfr_length;
int[] a_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
int isInterpLower, shift;
int res_nrg0, res_nrg1;
int rshift0, rshift1;
BoxedValueInt scratch_box1 = new BoxedValueInt();
BoxedValueInt scratch_box2 = new BoxedValueInt();
/* Used only for LSF interpolation */
int[] a_tmp_Q16 = new int[SilkConstants.MAX_LPC_ORDER];
int res_nrg_interp, res_nrg, res_tmp_nrg;
int res_nrg_interp_Q, res_nrg_Q, res_tmp_nrg_Q;
short[] a_tmp_Q12 = new short[SilkConstants.MAX_LPC_ORDER];
short[] NLSF0_Q15 = new short[SilkConstants.MAX_LPC_ORDER];
subfr_length = psEncC.subfr_length + psEncC.predictLPCOrder;
/* Default: no interpolation */
psEncC.indices.NLSFInterpCoef_Q2 = 4;
/* Burg AR analysis for the full frame */
BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_Q16, x, 0, minInvGain_Q30, subfr_length, psEncC.nb_subfr, psEncC.predictLPCOrder);
res_nrg = scratch_box1.Val;
res_nrg_Q = scratch_box2.Val;
if (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
short[] LPC_res;
/* Optimal solution for last 10 ms */
BurgModified.silk_burg_modified(scratch_box1, scratch_box2, a_tmp_Q16, x, (2 * subfr_length), minInvGain_Q30, subfr_length, 2, psEncC.predictLPCOrder);
res_tmp_nrg = scratch_box1.Val;
res_tmp_nrg_Q = scratch_box2.Val;
/* subtract residual energy here, as that's easier than adding it to the */
/* residual energy of the first 10 ms in each iteration of the search below */
shift = res_tmp_nrg_Q - res_nrg_Q;
if (shift >= 0)
{
if (shift < 32)
{
res_nrg = res_nrg - Inlines.silk_RSHIFT(res_tmp_nrg, shift);
}
}
else {
Inlines.OpusAssert(shift > -32);
res_nrg = Inlines.silk_RSHIFT(res_nrg, -shift) - res_tmp_nrg;
res_nrg_Q = res_tmp_nrg_Q;
}
/* Convert to NLSFs */
NLSF.silk_A2NLSF(NLSF_Q15, a_tmp_Q16, psEncC.predictLPCOrder);
LPC_res = new short[2 * subfr_length];
/* Search over interpolation indices to find the one with lowest residual energy */
for (k = 3; k >= 0; k--)
{
/* Interpolate NLSFs for first half */
Inlines.silk_interpolate(NLSF0_Q15, psEncC.prev_NLSFq_Q15, NLSF_Q15, k, psEncC.predictLPCOrder);
/* Convert to LPC for residual energy evaluation */
NLSF.silk_NLSF2A(a_tmp_Q12, NLSF0_Q15, psEncC.predictLPCOrder);
/* Calculate residual energy with NLSF interpolation */
Filters.silk_LPC_analysis_filter(LPC_res, 0, x, 0, a_tmp_Q12, 0, 2 * subfr_length, psEncC.predictLPCOrder);
SumSqrShift.silk_sum_sqr_shift(out res_nrg0, out rshift0, LPC_res, psEncC.predictLPCOrder, subfr_length - psEncC.predictLPCOrder);
SumSqrShift.silk_sum_sqr_shift(out res_nrg1, out rshift1, LPC_res, psEncC.predictLPCOrder + subfr_length, subfr_length - psEncC.predictLPCOrder);
/* Add subframe energies from first half frame */
shift = rshift0 - rshift1;
if (shift >= 0)
{
res_nrg1 = Inlines.silk_RSHIFT(res_nrg1, shift);
res_nrg_interp_Q = -rshift0;
}
else {
res_nrg0 = Inlines.silk_RSHIFT(res_nrg0, -shift);
res_nrg_interp_Q = -rshift1;
}
res_nrg_interp = Inlines.silk_ADD32(res_nrg0, res_nrg1);
/* Compare with first half energy without NLSF interpolation, or best interpolated value so far */
shift = res_nrg_interp_Q - res_nrg_Q;
if (shift >= 0)
{
if (Inlines.silk_RSHIFT(res_nrg_interp, shift) < res_nrg)
{
isInterpLower = (true ? 1 : 0);
}
else {
isInterpLower = (false ? 1 : 0);
}
}
else {
if (-shift < 32)
{
if (res_nrg_interp < Inlines.silk_RSHIFT(res_nrg, -shift))
{
isInterpLower = (true ? 1 : 0);
}
else {
isInterpLower = (false ? 1 : 0);
}
}
else {
isInterpLower = (false ? 1 : 0);
}
}
/* Determine whether current interpolated NLSFs are best so far */
if (isInterpLower == (true ? 1 : 0))
{
/* Interpolation has lower residual energy */
res_nrg = res_nrg_interp;
res_nrg_Q = res_nrg_interp_Q;
psEncC.indices.NLSFInterpCoef_Q2 = (sbyte)k;
}
}
}
if (psEncC.indices.NLSFInterpCoef_Q2 == 4)
{
/* NLSF interpolation is currently inactive, calculate NLSFs from full frame AR coefficients */
NLSF.silk_A2NLSF(NLSF_Q15, a_Q16, psEncC.predictLPCOrder);
}
Inlines.OpusAssert(psEncC.indices.NLSFInterpCoef_Q2 == 4 || (psEncC.useInterpolatedNLSFs != 0 && psEncC.first_frame_after_reset == 0 && psEncC.nb_subfr == SilkConstants.MAX_NB_SUBFR));
}
}
}
@@ -0,0 +1,295 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class FindLTP
{
/* Head room for correlations */
private const int LTP_CORRS_HEAD_ROOM = 2;
/// <summary>
/// Finds linear prediction coeffecients and weights
/// </summary>
/// <param name="b_Q14"></param>
/// <param name="WLTP"></param>
/// <param name="LTPredCodGain_Q7"></param>
/// <param name="r_lpc"></param>
/// <param name="lag"></param>
/// <param name="Wght_Q15"></param>
/// <param name="subfr_length"></param>
/// <param name="nb_subfr"></param>
/// <param name="mem_offset"></param>
/// <param name="corr_rshifts"></param>
internal static void silk_find_LTP(
short[] b_Q14, /* O LTP coefs [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER] */
int[] WLTP, /* O Weight for LTP quantization [SilkConstants.MAX_NB_SUBFR * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER] */
BoxedValueInt LTPredCodGain_Q7, /* O LTP coding gain */
short[] r_lpc, /* I residual signal after LPC signal + state for first 10 ms */
int[] lag, /* I LTP lags [SilkConstants.MAX_NB_SUBFR] */
int[] Wght_Q15, /* I weights [SilkConstants.MAX_NB_SUBFR] */
int subfr_length, /* I subframe length */
int nb_subfr, /* I number of subframes */
int mem_offset, /* I number of samples in LTP memory */
int[] corr_rshifts /* O right shifts applied to correlations [SilkConstants.MAX_NB_SUBFR] */
)
{
int i, k, lshift;
int r_ptr;
int lag_ptr;
int b_Q14_ptr;
int regu;
int WLTP_ptr;
int[] b_Q16 = new int[SilkConstants.LTP_ORDER];
int[] delta_b_Q14 = new int[SilkConstants.LTP_ORDER];
int[] d_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
int[] nrg = new int[SilkConstants.MAX_NB_SUBFR];
int g_Q26;
int[] w = new int[SilkConstants.MAX_NB_SUBFR];
int WLTP_max, max_abs_d_Q14, max_w_bits;
int temp32, denom32;
int extra_shifts;
int rr_shifts, maxRshifts, maxRshifts_wxtra, LZs;
int LPC_res_nrg, LPC_LTP_res_nrg, div_Q16;
int[] Rr = new int[SilkConstants.LTP_ORDER];
int[] rr = new int[SilkConstants.MAX_NB_SUBFR];
int wd, m_Q12;
b_Q14_ptr = 0;
WLTP_ptr = 0;
r_ptr = mem_offset;
for (k = 0; k < nb_subfr; k++)
{
lag_ptr = r_ptr - (lag[k] + SilkConstants.LTP_ORDER / 2);
SumSqrShift.silk_sum_sqr_shift(out rr[k], out rr_shifts, r_lpc, r_ptr, subfr_length); /* rr[ k ] in Q( -rr_shifts ) */
/* Assure headroom */
LZs = Inlines.silk_CLZ32(rr[k]);
if (LZs < LTP_CORRS_HEAD_ROOM)
{
rr[k] = Inlines.silk_RSHIFT_ROUND(rr[k], LTP_CORRS_HEAD_ROOM - LZs);
rr_shifts += (LTP_CORRS_HEAD_ROOM - LZs);
}
corr_rshifts[k] = rr_shifts;
BoxedValueInt boxed_shifts = new BoxedValueInt(corr_rshifts[k]);
CorrelateMatrix.silk_corrMatrix(r_lpc, lag_ptr, subfr_length, SilkConstants.LTP_ORDER, LTP_CORRS_HEAD_ROOM, WLTP, WLTP_ptr, boxed_shifts); /* WLTP_ptr in Q( -corr_rshifts[ k ] ) */
corr_rshifts[k] = boxed_shifts.Val;
/* The correlation vector always has lower max abs value than rr and/or RR so head room is assured */
CorrelateMatrix.silk_corrVector(r_lpc, lag_ptr, r_lpc, r_ptr, subfr_length, SilkConstants.LTP_ORDER, Rr, corr_rshifts[k]); /* Rr_ptr in Q( -corr_rshifts[ k ] ) */
if (corr_rshifts[k] > rr_shifts)
{
rr[k] = Inlines.silk_RSHIFT(rr[k], corr_rshifts[k] - rr_shifts); /* rr[ k ] in Q( -corr_rshifts[ k ] ) */
}
Inlines.OpusAssert(rr[k] >= 0);
regu = 1;
regu = Inlines.silk_SMLAWB(regu, rr[k], ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, 0, 0, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
regu = Inlines.silk_SMLAWB(regu, Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER - 1, SilkConstants.LTP_ORDER), ((int)((TuningParameters.LTP_DAMPING / 3) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_DAMPING / 3, 16)*/);
RegularizeCorrelations.silk_regularize_correlations(WLTP, WLTP_ptr, rr, k, regu, SilkConstants.LTP_ORDER);
LinearAlgebra.silk_solve_LDL(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER, Rr, b_Q16); /* WLTP_ptr and Rr_ptr both in Q(-corr_rshifts[k]) */
/* Limit and store in Q14 */
silk_fit_LTP(b_Q16, b_Q14, b_Q14_ptr);
/* Calculate residual energy */
nrg[k] = ResidualEnergy.silk_residual_energy16_covar(b_Q14, b_Q14_ptr, WLTP, WLTP_ptr, Rr, rr[k], SilkConstants.LTP_ORDER, 14); /* nrg in Q( -corr_rshifts[ k ] ) */
/* temp = Wght[ k ] / ( nrg[ k ] * Wght[ k ] + 0.01f * subfr_length ); */
extra_shifts = Inlines.silk_min_int(corr_rshifts[k], LTP_CORRS_HEAD_ROOM);
denom32 = Inlines.silk_LSHIFT_SAT32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1 + extra_shifts) + /* Q( -corr_rshifts[ k ] + extra_shifts ) */
Inlines.silk_RSHIFT(Inlines.silk_SMULWB((int)subfr_length, 655), corr_rshifts[k] - extra_shifts); /* Q( -corr_rshifts[ k ] + extra_shifts ) */
denom32 = Inlines.silk_max(denom32, 1);
Inlines.OpusAssert(((long)Wght_Q15[k] << 16) < int.MaxValue); /* Wght always < 0.5 in Q0 */
temp32 = Inlines.silk_DIV32(Inlines.silk_LSHIFT((int)Wght_Q15[k], 16), denom32); /* Q( 15 + 16 + corr_rshifts[k] - extra_shifts ) */
temp32 = Inlines.silk_RSHIFT(temp32, 31 + corr_rshifts[k] - extra_shifts - 26); /* Q26 */
/* Limit temp such that the below scaling never wraps around */
WLTP_max = 0;
for (i = WLTP_ptr; i < WLTP_ptr + (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); i++)
{
WLTP_max = Inlines.silk_max(WLTP[i], WLTP_max);
}
lshift = Inlines.silk_CLZ32(WLTP_max) - 1 - 3; /* keep 3 bits free for vq_nearest_neighbor */
Inlines.OpusAssert(26 - 18 + lshift >= 0);
if (26 - 18 + lshift < 31)
{
temp32 = Inlines.silk_min_32(temp32, Inlines.silk_LSHIFT((int)1, 26 - 18 + lshift));
}
Inlines.silk_scale_vector32_Q26_lshift_18(WLTP, WLTP_ptr, temp32, SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER); /* WLTP_ptr in Q( 18 - corr_rshifts[ k ] ) */
w[k] = Inlines.MatrixGet(WLTP, WLTP_ptr, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER / 2, SilkConstants.LTP_ORDER); /* w in Q( 18 - corr_rshifts[ k ] ) */
Inlines.OpusAssert(w[k] >= 0);
r_ptr += subfr_length;
b_Q14_ptr += SilkConstants.LTP_ORDER;
WLTP_ptr += (SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER);
}
maxRshifts = 0;
for (k = 0; k < nb_subfr; k++)
{
maxRshifts = Inlines.silk_max_int(corr_rshifts[k], maxRshifts);
}
/* Compute LTP coding gain */
if (LTPredCodGain_Q7 != null)
{
LPC_LTP_res_nrg = 0;
LPC_res_nrg = 0;
Inlines.OpusAssert(LTP_CORRS_HEAD_ROOM >= 2); /* Check that no overflow will happen when adding */
for (k = 0; k < nb_subfr; k++)
{
LPC_res_nrg = Inlines.silk_ADD32(LPC_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(rr[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */
LPC_LTP_res_nrg = Inlines.silk_ADD32(LPC_LTP_res_nrg, Inlines.silk_RSHIFT(Inlines.silk_ADD32(Inlines.silk_SMULWB(nrg[k], Wght_Q15[k]), 1), 1 + (maxRshifts - corr_rshifts[k]))); /* Q( -maxRshifts ) */
}
LPC_LTP_res_nrg = Inlines.silk_max(LPC_LTP_res_nrg, 1); /* avoid division by zero */
div_Q16 = Inlines.silk_DIV32_varQ(LPC_res_nrg, LPC_LTP_res_nrg, 16);
LTPredCodGain_Q7.Val = (int)Inlines.silk_SMULBB(3, Inlines.silk_lin2log(div_Q16) - (16 << 7));
Inlines.OpusAssert(LTPredCodGain_Q7.Val == (int)Inlines.silk_SAT16(Inlines.silk_MUL(3, Inlines.silk_lin2log(div_Q16) - (16 << 7))));
}
/* smoothing */
/* d = sum( B, 1 ); */
b_Q14_ptr = 0;
for (k = 0; k < nb_subfr; k++)
{
d_Q14[k] = 0;
for (i = b_Q14_ptr; i < b_Q14_ptr + SilkConstants.LTP_ORDER; i++)
{
d_Q14[k] += b_Q14[i];
}
b_Q14_ptr += SilkConstants.LTP_ORDER;
}
/* m = ( w * d' ) / ( sum( w ) + 1e-3 ); */
/* Find maximum absolute value of d_Q14 and the bits used by w in Q0 */
max_abs_d_Q14 = 0;
max_w_bits = 0;
for (k = 0; k < nb_subfr; k++)
{
max_abs_d_Q14 = Inlines.silk_max_32(max_abs_d_Q14, Inlines.silk_abs(d_Q14[k]));
/* w[ k ] is in Q( 18 - corr_rshifts[ k ] ) */
/* Find bits needed in Q( 18 - maxRshifts ) */
max_w_bits = Inlines.silk_max_32(max_w_bits, 32 - Inlines.silk_CLZ32(w[k]) + corr_rshifts[k] - maxRshifts);
}
/* max_abs_d_Q14 = (5 << 15); worst case, i.e. SilkConstants.LTP_ORDER * -silk_int16_MIN */
Inlines.OpusAssert(max_abs_d_Q14 <= (5 << 15));
/* How many bits is needed for w*d' in Q( 18 - maxRshifts ) in the worst case, of all d_Q14's being equal to max_abs_d_Q14 */
extra_shifts = max_w_bits + 32 - Inlines.silk_CLZ32(max_abs_d_Q14) - 14;
/* Subtract what we got available; bits in output var plus maxRshifts */
extra_shifts -= (32 - 1 - 2 + maxRshifts); /* Keep sign bit free as well as 2 bits for accumulation */
extra_shifts = Inlines.silk_max_int(extra_shifts, 0);
maxRshifts_wxtra = maxRshifts + extra_shifts;
temp32 = Inlines.silk_RSHIFT(262, maxRshifts + extra_shifts) + 1; /* 1e-3f in Q( 18 - (maxRshifts + extra_shifts) ) */
wd = 0;
for (k = 0; k < nb_subfr; k++)
{
/* w has at least 2 bits of headroom so no overflow should happen */
temp32 = Inlines.silk_ADD32(temp32, Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k])); /* Q( 18 - maxRshifts_wxtra ) */
wd = Inlines.silk_ADD32(wd, Inlines.silk_LSHIFT(Inlines.silk_SMULWW(Inlines.silk_RSHIFT(w[k], maxRshifts_wxtra - corr_rshifts[k]), d_Q14[k]), 2)); /* Q( 18 - maxRshifts_wxtra ) */
}
m_Q12 = Inlines.silk_DIV32_varQ(wd, temp32, 12);
b_Q14_ptr = 0;
for (k = 0; k < nb_subfr; k++)
{
/* w[ k ] from Q( 18 - corr_rshifts[ k ] ) to Q( 16 ) */
if (2 - corr_rshifts[k] > 0)
{
temp32 = Inlines.silk_RSHIFT(w[k], 2 - corr_rshifts[k]);
}
else {
temp32 = Inlines.silk_LSHIFT_SAT32(w[k], corr_rshifts[k] - 2);
}
g_Q26 = Inlines.silk_MUL(
Inlines.silk_DIV32(
((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/,
Inlines.silk_RSHIFT(((int)((TuningParameters.LTP_SMOOTHING) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LTP_SMOOTHING, 26)*/, 10) + temp32), /* Q10 */
Inlines.silk_LSHIFT_SAT32(Inlines.silk_SUB_SAT32((int)m_Q12, Inlines.silk_RSHIFT(d_Q14[k], 2)), 4)); /* Q16 */
temp32 = 0;
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
delta_b_Q14[i] = Inlines.silk_max_16(b_Q14[b_Q14_ptr + i], 1638); /* 1638_Q14 = 0.1_Q0 */
temp32 += delta_b_Q14[i]; /* Q14 */
}
temp32 = Inlines.silk_DIV32(g_Q26, temp32); /* Q14 . Q12 */
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
b_Q14[b_Q14_ptr + i] = (short)(Inlines.silk_LIMIT_32((int)b_Q14[b_Q14_ptr + i] + Inlines.silk_SMULWB(Inlines.silk_LSHIFT_SAT32(temp32, 4), delta_b_Q14[i]), -16000, 28000));
}
b_Q14_ptr += SilkConstants.LTP_ORDER;
}
}
/// <summary>
///
/// </summary>
/// <param name="LTP_coefs_Q16">[SilkConstants.LTP_ORDER]</param>
/// <param name="LTP_coefs_Q14">[SilkConstants.LTP_ORDER]</param>
/// <param name="LTP_coefs_Q14_ptr"></param>
internal static void silk_fit_LTP(
int[] LTP_coefs_Q16,
short[] LTP_coefs_Q14,
int LTP_coefs_Q14_ptr)
{
int i;
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
LTP_coefs_Q14[LTP_coefs_Q14_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(LTP_coefs_Q16[i], 2));
}
}
}
}
@@ -0,0 +1,167 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class FindPitchLags
{
/* Find pitch lags */
internal static void silk_find_pitch_lags(
SilkChannelEncoder psEnc, /* I/O encoder state */
SilkEncoderControl psEncCtrl, /* I/O encoder control */
short[] res, /* O residual */
short[] x, /* I Speech signal */
int x_ptr
)
{
int buf_len, i, scale;
int thrhld_Q13, res_nrg;
int x_buf, x_buf_ptr;
short[] Wsig;
int Wsig_ptr;
int[] auto_corr = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER + 1];
short[] rc_Q15 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
int[] A_Q24 = new int[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
short[] A_Q12 = new short[SilkConstants.MAX_FIND_PITCH_LPC_ORDER];
/******************************************/
/* Set up buffer lengths etc based on Fs */
/******************************************/
buf_len = psEnc.la_pitch + psEnc.frame_length + psEnc.ltp_mem_length;
/* Safety check */
Inlines.OpusAssert(buf_len >= psEnc.pitch_LPC_win_length);
x_buf = x_ptr - psEnc.ltp_mem_length;
/*************************************/
/* Estimate LPC AR coefficients */
/*************************************/
/* Calculate windowed signal */
Wsig = new short[psEnc.pitch_LPC_win_length];
/* First LA_LTP samples */
x_buf_ptr = x_buf + buf_len - psEnc.pitch_LPC_win_length;
Wsig_ptr = 0;
ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 1, psEnc.la_pitch);
/* Middle un - windowed samples */
Wsig_ptr += psEnc.la_pitch;
x_buf_ptr += psEnc.la_pitch;
Array.Copy(x, x_buf_ptr, Wsig, Wsig_ptr, (psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1)));
/* Last LA_LTP samples */
Wsig_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1);
x_buf_ptr += psEnc.pitch_LPC_win_length - Inlines.silk_LSHIFT(psEnc.la_pitch, 1);
ApplySineWindow.silk_apply_sine_window(Wsig, Wsig_ptr, x, x_buf_ptr, 2, psEnc.la_pitch);
/* Calculate autocorrelation sequence */
BoxedValueInt boxed_scale = new BoxedValueInt();
Autocorrelation.silk_autocorr(auto_corr, boxed_scale, Wsig, psEnc.pitch_LPC_win_length, psEnc.pitchEstimationLPCOrder + 1);
scale = boxed_scale.Val;
/* Add white noise, as fraction of energy */
auto_corr[0] = Inlines.silk_SMLAWB(auto_corr[0], auto_corr[0], ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/) + 1;
/* Calculate the reflection coefficients using schur */
res_nrg = Schur.silk_schur(rc_Q15, auto_corr, psEnc.pitchEstimationLPCOrder);
/* Prediction gain */
psEncCtrl.predGain_Q16 = Inlines.silk_DIV32_varQ(auto_corr[0], Inlines.silk_max_int(res_nrg, 1), 16);
/* Convert reflection coefficients to prediction coefficients */
K2A.silk_k2a(A_Q24, rc_Q15, psEnc.pitchEstimationLPCOrder);
/* Convert From 32 bit Q24 to 16 bit Q12 coefs */
for (i = 0; i < psEnc.pitchEstimationLPCOrder; i++)
{
A_Q12[i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(A_Q24[i], 12));
}
/* Do BWE */
BWExpander.silk_bwexpander(A_Q12, psEnc.pitchEstimationLPCOrder, ((int)((TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_BANDWIDTH_EXPANSION, 16)*/);
/*****************************************/
/* LPC analysis filtering */
/*****************************************/
Filters.silk_LPC_analysis_filter(res, 0, x, x_buf, A_Q12, 0, buf_len, psEnc.pitchEstimationLPCOrder);
if (psEnc.indices.signalType != SilkConstants.TYPE_NO_VOICE_ACTIVITY && psEnc.first_frame_after_reset == 0)
{
/* Threshold for pitch estimator */
thrhld_Q13 = ((int)((0.6f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(0.6f, 13)*/;
thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.004f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.004f, 13)*/, psEnc.pitchEstimationLPCOrder);
thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (21)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 21)*/, psEnc.speech_activity_Q8);
thrhld_Q13 = Inlines.silk_SMLABB(thrhld_Q13, ((int)((-0.15f) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(-0.15f, 13)*/, Inlines.silk_RSHIFT(psEnc.prevSignalType, 1));
thrhld_Q13 = Inlines.silk_SMLAWB(thrhld_Q13, ((int)((-0.1f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(-0.1f, 14)*/, psEnc.input_tilt_Q15);
thrhld_Q13 = Inlines.silk_SAT16(thrhld_Q13);
/*****************************************/
/* Call pitch estimator */
/*****************************************/
BoxedValueShort boxed_lagIndex = new BoxedValueShort(psEnc.indices.lagIndex);
BoxedValueSbyte boxed_contourIndex = new BoxedValueSbyte(psEnc.indices.contourIndex);
BoxedValueInt boxed_LTPcorr = new BoxedValueInt(psEnc.LTPCorr_Q15);
if (PitchAnalysisCore.silk_pitch_analysis_core(res, psEncCtrl.pitchL, boxed_lagIndex, boxed_contourIndex,
boxed_LTPcorr, psEnc.prevLag, psEnc.pitchEstimationThreshold_Q16,
(int)thrhld_Q13, psEnc.fs_kHz, psEnc.pitchEstimationComplexity, psEnc.nb_subfr) == 0)
{
psEnc.indices.signalType = SilkConstants.TYPE_VOICED;
}
else {
psEnc.indices.signalType = SilkConstants.TYPE_UNVOICED;
}
psEnc.indices.lagIndex = boxed_lagIndex.Val;
psEnc.indices.contourIndex = boxed_contourIndex.Val;
psEnc.LTPCorr_Q15 = boxed_LTPcorr.Val;
}
else {
Arrays.MemSetInt(psEncCtrl.pitchL, 0, SilkConstants.MAX_NB_SUBFR);
psEnc.indices.lagIndex = 0;
psEnc.indices.contourIndex = 0;
psEnc.LTPCorr_Q15 = 0;
}
}
}
}
@@ -0,0 +1,171 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class FindPredCoefs
{
internal static void silk_find_pred_coefs(
SilkChannelEncoder psEnc, /* I/O encoder state */
SilkEncoderControl psEncCtrl, /* I/O encoder control */
short[] res_pitch, /* I Residual from pitch analysis */
short[] x, /* I Speech signal */
int x_ptr,
int condCoding /* I The type of conditional coding to use */
)
{
int i;
int[] invGains_Q16 = new int[SilkConstants.MAX_NB_SUBFR];
int[] local_gains = new int[SilkConstants.MAX_NB_SUBFR];
int[] Wght_Q15 = new int[SilkConstants.MAX_NB_SUBFR];
short[] NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER];
int x_ptr2;
int x_pre_ptr;
short[] LPC_in_pre;
int tmp, min_gain_Q16, minInvGain_Q30;
int[] LTP_corrs_rshift = new int[SilkConstants.MAX_NB_SUBFR];
/* weighting for weighted least squares */
min_gain_Q16 = int.MaxValue >> 6;
for (i = 0; i < psEnc.nb_subfr; i++)
{
min_gain_Q16 = Inlines.silk_min(min_gain_Q16, psEncCtrl.Gains_Q16[i]);
}
for (i = 0; i < psEnc.nb_subfr; i++)
{
/* Divide to Q16 */
Inlines.OpusAssert(psEncCtrl.Gains_Q16[i] > 0);
/* Invert and normalize gains, and ensure that maximum invGains_Q16 is within range of a 16 bit int */
invGains_Q16[i] = Inlines.silk_DIV32_varQ(min_gain_Q16, psEncCtrl.Gains_Q16[i], 16 - 2);
/* Ensure Wght_Q15 a minimum value 1 */
invGains_Q16[i] = Inlines.silk_max(invGains_Q16[i], 363);
/* Square the inverted gains */
Inlines.OpusAssert(invGains_Q16[i] == Inlines.silk_SAT16(invGains_Q16[i]));
tmp = Inlines.silk_SMULWB(invGains_Q16[i], invGains_Q16[i]);
Wght_Q15[i] = Inlines.silk_RSHIFT(tmp, 1);
/* Invert the inverted and normalized gains */
local_gains[i] = Inlines.silk_DIV32(((int)1 << 16), invGains_Q16[i]);
}
LPC_in_pre = new short[psEnc.nb_subfr * psEnc.predictLPCOrder + psEnc.frame_length];
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
int[] WLTP;
/**********/
/* VOICED */
/**********/
Inlines.OpusAssert(psEnc.ltp_mem_length - psEnc.predictLPCOrder >= psEncCtrl.pitchL[0] + SilkConstants.LTP_ORDER / 2);
WLTP = new int[psEnc.nb_subfr * SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER];
/* LTP analysis */
BoxedValueInt boxed_codgain = new BoxedValueInt(psEncCtrl.LTPredCodGain_Q7);
FindLTP.silk_find_LTP(psEncCtrl.LTPCoef_Q14, WLTP, boxed_codgain,
res_pitch, psEncCtrl.pitchL, Wght_Q15, psEnc.subfr_length,
psEnc.nb_subfr, psEnc.ltp_mem_length, LTP_corrs_rshift);
psEncCtrl.LTPredCodGain_Q7 = boxed_codgain.Val;
/* Quantize LTP gain parameters */
BoxedValueSbyte boxed_periodicity = new BoxedValueSbyte(psEnc.indices.PERIndex);
BoxedValueInt boxed_gain = new BoxedValueInt(psEnc.sum_log_gain_Q7);
QuantizeLTPGains.silk_quant_LTP_gains(psEncCtrl.LTPCoef_Q14, psEnc.indices.LTPIndex, boxed_periodicity,
boxed_gain, WLTP, psEnc.mu_LTP_Q9, psEnc.LTPQuantLowComplexity, psEnc.nb_subfr
);
psEnc.indices.PERIndex = boxed_periodicity.Val;
psEnc.sum_log_gain_Q7 = boxed_gain.Val;
/* Control LTP scaling */
LTPScaleControl.silk_LTP_scale_ctrl(psEnc, psEncCtrl, condCoding);
/* Create LTP residual */
LTPAnalysisFilter.silk_LTP_analysis_filter(LPC_in_pre, x, x_ptr - psEnc.predictLPCOrder, psEncCtrl.LTPCoef_Q14,
psEncCtrl.pitchL, invGains_Q16, psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder);
}
else {
/************/
/* UNVOICED */
/************/
/* Create signal with prepended subframes, scaled by inverse gains */
x_ptr2 = x_ptr - psEnc.predictLPCOrder;
x_pre_ptr = 0;
for (i = 0; i < psEnc.nb_subfr; i++)
{
Inlines.silk_scale_copy_vector16(LPC_in_pre, x_pre_ptr, x, x_ptr2, invGains_Q16[i],
psEnc.subfr_length + psEnc.predictLPCOrder);
x_pre_ptr += psEnc.subfr_length + psEnc.predictLPCOrder;
x_ptr2 += psEnc.subfr_length;
}
Arrays.MemSetShort(psEncCtrl.LTPCoef_Q14, 0, psEnc.nb_subfr * SilkConstants.LTP_ORDER);
psEncCtrl.LTPredCodGain_Q7 = 0;
psEnc.sum_log_gain_Q7 = 0;
}
/* Limit on total predictive coding gain */
if (psEnc.first_frame_after_reset != 0)
{
minInvGain_Q30 = ((int)((1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET) * ((long)1 << (30)) + 0.5))/*Inlines.SILK_CONST(1.0f / SilkConstants.MAX_PREDICTION_POWER_GAIN_AFTER_RESET, 30)*/;
}
else {
minInvGain_Q30 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(16 << 7, (int)psEncCtrl.LTPredCodGain_Q7, ((int)((1.0f / 3f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f / 3f, 16)*/)); /* Q16 */
minInvGain_Q30 = Inlines.silk_DIV32_varQ(minInvGain_Q30,
Inlines.silk_SMULWW(((int)((SilkConstants.MAX_PREDICTION_POWER_GAIN) * ((long)1 << (0)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MAX_PREDICTION_POWER_GAIN, 0)*/,
Inlines.silk_SMLAWB(((int)((0.25f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.25f, 18)*/, ((int)((0.75f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.75f, 18)*/, psEncCtrl.coding_quality_Q14)), 14);
}
/* LPC_in_pre contains the LTP-filtered input for voiced, and the unfiltered input for unvoiced */
FindLPC.silk_find_LPC(psEnc, NLSF_Q15, LPC_in_pre, minInvGain_Q30);
/* Quantize LSFs */
NLSF.silk_process_NLSFs(psEnc, psEncCtrl.PredCoef_Q12, NLSF_Q15, psEnc.prev_NLSFq_Q15);
/* Calculate residual energy using quantized LPC coefficients */
ResidualEnergy.silk_residual_energy(psEncCtrl.ResNrg, psEncCtrl.ResNrgQ, LPC_in_pre, psEncCtrl.PredCoef_Q12, local_gains,
psEnc.subfr_length, psEnc.nb_subfr, psEnc.predictLPCOrder);
/* Copy to prediction struct for use in next frame for interpolation */
Array.Copy(NLSF_Q15, psEnc.prev_NLSFq_Q15, SilkConstants.MAX_LPC_ORDER);
}
}
}
@@ -0,0 +1,187 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class GainQuantization
{
private static readonly int OFFSET = ((SilkConstants.MIN_QGAIN_DB * 128) / 6 + 16 * 128);
private static readonly int SCALE_Q16 = ((65536 * (SilkConstants.N_LEVELS_QGAIN - 1)) / (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6));
private static readonly int INV_SCALE_Q16 = ((65536 * (((SilkConstants.MAX_QGAIN_DB - SilkConstants.MIN_QGAIN_DB) * 128) / 6)) / (SilkConstants.N_LEVELS_QGAIN - 1));
/// <summary>
/// Gain scalar quantization with hysteresis, uniform on log scale
/// </summary>
/// <param name="ind">O gain indices [MAX_NB_SUBFR]</param>
/// <param name="gain_Q16">I/O gains (quantized out) [MAX_NB_SUBFR]</param>
/// <param name="prev_ind">I/O last index in previous frame. [Porting note] original implementation passed this as an int8*</param>
/// <param name="conditional">I first gain is delta coded if 1</param>
/// <param name="nb_subfr">I number of subframes</param>
internal static void silk_gains_quant(
sbyte[] ind,
int[] gain_Q16,
BoxedValueSbyte prev_ind,
int conditional,
int nb_subfr)
{
int k, double_step_size_threshold;
for (k = 0; k < nb_subfr; k++)
{
// Debug.WriteLine("2a 0x{0:x}", (uint)gain_Q16[k]);
/* Convert to log scale, scale, floor() */
ind[k] = (sbyte)(Inlines.silk_SMULWB(SCALE_Q16, Inlines.silk_lin2log(gain_Q16[k]) - OFFSET));
/* Round towards previous quantized gain (hysteresis) */
if (ind[k] < prev_ind.Val)
{
ind[k]++;
}
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], 0, SilkConstants.N_LEVELS_QGAIN - 1));
/* Compute delta indices and limit */
if (k == 0 && conditional == 0)
{
/* Full index */
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], prev_ind.Val + SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.N_LEVELS_QGAIN - 1));
prev_ind.Val = ind[k];
}
else
{
/* Delta index */
ind[k] = (sbyte)(ind[k] - prev_ind.Val);
/* Double the quantization step size for large gain increases, so that the max gain level can be reached */
double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val;
if (ind[k] > double_step_size_threshold)
{
ind[k] = (sbyte)(double_step_size_threshold + Inlines.silk_RSHIFT(ind[k] - double_step_size_threshold + 1, 1));
}
ind[k] = (sbyte)(Inlines.silk_LIMIT_int(ind[k], SilkConstants.MIN_DELTA_GAIN_QUANT, SilkConstants.MAX_DELTA_GAIN_QUANT));
/* Accumulate deltas */
if (ind[k] > double_step_size_threshold)
{
prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind[k], 1) - double_step_size_threshold);
}
else
{
prev_ind.Val += ind[k];
}
/* Shift to make non-negative */
ind[k] -= SilkConstants.MIN_DELTA_GAIN_QUANT;
// Debug.WriteLine("2b 0x{0:x}", (uint)ind[k]);
}
/* Scale and convert to linear scale */
gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */
}
}
/// <summary>
/// Gains scalar dequantization, uniform on log scale
/// </summary>
/// <param name="gain_Q16">O quantized gains [MAX_NB_SUBFR]</param>
/// <param name="ind">I gain indices [MAX_NB_SUBFR]</param>
/// <param name="prev_ind">I/O last index in previous frame [Porting note] original implementation passed this as an int8*</param>
/// <param name="conditional">I first gain is delta coded if 1</param>
/// <param name="nb_subfr">I number of subframes</param>
internal static void silk_gains_dequant(
int[] gain_Q16,
sbyte[] ind,
BoxedValueSbyte prev_ind,
int conditional,
int nb_subfr)
{
int k, ind_tmp, double_step_size_threshold;
for (k = 0; k < nb_subfr; k++)
{
if (k == 0 && conditional == 0)
{
/* Gain index is not allowed to go down more than 16 steps (~21.8 dB) */
prev_ind.Val = (sbyte)(Inlines.silk_max_int(ind[k], prev_ind.Val - 16));
}
else
{
/* Delta index */
ind_tmp = ind[k] + SilkConstants.MIN_DELTA_GAIN_QUANT;
/* Accumulate deltas */
double_step_size_threshold = 2 * SilkConstants.MAX_DELTA_GAIN_QUANT - SilkConstants.N_LEVELS_QGAIN + prev_ind.Val;
if (ind_tmp > double_step_size_threshold)
{
prev_ind.Val += (sbyte)(Inlines.silk_LSHIFT(ind_tmp, 1) - double_step_size_threshold);
}
else
{
prev_ind.Val += (sbyte)(ind_tmp);
}
}
prev_ind.Val = (sbyte)(Inlines.silk_LIMIT_int(prev_ind.Val, 0, SilkConstants.N_LEVELS_QGAIN - 1));
/* Scale and convert to linear scale */
gain_Q16[k] = Inlines.silk_log2lin(Inlines.silk_min_32(Inlines.silk_SMULWB(INV_SCALE_Q16, prev_ind.Val) + OFFSET, 3967)); /* 3967 = 31 in Q7 */
}
}
/// <summary>
/// Compute unique identifier of gain indices vector
/// </summary>
/// <param name="ind">I gain indices [MAX_NB_SUBFR]</param>
/// <param name="nb_subfr">I number of subframes</param>
/// <returns>unique identifier of gains</returns>
internal static int silk_gains_ID(sbyte[] ind, int nb_subfr)
{
int k;
int gainsID;
gainsID = 0;
for (k = 0; k < nb_subfr; k++)
{
gainsID = Inlines.silk_ADD_LSHIFT32(ind[k], gainsID, 8);
}
return gainsID;
}
}
}
@@ -0,0 +1,90 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class HPVariableCutoff
{
/// <summary>
/// High-pass filter with cutoff frequency adaptation based on pitch lag statistics
/// </summary>
/// <param name="state_Fxx">I/O Encoder states</param>
internal static void silk_HP_variable_cutoff(SilkChannelEncoder[] state_Fxx)
{
int quality_Q15;
int pitch_freq_Hz_Q16, pitch_freq_log_Q7, delta_freq_Q7;
SilkChannelEncoder psEncC1 = state_Fxx[0];
/* Adaptive cutoff frequency: estimate low end of pitch frequency range */
if (psEncC1.prevSignalType == SilkConstants.TYPE_VOICED)
{
/* difference, in log domain */
pitch_freq_Hz_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(Inlines.silk_MUL(psEncC1.fs_kHz, 1000), 16), psEncC1.prevLag);
pitch_freq_log_Q7 = Inlines.silk_lin2log(pitch_freq_Hz_Q16) - (16 << 7);
/* adjustment based on quality */
quality_Q15 = psEncC1.input_quality_bands_Q15[0];
pitch_freq_log_Q7 = Inlines.silk_SMLAWB(pitch_freq_log_Q7, Inlines.silk_SMULWB(Inlines.silk_LSHIFT(-quality_Q15, 2), quality_Q15),
pitch_freq_log_Q7 - (Inlines.silk_lin2log(((int)((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7)));
/* delta_freq = pitch_freq_log - psEnc.variable_HP_smth1; */
delta_freq_Q7 = pitch_freq_log_Q7 - Inlines.silk_RSHIFT(psEncC1.variable_HP_smth1_Q15, 8);
if (delta_freq_Q7 < 0)
{
/* less smoothing for decreasing pitch frequency, to track something close to the minimum */
delta_freq_Q7 = Inlines.silk_MUL(delta_freq_Q7, 3);
}
/* limit delta, to reduce impact of outliers in pitch estimation */
delta_freq_Q7 = Inlines.silk_LIMIT_32(
delta_freq_Q7,
0 - ((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/,
((int)((TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MAX_DELTA_FREQ, 7)*/);
/* update smoother */
psEncC1.variable_HP_smth1_Q15 = Inlines.silk_SMLAWB(psEncC1.variable_HP_smth1_Q15,
Inlines.silk_SMULBB(psEncC1.speech_activity_Q8, delta_freq_Q7), ((int)((TuningParameters.VARIABLE_HP_SMTH_COEF1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_SMTH_COEF1, 16)*/);
/* limit frequency range */
psEncC1.variable_HP_smth1_Q15 = Inlines.silk_LIMIT_32(psEncC1.variable_HP_smth1_Q15,
Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ), 8),
Inlines.silk_LSHIFT(Inlines.silk_lin2log(TuningParameters.VARIABLE_HP_MAX_CUTOFF_HZ), 8));
}
}
}
}
@@ -0,0 +1,92 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class K2A
{
/* Step up function, converts reflection coefficients to prediction coefficients */
internal static void silk_k2a(
int[] A_Q24, /* O Prediction coefficients [order] Q24 */
short[] rc_Q15, /* I Reflection coefficients [order] Q15 */
int order /* I Prediction order */
)
{
int k, n;
int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC];
for (k = 0; k < order; k++)
{
for (n = 0; n < k; n++)
{
Atmp[n] = A_Q24[n];
}
for (n = 0; n < k; n++)
{
A_Q24[n] = Inlines.silk_SMLAWB(A_Q24[n], Inlines.silk_LSHIFT(Atmp[k - n - 1], 1), rc_Q15[k]);
}
A_Q24[k] = 0 - Inlines.silk_LSHIFT((int)rc_Q15[k], 9);
}
}
/* Step up function, converts reflection coefficients to prediction coefficients */
internal static void silk_k2a_Q16(
int[] A_Q24, /* O Prediction coefficients [order] Q24 */
int[] rc_Q16, /* I Reflection coefficients [order] Q16 */
int order /* I Prediction order */
)
{
int k, n;
int[] Atmp = new int[SilkConstants.SILK_MAX_ORDER_LPC];
for (k = 0; k < order; k++)
{
for (n = 0; n < k; n++)
{
Atmp[n] = A_Q24[n];
}
for (n = 0; n < k; n++)
{
A_Q24[n] = Inlines.silk_SMLAWW(A_Q24[n], Atmp[k - n - 1], rc_Q16[k]);
}
A_Q24[k] = 0 - Inlines.silk_LSHIFT(rc_Q16[k], 8);
}
}
}
}
@@ -0,0 +1,169 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class LPCInversePredGain
{
private const float RC_THRESHOLD = 0.9999f;
private const int QA = 24;
private static readonly int A_LIMIT = ((int)((0.99975f) * ((long)1 << (QA)) + 0.5))/*Inlines.SILK_CONST(0.99975f, QA)*/;
/* Compute inverse of LPC prediction gain, and */
/* test if LPC coefficients are stable (all poles within unit circle) */
internal static int LPC_inverse_pred_gain_QA( /* O Returns inverse prediction gain in energy domain, Q30 */
int[][] A_QA, /* I Prediction coefficients [ 2 ][SILK_MAX_ORDER_LPC] */
int order /* I Prediction order */
)
{
int k, n, mult2Q;
int invGain_Q30, rc_Q31, rc_mult1_Q30, rc_mult2, tmp_QA;
int[] Aold_QA;
int[] Anew_QA;
Anew_QA = A_QA[order & 1];
invGain_Q30 = (int)1 << 30;
for (k = order - 1; k > 0; k--)
{
/* Check for stability */
if ((Anew_QA[k] > A_LIMIT) || (Anew_QA[k] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[k], 31 - QA);
/* rc_mult1_Q30 range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
Inlines.OpusAssert(rc_mult1_Q30 > (1 << 15)); /* reduce A_LIMIT if fails */
Inlines.OpusAssert(rc_mult1_Q30 <= (1 << 30));
/* rc_mult2 range: [ 2^30 : silk_int32_MAX ] */
mult2Q = 32 - Inlines.silk_CLZ32(Inlines.silk_abs(rc_mult1_Q30));
rc_mult2 = Inlines.silk_INVERSE32_varQ(rc_mult1_Q30, mult2Q + 30);
/* Update inverse gain */
/* invGain_Q30 range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= (1 << 30));
/* Swap pointers */
Aold_QA = Anew_QA;
Anew_QA = A_QA[k & 1];
/* Update AR coefficient */
for (n = 0; n < k; n++)
{
tmp_QA = Aold_QA[n] - Inlines.MUL32_FRAC_Q(Aold_QA[k - n - 1], rc_Q31, 31);
Anew_QA[n] = Inlines.MUL32_FRAC_Q(tmp_QA, rc_mult2, mult2Q);
}
}
/* Check for stability */
if ((Anew_QA[0] > A_LIMIT) || (Anew_QA[0] < -A_LIMIT))
{
return 0;
}
/* Set RC equal to negated AR coef */
rc_Q31 = 0 - Inlines.silk_LSHIFT(Anew_QA[0], 31 - QA);
/* Range: [ 1 : 2^30 ] */
rc_mult1_Q30 = ((int)1 << 30) - Inlines.silk_SMMUL(rc_Q31, rc_Q31);
/* Update inverse gain */
/* Range: [ 0 : 2^30 ] */
invGain_Q30 = Inlines.silk_LSHIFT(Inlines.silk_SMMUL(invGain_Q30, rc_mult1_Q30), 2);
Inlines.OpusAssert(invGain_Q30 >= 0);
Inlines.OpusAssert(invGain_Q30 <= 1 << 30);
return invGain_Q30;
}
/* For input in Q12 domain */
internal static int silk_LPC_inverse_pred_gain( /* O Returns inverse prediction gain in energy domain, Q30 */
short[] A_Q12, /* I Prediction coefficients, Q12 [order] */
int order /* I Prediction order */
)
{
int k;
int[][] Atmp_QA = Arrays.InitTwoDimensionalArray<int>(2, SilkConstants.SILK_MAX_ORDER_LPC);
int[] Anew_QA;
int DC_resp = 0;
Anew_QA = Atmp_QA[order & 1];
/* Increase Q domain of the AR coefficients */
for (k = 0; k < order; k++)
{
DC_resp += (int)A_Q12[k];
Anew_QA[k] = Inlines.silk_LSHIFT32((int)A_Q12[k], QA - 12);
}
/* If the DC is unstable, we don't even need to do the full calculations */
if (DC_resp >= 4096)
{
return 0;
}
return LPC_inverse_pred_gain_QA(Atmp_QA, order);
}
internal static int silk_LPC_inverse_pred_gain_Q24( /* O Returns inverse prediction gain in energy domain, Q30 */
int[] A_Q24, /* I Prediction coefficients [order] */
int order /* I Prediction order */
)
{
int k;
int[][] Atmp_QA = Arrays.InitTwoDimensionalArray<int>(2, SilkConstants.SILK_MAX_ORDER_LPC);
int[] Anew_QA;
Anew_QA = Atmp_QA[order & 1];
/* Increase Q domain of the AR coefficients */
for (k = 0; k < order; k++)
{
Anew_QA[k] = Inlines.silk_RSHIFT32(A_Q24[k], 24 - QA);
}
return LPC_inverse_pred_gain_QA(Atmp_QA, order);
}
}
}
@@ -0,0 +1,103 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class LTPAnalysisFilter
{
internal static void silk_LTP_analysis_filter(
short[] LTP_res, /* O LTP residual signal of length SilkConstants.MAX_NB_SUBFR * ( pre_length + subfr_length ) */
short[] x, /* I Pointer to input signal with at least max( pitchL ) preceding samples */
int x_ptr,
short[] LTPCoef_Q14,/* I LTP_ORDER LTP coefficients for each MAX_NB_SUBFR subframe [SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR] */
int[] pitchL, /* I Pitch lag, one for each subframe [SilkConstants.MAX_NB_SUBFR] */
int[] invGains_Q16, /* I Inverse quantization gains, one for each subframe [SilkConstants.MAX_NB_SUBFR] */
int subfr_length, /* I Length of each subframe */
int nb_subfr, /* I Number of subframes */
int pre_length /* I Length of the preceding samples starting at &x[0] for each subframe */
)
{
int x_ptr2, x_lag_ptr;
short[] Btmp_Q14 = new short[SilkConstants.LTP_ORDER];
int LTP_res_ptr;
int k, i;
int LTP_est;
x_ptr2 = x_ptr;
LTP_res_ptr = 0;
for (k = 0; k < nb_subfr; k++)
{
x_lag_ptr = x_ptr2 - pitchL[k];
Btmp_Q14[0] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER];
Btmp_Q14[1] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 1];
Btmp_Q14[2] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 2];
Btmp_Q14[3] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 3];
Btmp_Q14[4] = LTPCoef_Q14[k * SilkConstants.LTP_ORDER + 4];
/* LTP analysis FIR filter */
for (i = 0; i < subfr_length + pre_length; i++)
{
int LTP_res_ptri = LTP_res_ptr + i;
LTP_res[LTP_res_ptri] = x[x_ptr2 + i];
/* Long-term prediction */
LTP_est = Inlines.silk_SMULBB(x[x_lag_ptr + SilkConstants.LTP_ORDER / 2], Btmp_Q14[0]);
LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr + 1], Btmp_Q14[1]);
LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr], Btmp_Q14[2]);
LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 1], Btmp_Q14[3]);
LTP_est = Inlines.silk_SMLABB_ovflw(LTP_est, x[x_lag_ptr - 2], Btmp_Q14[4]);
LTP_est = Inlines.silk_RSHIFT_ROUND(LTP_est, 14); /* round and . Q0*/
/* Subtract long-term prediction */
LTP_res[LTP_res_ptri] = (short)Inlines.silk_SAT16((int)x[x_ptr2 + i] - LTP_est);
/* Scale residual */
LTP_res[LTP_res_ptri] = (short)(Inlines.silk_SMULWB(invGains_Q16[k], LTP_res[LTP_res_ptri]));
x_lag_ptr++;
}
/* Update pointers */
LTP_res_ptr += subfr_length + pre_length;
x_ptr2 += subfr_length;
}
}
}
}
@@ -0,0 +1,66 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class LTPScaleControl
{
/* Calculation of LTP state scaling */
internal static void silk_LTP_scale_ctrl(
SilkChannelEncoder psEnc, /* I/O encoder state */
SilkEncoderControl psEncCtrl, /* I/O encoder control */
int condCoding /* I The type of conditional coding to use */
)
{
int round_loss;
if (condCoding == SilkConstants.CODE_INDEPENDENTLY)
{
/* Only scale if first frame in packet */
round_loss = psEnc.PacketLoss_perc + psEnc.nFramesPerPacket;
psEnc.indices.LTP_scaleIndex = (sbyte)Inlines.silk_LIMIT(
Inlines.silk_SMULWB(Inlines.silk_SMULBB(round_loss, psEncCtrl.LTPredCodGain_Q7), ((int)((0.1f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(0.1f, 9)*/), 0, 2);
}
else {
/* Default is minimum scaling */
psEnc.indices.LTP_scaleIndex = 0;
}
psEncCtrl.LTP_scale_Q14 = Tables.silk_LTPScales_table_Q14[psEnc.indices.LTP_scaleIndex];
}
}
}
@@ -0,0 +1,245 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class LinearAlgebra
{
/* Solves Ax = b, assuming A is symmetric */
internal static void silk_solve_LDL(
int[] A, /* I Pointer to symetric square matrix A */
int A_ptr,
int M, /* I Size of matrix */
int[] b, /* I Pointer to b vector */
int[] x_Q16 /* O Pointer to x solution vector */
)
{
Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE);
int[] L_Q16 = new int[M * M];
int[] Y = new int[SilkConstants.MAX_MATRIX_SIZE];
// [Porting note] This is an interleaved array. Formerly it was an array of data structures laid out thus:
//private struct inv_D_t
//{
// int Q36_part;
// int Q48_part;
//}
int[] inv_D = new int[SilkConstants.MAX_MATRIX_SIZE * 2];
/***************************************************
Factorize A by LDL such that A = L*D*L',
where L is lower triangular with ones on diagonal
****************************************************/
silk_LDL_factorize(A, A_ptr, M, L_Q16, inv_D);
/****************************************************
* substitute D*L'*x = Y. ie:
L*D*L'*x = b => L*Y = b <=> Y = inv(L)*b
******************************************************/
silk_LS_SolveFirst(L_Q16, M, b, Y);
/****************************************************
D*L'*x = Y <=> L'*x = inv(D)*Y, because D is
diagonal just multiply with 1/d_i
****************************************************/
silk_LS_divide_Q16(Y, inv_D, M);
/****************************************************
x = inv(L') * inv(D) * Y
*****************************************************/
silk_LS_SolveLast(L_Q16, M, Y, x_Q16);
}
/* Factorize square matrix A into LDL form */
private static void silk_LDL_factorize(
int[] A, /* I/O Pointer to Symetric Square Matrix */
int A_ptr,
int M, /* I Size of Matrix */
int[] L_Q16, /* I/O Pointer to Square Upper triangular Matrix */
int[] inv_D /* I/O Pointer to vector holding inverted diagonal elements of D */
)
{
int i, j, k, status, loop_count;
int[] scratch1;
int scratch1_ptr;
int[] scratch2;
int scratch2_ptr;
int diag_min_value, tmp_32, err;
int[] v_Q0 = new int[M]; /*SilkConstants.MAX_MATRIX_SIZE*/
int[] D_Q0 = new int[M]; /*SilkConstants.MAX_MATRIX_SIZE*/
int one_div_diag_Q36, one_div_diag_Q40, one_div_diag_Q48;
Inlines.OpusAssert(M <= SilkConstants.MAX_MATRIX_SIZE);
status = 1;
diag_min_value = Inlines.silk_max_32(Inlines.silk_SMMUL(Inlines.silk_ADD_SAT32(A[A_ptr], A[A_ptr + Inlines.silk_SMULBB(M, M) - 1]), ((int)((TuningParameters.FIND_LTP_COND_FAC) * ((long)1 << (31)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_LTP_COND_FAC, 31)*/), 1 << 9);
for (loop_count = 0; loop_count < M && status == 1; loop_count++)
{
status = 0;
for (j = 0; j < M; j++)
{
scratch1 = L_Q16;
scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M);
tmp_32 = 0;
for (i = 0; i < j; i++)
{
v_Q0[i] = Inlines.silk_SMULWW(D_Q0[i], scratch1[scratch1_ptr + i]); /* Q0 */
tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[i], scratch1[scratch1_ptr + i]); /* Q0 */
}
tmp_32 = Inlines.silk_SUB32(Inlines.MatrixGet(A, A_ptr, j, j, M), tmp_32);
if (tmp_32 < diag_min_value)
{
tmp_32 = Inlines.silk_SUB32(Inlines.silk_SMULBB(loop_count + 1, diag_min_value), tmp_32);
/* Matrix not positive semi-definite, or ill conditioned */
for (i = 0; i < M; i++)
{
Inlines.MatrixSet(A, A_ptr, i, i, M, Inlines.silk_ADD32(Inlines.MatrixGet(A, A_ptr, i, i, M), tmp_32));
}
status = 1;
break;
}
D_Q0[j] = tmp_32; /* always < max(Correlation) */
/* two-step division */
one_div_diag_Q36 = Inlines.silk_INVERSE32_varQ(tmp_32, 36); /* Q36 */
one_div_diag_Q40 = Inlines.silk_LSHIFT(one_div_diag_Q36, 4); /* Q40 */
err = Inlines.silk_SUB32((int)1 << 24, Inlines.silk_SMULWW(tmp_32, one_div_diag_Q40)); /* Q24 */
one_div_diag_Q48 = Inlines.silk_SMULWW(err, one_div_diag_Q40); /* Q48 */
/* Save 1/Ds */
inv_D[(j * 2) + 0] = one_div_diag_Q36;
inv_D[(j * 2) + 1] = one_div_diag_Q48;
Inlines.MatrixSet(L_Q16, j, j, M, 65536); /* 1.0 in Q16 */
scratch1 = A;
scratch1_ptr = Inlines.MatrixGetPointer(j, 0, M) + A_ptr;
scratch2 = L_Q16;
scratch2_ptr = Inlines.MatrixGetPointer(j + 1, 0, M);
for (i = j + 1; i < M; i++)
{
tmp_32 = 0;
for (k = 0; k < j; k++)
{
tmp_32 = Inlines.silk_SMLAWW(tmp_32, v_Q0[k], scratch2[scratch2_ptr + k]); /* Q0 */
}
tmp_32 = Inlines.silk_SUB32(scratch1[scratch1_ptr + i], tmp_32); /* always < max(Correlation) */
/* tmp_32 / D_Q0[j] : Divide to Q16 */
Inlines.MatrixSet(L_Q16, i, j, M, Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48),
Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4)));
/* go to next column */
scratch2_ptr += M;
}
}
}
Inlines.OpusAssert(status == 0);
}
private static void silk_LS_divide_Q16(
int[] T, /* I/O Numenator vector */
int[] inv_D, /* I 1 / D vector */
int M /* I dimension */
)
{
int i;
int tmp_32;
int one_div_diag_Q36, one_div_diag_Q48;
for (i = 0; i < M; i++)
{
one_div_diag_Q36 = inv_D[(i * 2) + 0];
one_div_diag_Q48 = inv_D[(i * 2) + 1];
tmp_32 = T[i];
T[i] = Inlines.silk_ADD32(Inlines.silk_SMMUL(tmp_32, one_div_diag_Q48), Inlines.silk_RSHIFT(Inlines.silk_SMULWW(tmp_32, one_div_diag_Q36), 4));
}
}
/* Solve Lx = b, when L is lower triangular and has ones on the diagonal */
private static void silk_LS_SolveFirst(
int[] L_Q16, /* I Pointer to Lower Triangular Matrix */
int M, /* I Dim of Matrix equation */
int[] b, /* I b Vector */
int[] x_Q16 /* O x Vector */
)
{
int i, j;
int ptr32;
int tmp_32;
for (i = 0; i < M; i++)
{
ptr32 = Inlines.MatrixGetPointer(i, 0, M);
tmp_32 = 0;
for (j = 0; j < i; j++)
{
tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + j], x_Q16[j]);
}
x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32);
}
}
/* Solve L^t*x = b, where L is lower triangular with ones on the diagonal */
private static void silk_LS_SolveLast(
int[] L_Q16, /* I Pointer to Lower Triangular Matrix */
int M, /* I Dim of Matrix equation */
int[] b, /* I b Vector */
int[] x_Q16 /* O x Vector */
)
{
int i, j;
int ptr32;
int tmp_32;
for (i = M - 1; i >= 0; i--)
{
ptr32 = Inlines.MatrixGetPointer(0, i, M);
tmp_32 = 0;
for (j = M - 1; j > i; j--)
{
tmp_32 = Inlines.silk_SMLAWW(tmp_32, L_Q16[ptr32 + Inlines.silk_SMULBB(j, M)], x_Q16[j]);
}
x_Q16[i] = Inlines.silk_SUB32(b[i], tmp_32);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,498 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class NoiseShapeAnalysis
{
/* Compute gain to make warped filter coefficients have a zero mean log frequency response on a */
/* non-warped frequency scale. (So that it can be implemented with a minimum-phase monic filter.) */
/* Note: A monic filter is one with the first coefficient equal to 1.0. In Silk we omit the first */
/* coefficient in an array of coefficients, for monic filters. */
internal static int warped_gain( /* gain in Q16*/
int[] coefs_Q24,
int lambda_Q16,
int order
)
{
int i;
int gain_Q24;
lambda_Q16 = -lambda_Q16;
gain_Q24 = coefs_Q24[order - 1];
for (i = order - 2; i >= 0; i--)
{
gain_Q24 = Inlines.silk_SMLAWB(coefs_Q24[i], gain_Q24, lambda_Q16);
}
gain_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, gain_Q24, -lambda_Q16);
return Inlines.silk_INVERSE32_varQ(gain_Q24, 40);
}
/* Convert warped filter coefficients to monic pseudo-warped coefficients and limit maximum */
/* amplitude of monic warped coefficients by using bandwidth expansion on the true coefficients */
internal static void limit_warped_coefs(
int[] coefs_syn_Q24,
int[] coefs_ana_Q24,
int lambda_Q16,
int limit_Q24,
int order
)
{
int i, iter, ind = 0;
int tmp, maxabs_Q24, chirp_Q16, gain_syn_Q16, gain_ana_Q16;
int nom_Q16, den_Q24;
/* Convert to monic coefficients */
lambda_Q16 = -lambda_Q16;
for (i = order - 1; i > 0; i--)
{
coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16);
coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16);
}
lambda_Q16 = -lambda_Q16;
nom_Q16 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int)lambda_Q16, lambda_Q16);
den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16);
gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24);
den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16);
gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24);
for (i = 0; i < order; i++)
{
coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]);
coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]);
}
for (iter = 0; iter < 10; iter++)
{
/* Find maximum absolute value */
maxabs_Q24 = -1;
for (i = 0; i < order; i++)
{
tmp = Inlines.silk_max(Inlines.silk_abs_int32(coefs_syn_Q24[i]), Inlines.silk_abs_int32(coefs_ana_Q24[i]));
if (tmp > maxabs_Q24)
{
maxabs_Q24 = tmp;
ind = i;
}
}
if (maxabs_Q24 <= limit_Q24)
{
/* Coefficients are within range - done */
return;
}
/* Convert back to true warped coefficients */
for (i = 1; i < order; i++)
{
coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16);
coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16);
}
gain_syn_Q16 = Inlines.silk_INVERSE32_varQ(gain_syn_Q16, 32);
gain_ana_Q16 = Inlines.silk_INVERSE32_varQ(gain_ana_Q16, 32);
for (i = 0; i < order; i++)
{
coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]);
coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]);
}
/* Apply bandwidth expansion */
chirp_Q16 = ((int)((0.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.99f, 16)*/ - Inlines.silk_DIV32_varQ(
Inlines.silk_SMULWB(maxabs_Q24 - limit_Q24, Inlines.silk_SMLABB(((int)((0.8f) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.8f, 10)*/, ((int)((0.1f) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(0.1f, 10)*/, iter)),
Inlines.silk_MUL(maxabs_Q24, ind + 1), 22);
BWExpander.silk_bwexpander_32(coefs_syn_Q24, order, chirp_Q16);
BWExpander.silk_bwexpander_32(coefs_ana_Q24, order, chirp_Q16);
/* Convert to monic warped coefficients */
lambda_Q16 = -lambda_Q16;
for (i = order - 1; i > 0; i--)
{
coefs_syn_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_syn_Q24[i - 1], coefs_syn_Q24[i], lambda_Q16);
coefs_ana_Q24[i - 1] = Inlines.silk_SMLAWB(coefs_ana_Q24[i - 1], coefs_ana_Q24[i], lambda_Q16);
}
lambda_Q16 = -lambda_Q16;
nom_Q16 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, -(int)lambda_Q16, lambda_Q16);
den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_syn_Q24[0], lambda_Q16);
gain_syn_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24);
den_Q24 = Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(1.0f, 24)*/, coefs_ana_Q24[0], lambda_Q16);
gain_ana_Q16 = Inlines.silk_DIV32_varQ(nom_Q16, den_Q24, 24);
for (i = 0; i < order; i++)
{
coefs_syn_Q24[i] = Inlines.silk_SMULWW(gain_syn_Q16, coefs_syn_Q24[i]);
coefs_ana_Q24[i] = Inlines.silk_SMULWW(gain_ana_Q16, coefs_ana_Q24[i]);
}
}
Inlines.OpusAssert(false);
}
/**************************************************************/
/* Compute noise shaping coefficients and initial gain values */
/**************************************************************/
internal static void silk_noise_shape_analysis(
SilkChannelEncoder psEnc, /* I/O Encoder state FIX */
SilkEncoderControl psEncCtrl, /* I/O Encoder control FIX */
short[] pitch_res, /* I LPC residual from pitch analysis */
int pitch_res_ptr,
short[] x, /* I Input signal [ frame_length + la_shape ] */
int x_ptr
)
{
SilkShapeState psShapeSt = psEnc.sShape;
int k, i, nSamples, Qnrg, b_Q14, warping_Q16, scale = 0;
int SNR_adj_dB_Q7, HarmBoost_Q16, HarmShapeGain_Q16, Tilt_Q16, tmp32;
int nrg, pre_nrg_Q30, log_energy_Q7, log_energy_prev_Q7, energy_variation_Q7;
int delta_Q16, BWExp1_Q16, BWExp2_Q16, gain_mult_Q16, gain_add_Q16, strength_Q16, b_Q8;
int[] auto_corr = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1];
int[] refl_coef_Q16 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
int[] AR1_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
int[] AR2_Q24 = new int[SilkConstants.MAX_SHAPE_LPC_ORDER];
short[] x_windowed;
int pitch_res_ptr2;
int x_ptr2;
/* Point to start of first LPC analysis block */
x_ptr2 = x_ptr - psEnc.la_shape;
/****************/
/* GAIN CONTROL */
/****************/
SNR_adj_dB_Q7 = psEnc.SNR_dB_Q7;
/* Input quality is the average of the quality in the lowest two VAD bands */
psEncCtrl.input_quality_Q14 = (int)Inlines.silk_RSHIFT((int)psEnc.input_quality_bands_Q15[0]
+ psEnc.input_quality_bands_Q15[1], 2);
/* Coding quality level, between 0.0_Q0 and 1.0_Q0, but in Q14 */
psEncCtrl.coding_quality_Q14 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(SNR_adj_dB_Q7 -
((int)((20.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(20.0f, 7)*/, 4)), 1);
/* Reduce coding SNR during low speech activity */
if (psEnc.useCBR == 0)
{
b_Q8 = ((int)((1.0f) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(1.0f, 8)*/ - psEnc.speech_activity_Q8;
b_Q8 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(b_Q8, 8), b_Q8);
SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7,
Inlines.silk_SMULBB(((int)((0 - TuningParameters.BG_SNR_DECR_dB) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0 - TuningParameters.BG_SNR_DECR_dB, 7)*/ >> (4 + 1), b_Q8), /* Q11*/
Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ + psEncCtrl.input_quality_Q14, psEncCtrl.coding_quality_Q14)); /* Q12*/
}
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
/* Reduce gains for periodic signals */
SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.HARM_SNR_INCR_dB) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_SNR_INCR_dB, 8)*/, psEnc.LTPCorr_Q15);
}
else {
/* For unvoiced signals and low-quality input, adjust the quality slower than SNR_dB setting */
SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7,
Inlines.silk_SMLAWB(((int)((6.0f) * ((long)1 << (9)) + 0.5))/*Inlines.SILK_CONST(6.0f, 9)*/, -((int)((0.4f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.4f, 18)*/, psEnc.SNR_dB_Q7),
((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - psEncCtrl.input_quality_Q14);
}
/*************************/
/* SPARSENESS PROCESSING */
/*************************/
/* Set quantizer offset */
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
/* Initially set to 0; may be overruled in process_gains(..) */
psEnc.indices.quantOffsetType = 0;
psEncCtrl.sparseness_Q8 = 0;
}
else {
/* Sparseness measure, based on relative fluctuations of energy per 2 milliseconds */
nSamples = Inlines.silk_LSHIFT(psEnc.fs_kHz, 1);
energy_variation_Q7 = 0;
log_energy_prev_Q7 = 0;
pitch_res_ptr2 = pitch_res_ptr;
for (k = 0; k < Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, psEnc.nb_subfr) / 2; k++)
{
SumSqrShift.silk_sum_sqr_shift(out nrg, out scale, pitch_res, pitch_res_ptr2, nSamples);
nrg += Inlines.silk_RSHIFT(nSamples, scale); /* Q(-scale)*/
log_energy_Q7 = Inlines.silk_lin2log(nrg);
if (k > 0)
{
energy_variation_Q7 += Inlines.silk_abs(log_energy_Q7 - log_energy_prev_Q7);
}
log_energy_prev_Q7 = log_energy_Q7;
pitch_res_ptr2 += nSamples;
}
psEncCtrl.sparseness_Q8 = Inlines.silk_RSHIFT(Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(energy_variation_Q7 -
((int)((5.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(5.0f, 7)*/, ((int)((0.1f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.1f, 16)*/)), 7);
/* Set quantization offset depending on sparseness measure */
if (psEncCtrl.sparseness_Q8 > ((int)((TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSENESS_THRESHOLD_QNT_OFFSET, 8)*/)
{
psEnc.indices.quantOffsetType = 0;
}
else {
psEnc.indices.quantOffsetType = 1;
}
/* Increase coding SNR for sparse signals */
SNR_adj_dB_Q7 = Inlines.silk_SMLAWB(SNR_adj_dB_Q7, ((int)((TuningParameters.SPARSE_SNR_INCR_dB) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SPARSE_SNR_INCR_dB, 15)*/, psEncCtrl.sparseness_Q8 - ((int)((0.5f) * ((long)1 << (8)) + 0.5))/*Inlines.SILK_CONST(0.5f, 8)*/);
}
/*******************************/
/* Control bandwidth expansion */
/*******************************/
/* More BWE for signals with high prediction gain */
strength_Q16 = Inlines.silk_SMULWB(psEncCtrl.predGain_Q16, ((int)((TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.FIND_PITCH_WHITE_NOISE_FRACTION, 16)*/);
BWExp1_Q16 = BWExp2_Q16 = Inlines.silk_DIV32_varQ(((int)((TuningParameters.BANDWIDTH_EXPANSION) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.BANDWIDTH_EXPANSION, 16)*/,
Inlines.silk_SMLAWW(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/, strength_Q16, strength_Q16), 16);
delta_Q16 = Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULBB(3, psEncCtrl.coding_quality_Q14),
((int)((TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_BANDWIDTH_EXPANSION_DELTA, 16)*/);
BWExp1_Q16 = Inlines.silk_SUB32(BWExp1_Q16, delta_Q16);
BWExp2_Q16 = Inlines.silk_ADD32(BWExp2_Q16, delta_Q16);
/* BWExp1 will be applied after BWExp2, so make it relative */
BWExp1_Q16 = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(BWExp1_Q16, 14), Inlines.silk_RSHIFT(BWExp2_Q16, 2));
if (psEnc.warping_Q16 > 0)
{
/* Slightly more warping in analysis will move quantization noise up in frequency, where it's better masked */
warping_Q16 = Inlines.silk_SMLAWB(psEnc.warping_Q16, (int)psEncCtrl.coding_quality_Q14, ((int)((0.01f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(0.01f, 18)*/);
}
else {
warping_Q16 = 0;
}
/********************************************/
/* Compute noise shaping AR coefs and gains */
/********************************************/
x_windowed = new short[psEnc.shapeWinLength];
for (k = 0; k < psEnc.nb_subfr; k++)
{
/* Apply window: sine slope followed by flat part followed by cosine slope */
int shift, slope_part, flat_part;
flat_part = psEnc.fs_kHz * 3;
slope_part = Inlines.silk_RSHIFT(psEnc.shapeWinLength - flat_part, 1);
ApplySineWindow.silk_apply_sine_window(x_windowed, 0, x, x_ptr2, 1, slope_part);
shift = slope_part;
Array.Copy(x, x_ptr2 + shift, x_windowed, shift, flat_part);
shift += flat_part;
ApplySineWindow.silk_apply_sine_window(x_windowed, shift, x, x_ptr2 + shift, 2, slope_part);
/* Update pointer: next LPC analysis block */
x_ptr2 += psEnc.subfr_length;
BoxedValueInt scale_boxed = new BoxedValueInt(scale);
if (psEnc.warping_Q16 > 0)
{
/* Calculate warped auto correlation */
Autocorrelation.silk_warped_autocorrelation(auto_corr, scale_boxed, x_windowed, warping_Q16, psEnc.shapeWinLength, psEnc.shapingLPCOrder);
}
else {
/* Calculate regular auto correlation */
Autocorrelation.silk_autocorr(auto_corr, scale_boxed, x_windowed, psEnc.shapeWinLength, psEnc.shapingLPCOrder + 1);
}
scale = scale_boxed.Val;
/* Add white noise, as a fraction of energy */
auto_corr[0] = Inlines.silk_ADD32(auto_corr[0], Inlines.silk_max_32(Inlines.silk_SMULWB(Inlines.silk_RSHIFT(auto_corr[0], 4),
((int)((TuningParameters.SHAPE_WHITE_NOISE_FRACTION) * ((long)1 << (20)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SHAPE_WHITE_NOISE_FRACTION, 20)*/), 1));
/* Calculate the reflection coefficients using schur */
nrg = Schur.silk_schur64(refl_coef_Q16, auto_corr, psEnc.shapingLPCOrder);
Inlines.OpusAssert(nrg >= 0);
/* Convert reflection coefficients to prediction coefficients */
K2A.silk_k2a_Q16(AR2_Q24, refl_coef_Q16, psEnc.shapingLPCOrder);
Qnrg = -scale; /* range: -12...30*/
Inlines.OpusAssert(Qnrg >= -12);
Inlines.OpusAssert(Qnrg <= 30);
/* Make sure that Qnrg is an even number */
if ((Qnrg & 1) != 0)
{
Qnrg -= 1;
nrg >>= 1;
}
tmp32 = Inlines.silk_SQRT_APPROX(nrg);
Qnrg >>= 1; /* range: -6...15*/
psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(tmp32, 16 - Qnrg);
if (psEnc.warping_Q16 > 0)
{
/* Adjust gain for warping */
gain_mult_Q16 = warped_gain(AR2_Q24, warping_Q16, psEnc.shapingLPCOrder);
Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0);
if (Inlines.silk_SMULWW(Inlines.silk_RSHIFT_ROUND(psEncCtrl.Gains_Q16[k], 1), gain_mult_Q16) >= (int.MaxValue >> 1))
{
psEncCtrl.Gains_Q16[k] = int.MaxValue;
}
else {
psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16);
}
}
/* Bandwidth expansion for synthesis filter shaping */
BWExpander.silk_bwexpander_32(AR2_Q24, psEnc.shapingLPCOrder, BWExp2_Q16);
/* Compute noise shaping filter coefficients */
Array.Copy(AR2_Q24, AR1_Q24, psEnc.shapingLPCOrder);
/* Bandwidth expansion for analysis filter shaping */
Inlines.OpusAssert(BWExp1_Q16 <= ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/);
BWExpander.silk_bwexpander_32(AR1_Q24, psEnc.shapingLPCOrder, BWExp1_Q16);
/* Ratio of prediction gains, in energy domain */
pre_nrg_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR2_Q24, psEnc.shapingLPCOrder);
nrg = LPCInversePredGain.silk_LPC_inverse_pred_gain_Q24(AR1_Q24, psEnc.shapingLPCOrder);
/*psEncCtrl.GainsPre[ k ] = 1.0f - 0.7f * ( 1.0f - pre_nrg / nrg ) = 0.3f + 0.7f * pre_nrg / nrg;*/
pre_nrg_Q30 = Inlines.silk_LSHIFT32(Inlines.silk_SMULWB(pre_nrg_Q30, ((int)((0.7f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(0.7f, 15)*/), 1);
psEncCtrl.GainsPre_Q14[k] = (int)((int)((0.3f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.3f, 14)*/ + Inlines.silk_DIV32_varQ(pre_nrg_Q30, nrg, 14);
/* Convert to monic warped prediction coefficients and limit absolute values */
limit_warped_coefs(AR2_Q24, AR1_Q24, warping_Q16, ((int)((3.999f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(3.999f, 24)*/, psEnc.shapingLPCOrder);
/* Convert from Q24 to Q13 and store in int16 */
for (i = 0; i < psEnc.shapingLPCOrder; i++)
{
psEncCtrl.AR1_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR1_Q24[i], 11));
psEncCtrl.AR2_Q13[k * SilkConstants.MAX_SHAPE_LPC_ORDER + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(AR2_Q24[i], 11));
}
}
/*****************/
/* Gain tweaking */
/*****************/
/* Increase gains during low speech activity and put lower limit on gains */
gain_mult_Q16 = Inlines.silk_log2lin(-Inlines.silk_SMLAWB(-((int)((16.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, SNR_adj_dB_Q7, ((int)((0.16f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/));
gain_add_Q16 = Inlines.silk_log2lin(Inlines.silk_SMLAWB(((int)((16.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(16.0f, 7)*/, ((int)((SilkConstants.MIN_QGAIN_DB) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.MIN_QGAIN_DB, 7)*/, ((int)((0.16f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.16f, 16)*/));
Inlines.OpusAssert(gain_mult_Q16 > 0);
for (k = 0; k < psEnc.nb_subfr; k++)
{
psEncCtrl.Gains_Q16[k] = Inlines.silk_SMULWW(psEncCtrl.Gains_Q16[k], gain_mult_Q16);
Inlines.OpusAssert(psEncCtrl.Gains_Q16[k] >= 0);
psEncCtrl.Gains_Q16[k] = Inlines.silk_ADD_POS_SAT32(psEncCtrl.Gains_Q16[k], gain_add_Q16);
}
gain_mult_Q16 = ((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ + Inlines.silk_RSHIFT_ROUND(Inlines.silk_MLA(((int)((TuningParameters.INPUT_TILT) * ((long)1 << (26)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.INPUT_TILT, 26)*/,
psEncCtrl.coding_quality_Q14, ((int)((TuningParameters.HIGH_RATE_INPUT_TILT) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_INPUT_TILT, 12)*/), 10);
for (k = 0; k < psEnc.nb_subfr; k++)
{
psEncCtrl.GainsPre_Q14[k] = Inlines.silk_SMULWB(gain_mult_Q16, psEncCtrl.GainsPre_Q14[k]);
}
/************************************************/
/* Control low-frequency shaping and noise tilt */
/************************************************/
/* Less low frequency shaping for noisy inputs */
strength_Q16 = Inlines.silk_MUL(((int)((TuningParameters.LOW_FREQ_SHAPING) * ((long)1 << (4)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_FREQ_SHAPING, 4)*/, Inlines.silk_SMLAWB(((int)((1.0f) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(1.0f, 12)*/,
((int)((TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_QUALITY_LOW_FREQ_SHAPING_DECR, 13)*/, psEnc.input_quality_bands_Q15[0] - ((int)((1.0f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(1.0f, 15)*/));
strength_Q16 = Inlines.silk_RSHIFT(Inlines.silk_MUL(strength_Q16, psEnc.speech_activity_Q8), 8);
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
/* Reduce low frequencies quantization noise for periodic signals, depending on pitch lag */
/*f = 400; freqz([1, -0.98 + 2e-4 * f], [1, -0.97 + 7e-4 * f], 2^12, Fs); axis([0, 1000, -10, 1])*/
int fs_kHz_inv = Inlines.silk_DIV32_16(((int)((0.2f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/, psEnc.fs_kHz);
for (k = 0; k < psEnc.nb_subfr; k++)
{
b_Q14 = fs_kHz_inv + Inlines.silk_DIV32_16(((int)((3.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(3.0f, 14)*/, psEncCtrl.pitchL[k]);
/* Pack two coefficients in one int32 */
psEncCtrl.LF_shp_Q14[k] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 - Inlines.silk_SMULWB(strength_Q16, b_Q14), 16);
psEncCtrl.LF_shp_Q14[k] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: again, cast to ushort was done here where bitwise masking was intended
}
Inlines.OpusAssert(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/ < ((int)((0.5f) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(0.5f, 24)*/); /* Guarantees that second argument to SMULWB() is within range of an short*/
Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/ -
Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - ((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/,
Inlines.silk_SMULWB(((int)((TuningParameters.HARM_HP_NOISE_COEF) * ((long)1 << (24)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARM_HP_NOISE_COEF, 24)*/, psEnc.speech_activity_Q8));
}
else {
b_Q14 = Inlines.silk_DIV32_16(21299, psEnc.fs_kHz); /* 1.3_Q0 = 21299_Q14*/
/* Pack two coefficients in one int32 */
psEncCtrl.LF_shp_Q14[0] = Inlines.silk_LSHIFT(((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/ - b_Q14 -
Inlines.silk_SMULWB(strength_Q16, Inlines.silk_SMULWB(((int)((0.6f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.6f, 16)*/, b_Q14)), 16);
psEncCtrl.LF_shp_Q14[0] |= (b_Q14 - ((int)((1.0f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1.0f, 14)*/) & 0xFFFF; // opus bug: cast to ushort is better expressed as a bitwise operator, otherwise runtime analysis might flag it as an overflow error
for (k = 1; k < psEnc.nb_subfr; k++)
{
psEncCtrl.LF_shp_Q14[k] = psEncCtrl.LF_shp_Q14[0];
}
Tilt_Q16 = -((int)((TuningParameters.HP_NOISE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HP_NOISE_COEF, 16)*/;
}
/****************************/
/* HARMONIC SHAPING CONTROL */
/****************************/
/* Control boosting of harmonic frequencies */
HarmBoost_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (17)) + 0.5))/*Inlines.SILK_CONST(1.0f, 17)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 3),
psEnc.LTPCorr_Q15), ((int)((TuningParameters.LOW_RATE_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_RATE_HARMONIC_BOOST, 16)*/);
/* More harmonic boost for noisy input signals */
HarmBoost_Q16 = Inlines.silk_SMLAWB(HarmBoost_Q16,
((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_LSHIFT(psEncCtrl.input_quality_Q14, 2), ((int)((TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LOW_INPUT_QUALITY_HARMONIC_BOOST, 16)*/);
if (SilkConstants.USE_HARM_SHAPING != 0 && psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
/* More harmonic noise shaping for high bitrates or noisy input */
HarmShapeGain_Q16 = Inlines.silk_SMLAWB(((int)((TuningParameters.HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HARMONIC_SHAPING, 16)*/,
((int)((1.0f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1.0f, 16)*/ - Inlines.silk_SMULWB(((int)((1.0f) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(1.0f, 18)*/ - Inlines.silk_LSHIFT(psEncCtrl.coding_quality_Q14, 4),
psEncCtrl.input_quality_Q14), ((int)((TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING, 16)*/);
/* Less harmonic noise shaping for less periodic signals */
HarmShapeGain_Q16 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(HarmShapeGain_Q16, 1),
Inlines.silk_SQRT_APPROX(Inlines.silk_LSHIFT(psEnc.LTPCorr_Q15, 15)));
}
else {
HarmShapeGain_Q16 = 0;
}
/*************************/
/* Smooth over subframes */
/*************************/
for (k = 0; k < SilkConstants.MAX_NB_SUBFR; k++)
{
psShapeSt.HarmBoost_smth_Q16 =
Inlines.silk_SMLAWB(psShapeSt.HarmBoost_smth_Q16, HarmBoost_Q16 - psShapeSt.HarmBoost_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);
psShapeSt.HarmShapeGain_smth_Q16 =
Inlines.silk_SMLAWB(psShapeSt.HarmShapeGain_smth_Q16, HarmShapeGain_Q16 - psShapeSt.HarmShapeGain_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);
psShapeSt.Tilt_smth_Q16 =
Inlines.silk_SMLAWB(psShapeSt.Tilt_smth_Q16, Tilt_Q16 - psShapeSt.Tilt_smth_Q16, ((int)((TuningParameters.SUBFR_SMTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.SUBFR_SMTH_COEF, 16)*/);
psEncCtrl.HarmBoost_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmBoost_smth_Q16, 2);
psEncCtrl.HarmShapeGain_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.HarmShapeGain_smth_Q16, 2);
psEncCtrl.Tilt_Q14[k] = (int)Inlines.silk_RSHIFT_ROUND(psShapeSt.Tilt_smth_Q16, 2);
}
}
}
}
@@ -0,0 +1,486 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
/// <summary>
/// Routines for managing packet loss concealment
/// </summary>
internal static class PLC
{
private const int NB_ATT = 2;
private static readonly short[] HARM_ATT_Q15 = { 32440, 31130 }; /* 0.99, 0.95 */
private static readonly short[] PLC_RAND_ATTENUATE_V_Q15 = { 31130, 26214 }; /* 0.95, 0.8 */
private static readonly short[] PLC_RAND_ATTENUATE_UV_Q15 = { 32440, 29491 }; /* 0.99, 0.9 */
internal static void silk_PLC_Reset(
SilkChannelDecoder psDec /* I/O Decoder state */
)
{
psDec.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDec.frame_length, 8 - 1);
psDec.sPLC.prevGain_Q16[0] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/;
psDec.sPLC.prevGain_Q16[1] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/;
psDec.sPLC.subfr_length = 20;
psDec.sPLC.nb_subfr = 2;
}
internal static void silk_PLC(
SilkChannelDecoder psDec, /* I/O Decoder state */
SilkDecoderControl psDecCtrl, /* I/O Decoder control */
short[] frame, /* I/O signal */
int frame_ptr,
int lost /* I Loss flag */
)
{
/* PLC control function */
if (psDec.fs_kHz != psDec.sPLC.fs_kHz)
{
silk_PLC_Reset(psDec);
psDec.sPLC.fs_kHz = psDec.fs_kHz;
}
if (lost != 0)
{
/****************************/
/* Generate Signal */
/****************************/
silk_PLC_conceal(psDec, psDecCtrl, frame, frame_ptr);
psDec.lossCnt++;
}
else {
/****************************/
/* Update state */
/****************************/
silk_PLC_update(psDec, psDecCtrl);
}
}
/**************************************************/
/* Update state of PLC */
/**************************************************/
internal static void silk_PLC_update(
SilkChannelDecoder psDec, /* I/O Decoder state */
SilkDecoderControl psDecCtrl /* I/O Decoder control */
)
{
int LTP_Gain_Q14, temp_LTP_Gain_Q14;
int i, j;
PLCStruct psPLC = psDec.sPLC; // [porting note] pointer on the stack
/* Update parameters used in case of packet loss */
psDec.prevSignalType = psDec.indices.signalType;
LTP_Gain_Q14 = 0;
if (psDec.indices.signalType == SilkConstants.TYPE_VOICED)
{
/* Find the parameters for the last subframe which contains a pitch pulse */
for (j = 0; j * psDec.subfr_length < psDecCtrl.pitchL[psDec.nb_subfr - 1]; j++)
{
if (j == psDec.nb_subfr)
{
break;
}
temp_LTP_Gain_Q14 = 0;
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
temp_LTP_Gain_Q14 += psDecCtrl.LTPCoef_Q14[(psDec.nb_subfr - 1 - j) * SilkConstants.LTP_ORDER + i];
}
if (temp_LTP_Gain_Q14 > LTP_Gain_Q14)
{
LTP_Gain_Q14 = temp_LTP_Gain_Q14;
Array.Copy(psDecCtrl.LTPCoef_Q14, Inlines.silk_SMULBB(psDec.nb_subfr - 1 - j, SilkConstants.LTP_ORDER), psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(psDecCtrl.pitchL[psDec.nb_subfr - 1 - j], 8);
}
}
Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
psPLC.LTPCoef_Q14[SilkConstants.LTP_ORDER / 2] = (short)(LTP_Gain_Q14);
/* Limit LT coefs */
if (LTP_Gain_Q14 < SilkConstants.V_PITCH_GAIN_START_MIN_Q14)
{
int scale_Q10;
int tmp;
tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MIN_Q14, 10);
scale_Q10 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1));
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q10), 10));
}
}
else if (LTP_Gain_Q14 > SilkConstants.V_PITCH_GAIN_START_MAX_Q14)
{
int scale_Q14;
int tmp;
tmp = Inlines.silk_LSHIFT(SilkConstants.V_PITCH_GAIN_START_MAX_Q14, 14);
scale_Q14 = Inlines.silk_DIV32(tmp, Inlines.silk_max(LTP_Gain_Q14, 1));
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
psPLC.LTPCoef_Q14[i] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(psPLC.LTPCoef_Q14[i], scale_Q14), 14));
}
}
}
else {
psPLC.pitchL_Q8 = Inlines.silk_LSHIFT(Inlines.silk_SMULBB(psDec.fs_kHz, 18), 8);
Arrays.MemSetShort(psPLC.LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
}
/* Save LPC coeficients */
Array.Copy(psDecCtrl.PredCoef_Q12[1], psPLC.prevLPC_Q12, psDec.LPC_order);
psPLC.prevLTP_scale_Q14 = (short)(psDecCtrl.LTP_scale_Q14);
/* Save last two gains */
Array.Copy(psDecCtrl.Gains_Q16, psDec.nb_subfr - 2, psPLC.prevGain_Q16, 0, 2);
psPLC.subfr_length = psDec.subfr_length;
psPLC.nb_subfr = psDec.nb_subfr;
}
/// <summary>
///
/// </summary>
/// <param name="energy1">O</param>
/// <param name="shift1">O</param>
/// <param name="energy2">O</param>
/// <param name="shift2">O</param>
/// <param name="exc_Q14">I</param>
/// <param name="prevGain_Q10">I</param>
/// <param name="subfr_length">I</param>
/// <param name="nb_subfr">I</param>
internal static void silk_PLC_energy(
out int energy1,
out int shift1,
out int energy2,
out int shift2,
int[] exc_Q14,
int[] prevGain_Q10,
int subfr_length,
int nb_subfr)
{
int i, k;
int exc_buf_ptr = 0;
short[] exc_buf = new short[2 * subfr_length];
/* Find random noise component */
/* Scale previous excitation signal */
for (k = 0; k < 2; k++)
{
for (i = 0; i < subfr_length; i++)
{
exc_buf[exc_buf_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT(
Inlines.silk_SMULWW(exc_Q14[i + (k + nb_subfr - 2) * subfr_length], prevGain_Q10[k]), 8));
}
exc_buf_ptr += subfr_length;
}
/* Find the subframe with lowest energy of the last two and use that as random noise generator */
SumSqrShift.silk_sum_sqr_shift(out energy1, out shift1, exc_buf, subfr_length);
SumSqrShift.silk_sum_sqr_shift(out energy2, out shift2, exc_buf, subfr_length, subfr_length);
}
internal static void silk_PLC_conceal(
SilkChannelDecoder psDec, /* I/O Decoder state */
SilkDecoderControl psDecCtrl, /* I/O Decoder control */
short[] frame, /* O LPC residual signal */
int frame_ptr
)
{
int i, j, k;
int lag, idx, sLTP_buf_idx;
int rand_seed, harm_Gain_Q15, rand_Gain_Q15, inv_gain_Q30;
int energy1, energy2, shift1, shift2;
int rand_ptr;
int pred_lag_ptr;
int LPC_pred_Q10, LTP_pred_Q12;
short rand_scale_Q14;
short[] B_Q14;
int sLPC_Q14_ptr;
short[] sLTP = new short[psDec.ltp_mem_length];
int[] sLTP_Q14 = new int[psDec.ltp_mem_length + psDec.frame_length];
PLCStruct psPLC = psDec.sPLC;
int[] prevGain_Q10 = new int[2];
prevGain_Q10[0] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[0], 6);
prevGain_Q10[1] = Inlines.silk_RSHIFT(psPLC.prevGain_Q16[1], 6);
if (psDec.first_frame_after_reset != 0)
{
Arrays.MemSetShort(psPLC.prevLPC_Q12, 0, SilkConstants.MAX_LPC_ORDER);
}
silk_PLC_energy(out energy1, out shift1, out energy2, out shift2, psDec.exc_Q14, prevGain_Q10, psDec.subfr_length, psDec.nb_subfr);
if (Inlines.silk_RSHIFT(energy1, shift2) < Inlines.silk_RSHIFT(energy2, shift1))
{
/* First sub-frame has lowest energy */
rand_ptr = Inlines.silk_max_int(0, (psPLC.nb_subfr - 1) * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE);
}
else {
/* Second sub-frame has lowest energy */
rand_ptr = Inlines.silk_max_int(0, psPLC.nb_subfr * psPLC.subfr_length - SilkConstants.RAND_BUF_SIZE);
}
/* Set up Gain to random noise component */
B_Q14 = psPLC.LTPCoef_Q14;
rand_scale_Q14 = psPLC.randScale_Q14;
/* Set up attenuation gains */
harm_Gain_Q15 = HARM_ATT_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
if (psDec.prevSignalType == SilkConstants.TYPE_VOICED)
{
rand_Gain_Q15 = PLC_RAND_ATTENUATE_V_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
}
else {
rand_Gain_Q15 = PLC_RAND_ATTENUATE_UV_Q15[Inlines.silk_min_int(NB_ATT - 1, psDec.lossCnt)];
}
/* LPC concealment. Apply BWE to previous LPC */
BWExpander.silk_bwexpander(psPLC.prevLPC_Q12, psDec.LPC_order, ((int)((SilkConstants.BWE_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.BWE_COEF, 16)*/);
/* First Lost frame */
if (psDec.lossCnt == 0)
{
rand_scale_Q14 = 1 << 14;
/* Reduce random noise Gain for voiced frames */
if (psDec.prevSignalType == SilkConstants.TYPE_VOICED)
{
for (i = 0; i < SilkConstants.LTP_ORDER; i++)
{
rand_scale_Q14 -= B_Q14[i];
}
rand_scale_Q14 = Inlines.silk_max_16(3277, rand_scale_Q14); /* 0.2 */
rand_scale_Q14 = (short)Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, psPLC.prevLTP_scale_Q14), 14);
}
else
{
/* Reduce random noise for unvoiced frames with high LPC gain */
int invGain_Q30, down_scale_Q30;
invGain_Q30 = LPCInversePredGain.silk_LPC_inverse_pred_gain(psPLC.prevLPC_Q12, psDec.LPC_order);
down_scale_Q30 = Inlines.silk_min_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES), invGain_Q30);
down_scale_Q30 = Inlines.silk_max_32(Inlines.silk_RSHIFT((int)1 << 30, SilkConstants.LOG2_INV_LPC_GAIN_LOW_THRES), down_scale_Q30);
down_scale_Q30 = Inlines.silk_LSHIFT(down_scale_Q30, SilkConstants.LOG2_INV_LPC_GAIN_HIGH_THRES);
rand_Gain_Q15 = Inlines.silk_RSHIFT(Inlines.silk_SMULWB(down_scale_Q30, rand_Gain_Q15), 14);
}
}
rand_seed = psPLC.rand_seed;
lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8);
sLTP_buf_idx = psDec.ltp_mem_length;
/* Rewhiten LTP state */
idx = psDec.ltp_mem_length - lag - psDec.LPC_order - SilkConstants.LTP_ORDER / 2;
Inlines.OpusAssert(idx > 0);
Filters.silk_LPC_analysis_filter(sLTP, idx, psDec.outBuf, idx, psPLC.prevLPC_Q12, 0, psDec.ltp_mem_length - idx, psDec.LPC_order);
/* Scale LTP state */
inv_gain_Q30 = Inlines.silk_INVERSE32_varQ(psPLC.prevGain_Q16[1], 46);
inv_gain_Q30 = Inlines.silk_min(inv_gain_Q30, int.MaxValue >> 1);
for (i = idx + psDec.LPC_order; i < psDec.ltp_mem_length; i++)
{
sLTP_Q14[i] = Inlines.silk_SMULWB(inv_gain_Q30, sLTP[i]);
}
/***************************/
/* LTP synthesis filtering */
/***************************/
for (k = 0; k < psDec.nb_subfr; k++)
{
/* Set up pointer */
pred_lag_ptr = sLTP_buf_idx - lag + SilkConstants.LTP_ORDER / 2;
for (i = 0; i < psDec.subfr_length; i++)
{
/* Unrolled loop */
/* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */
LTP_pred_Q12 = 2;
LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr], B_Q14[0]);
LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 1], B_Q14[1]);
LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 2], B_Q14[2]);
LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 3], B_Q14[3]);
LTP_pred_Q12 = Inlines.silk_SMLAWB(LTP_pred_Q12, sLTP_Q14[pred_lag_ptr - 4], B_Q14[4]);
pred_lag_ptr++;
/* Generate LPC excitation */
rand_seed = Inlines.silk_RAND(rand_seed);
idx = Inlines.silk_RSHIFT(rand_seed, 25) & SilkConstants.RAND_BUF_MASK;
sLTP_Q14[sLTP_buf_idx] = Inlines.silk_LSHIFT32(Inlines.silk_SMLAWB(LTP_pred_Q12, psDec.exc_Q14[rand_ptr + idx], rand_scale_Q14), 2);
sLTP_buf_idx++;
}
/* Gradually reduce LTP gain */
for (j = 0; j < SilkConstants.LTP_ORDER; j++)
{
B_Q14[j] = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(harm_Gain_Q15, B_Q14[j]), 15));
}
/* Gradually reduce excitation gain */
rand_scale_Q14 = (short)(Inlines.silk_RSHIFT(Inlines.silk_SMULBB(rand_scale_Q14, rand_Gain_Q15), 15));
/* Slowly increase pitch lag */
psPLC.pitchL_Q8 = Inlines.silk_SMLAWB(psPLC.pitchL_Q8, psPLC.pitchL_Q8, SilkConstants.PITCH_DRIFT_FAC_Q16);
psPLC.pitchL_Q8 = Inlines.silk_min_32(psPLC.pitchL_Q8, Inlines.silk_LSHIFT(Inlines.silk_SMULBB(SilkConstants.MAX_PITCH_LAG_MS, psDec.fs_kHz), 8));
lag = Inlines.silk_RSHIFT_ROUND(psPLC.pitchL_Q8, 8);
}
/***************************/
/* LPC synthesis filtering */
/***************************/
sLPC_Q14_ptr = psDec.ltp_mem_length - SilkConstants.MAX_LPC_ORDER;
/* Copy LPC state */
Array.Copy(psDec.sLPC_Q14_buf, 0, sLTP_Q14, sLPC_Q14_ptr, SilkConstants.MAX_LPC_ORDER);
Inlines.OpusAssert(psDec.LPC_order >= 10); /* check that unrolling works */
for (i = 0; i < psDec.frame_length; i++)
{
/* partly unrolled */
int sLPCmaxi = sLPC_Q14_ptr + SilkConstants.MAX_LPC_ORDER + i;
/* Avoids introducing a bias because Inlines.silk_SMLAWB() always rounds to -inf */
LPC_pred_Q10 = Inlines.silk_RSHIFT(psDec.LPC_order, 1);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 1], psPLC.prevLPC_Q12[0]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 2], psPLC.prevLPC_Q12[1]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 3], psPLC.prevLPC_Q12[2]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 4], psPLC.prevLPC_Q12[3]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 5], psPLC.prevLPC_Q12[4]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 6], psPLC.prevLPC_Q12[5]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 7], psPLC.prevLPC_Q12[6]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 8], psPLC.prevLPC_Q12[7]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 9], psPLC.prevLPC_Q12[8]);
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - 10], psPLC.prevLPC_Q12[9]);
for (j = 10; j < psDec.LPC_order; j++)
{
LPC_pred_Q10 = Inlines.silk_SMLAWB(LPC_pred_Q10, sLTP_Q14[sLPCmaxi - j - 1], psPLC.prevLPC_Q12[j]);
}
/* Add prediction to LPC excitation */
sLTP_Q14[sLPCmaxi] = Inlines.silk_ADD_LSHIFT32(sLTP_Q14[sLPCmaxi], LPC_pred_Q10, 4);
/* Scale with Gain */
frame[frame_ptr + i] = (short)Inlines.silk_SAT16(Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULWW(sLTP_Q14[sLPCmaxi], prevGain_Q10[1]), 8)));
}
/* Save LPC state */
Array.Copy(sLTP_Q14, sLPC_Q14_ptr + psDec.frame_length, psDec.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
/**************************************/
/* Update states */
/**************************************/
psPLC.rand_seed = rand_seed;
psPLC.randScale_Q14 = rand_scale_Q14;
for (i = 0; i < SilkConstants.MAX_NB_SUBFR; i++)
{
psDecCtrl.pitchL[i] = lag;
}
}
/* Glues concealed frames with new good received frames */
internal static void silk_PLC_glue_frames(
SilkChannelDecoder psDec, /* I/O decoder state */
short[] frame, /* I/O signal */
int frame_ptr,
int length /* I length of signal */
)
{
int i;
int energy_shift, energy;
PLCStruct psPLC = psDec.sPLC;
if (psDec.lossCnt != 0)
{
/* Calculate energy in concealed residual */
SumSqrShift.silk_sum_sqr_shift(out psPLC.conc_energy, out psPLC.conc_energy_shift, frame, frame_ptr, length);
psPLC.last_frame_lost = 1;
}
else
{
if (psDec.sPLC.last_frame_lost != 0)
{
/* Calculate residual in decoded signal if last frame was lost */
SumSqrShift.silk_sum_sqr_shift(out energy, out energy_shift, frame, frame_ptr, length);
/* Normalize energies */
if (energy_shift > psPLC.conc_energy_shift)
{
psPLC.conc_energy = Inlines.silk_RSHIFT(psPLC.conc_energy, energy_shift - psPLC.conc_energy_shift);
}
else if (energy_shift < psPLC.conc_energy_shift)
{
energy = Inlines.silk_RSHIFT(energy, psPLC.conc_energy_shift - energy_shift);
}
/* Fade in the energy difference */
if (energy > psPLC.conc_energy)
{
int frac_Q24, LZ;
int gain_Q16, slope_Q16;
LZ = Inlines.silk_CLZ32(psPLC.conc_energy);
LZ = LZ - 1;
psPLC.conc_energy = Inlines.silk_LSHIFT(psPLC.conc_energy, LZ);
energy = Inlines.silk_RSHIFT(energy, Inlines.silk_max_32(24 - LZ, 0));
frac_Q24 = Inlines.silk_DIV32(psPLC.conc_energy, Inlines.silk_max(energy, 1));
gain_Q16 = Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(frac_Q24), 4);
slope_Q16 = Inlines.silk_DIV32_16(((int)1 << 16) - gain_Q16, length);
/* Make slope 4x steeper to avoid missing onsets after DTX */
slope_Q16 = Inlines.silk_LSHIFT(slope_Q16, 2);
for (i = frame_ptr; i < frame_ptr + length; i++)
{
frame[i] = (short)(Inlines.silk_SMULWB(gain_Q16, frame[i]));
gain_Q16 += slope_Q16;
if (gain_Q16 > (int)1 << 16)
{
break;
}
}
}
}
psPLC.last_frame_lost = 0;
}
}
}
}
@@ -0,0 +1,792 @@
/* 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.
*/
namespace Concentus.Silk
{
using Celt;
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class PitchAnalysisCore
{
private const int SCRATCH_SIZE = 22;
private const int SF_LENGTH_4KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 4);
private const int SF_LENGTH_8KHZ = (SilkConstants.PE_SUBFR_LENGTH_MS * 8);
private const int MIN_LAG_4KHZ = (SilkConstants.PE_MIN_LAG_MS * 4);
private const int MIN_LAG_8KHZ = (SilkConstants.PE_MIN_LAG_MS * 8);
private const int MAX_LAG_4KHZ = (SilkConstants.PE_MAX_LAG_MS * 4);
private const int MAX_LAG_8KHZ = (SilkConstants.PE_MAX_LAG_MS * 8 - 1);
private const int CSTRIDE_4KHZ = (MAX_LAG_4KHZ + 1 - MIN_LAG_4KHZ);
private const int CSTRIDE_8KHZ = (MAX_LAG_8KHZ + 3 - (MIN_LAG_8KHZ - 2));
private const int D_COMP_MIN = (MIN_LAG_8KHZ - 3);
private const int D_COMP_MAX = (MAX_LAG_8KHZ + 4);
private const int D_COMP_STRIDE = (D_COMP_MAX - D_COMP_MIN);
// typedef int silk_pe_stage3_vals[SilkConstants.PE_NB_STAGE3_LAGS];
// fixme can I linearize this?
private class silk_pe_stage3_vals
{
public readonly int[] Values = new int[SilkConstants.PE_NB_STAGE3_LAGS];
}
/*************************************************************/
/* FIXED POINT CORE PITCH ANALYSIS FUNCTION */
/*************************************************************/
internal static int silk_pitch_analysis_core( /* O Voicing estimate: 0 voiced, 1 unvoiced */
short[] frame, /* I Signal of length PE_FRAME_LENGTH_MS*Fs_kHz */
int[] pitch_out, /* O 4 pitch lag values */
BoxedValueShort lagIndex, /* O Lag Index */
BoxedValueSbyte contourIndex, /* O Pitch contour Index */
BoxedValueInt LTPCorr_Q15, /* I/O Normalized correlation; input: value from previous frame */
int prevLag, /* I Last lag of previous frame; set to zero is unvoiced */
int search_thres1_Q16, /* I First stage threshold for lag candidates 0 - 1 */
int search_thres2_Q13, /* I Final threshold for lag candidates 0 - 1 */
int Fs_kHz, /* I Sample frequency (kHz) */
int complexity, /* I Complexity setting, 0-2, where 2 is highest */
int nb_subfr /* I number of 5 ms subframes */
)
{
short[] frame_8kHz;
short[] frame_4kHz;
int[] filt_state = new int[6];
short[] input_frame_ptr;
int i, k, d, j;
short[] C;
int[] xcorr32;
short[] basis;
int basis_ptr;
short[] target;
int target_ptr;
int cross_corr, normalizer, energy, shift, energy_basis, energy_target;
int Cmax, length_d_srch, length_d_comp;
int[] d_srch = new int[SilkConstants.PE_D_SRCH_LENGTH];
short[] d_comp;
int sum, threshold, lag_counter;
int CBimax, CBimax_new, CBimax_old, lag, start_lag, end_lag, lag_new;
int CCmax, CCmax_b, CCmax_new_b, CCmax_new;
int[] CC = new int[SilkConstants.PE_NB_CBKS_STAGE2_EXT];
silk_pe_stage3_vals[] energies_st3;
silk_pe_stage3_vals[] cross_corr_st3;
int frame_length, frame_length_8kHz, frame_length_4kHz;
int sf_length;
int min_lag;
int max_lag;
int contour_bias_Q15, diff;
int nb_cbk_search;
int delta_lag_log2_sqr_Q7, lag_log2_Q7, prevLag_log2_Q7, prev_lag_bias_Q13;
sbyte[][] Lag_CB_ptr;
/* Check for valid sampling frequency */
Inlines.OpusAssert(Fs_kHz == 8 || Fs_kHz == 12 || Fs_kHz == 16);
/* Check for valid complexity setting */
Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX);
Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX);
Inlines.OpusAssert(search_thres1_Q16 >= 0 && search_thres1_Q16 <= (1 << 16));
Inlines.OpusAssert(search_thres2_Q13 >= 0 && search_thres2_Q13 <= (1 << 13));
/* Set up frame lengths max / min lag for the sampling frequency */
frame_length = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * Fs_kHz;
frame_length_4kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 4;
frame_length_8kHz = (SilkConstants.PE_LTP_MEM_LENGTH_MS + nb_subfr * SilkConstants.PE_SUBFR_LENGTH_MS) * 8;
sf_length = SilkConstants.PE_SUBFR_LENGTH_MS * Fs_kHz;
min_lag = SilkConstants.PE_MIN_LAG_MS * Fs_kHz;
max_lag = SilkConstants.PE_MAX_LAG_MS * Fs_kHz - 1;
/* Resample from input sampled at Fs_kHz to 8 kHz */
frame_8kHz = new short[frame_length_8kHz];
if (Fs_kHz == 16)
{
Arrays.MemSetInt(filt_state, 0, 2);
Resampler.silk_resampler_down2(filt_state, frame_8kHz, frame, frame_length);
}
else if (Fs_kHz == 12)
{
Arrays.MemSetInt(filt_state, 0, 6);
Resampler.silk_resampler_down2_3(filt_state, frame_8kHz, frame, frame_length);
}
else {
Inlines.OpusAssert(Fs_kHz == 8);
Array.Copy(frame, frame_8kHz, frame_length_8kHz);
}
/* Decimate again to 4 kHz */
Arrays.MemSetInt(filt_state, 0, 2); /* Set state to zero */
frame_4kHz = new short[frame_length_4kHz];
Resampler.silk_resampler_down2(filt_state, frame_4kHz, frame_8kHz, frame_length_8kHz);
/* Low-pass filter */
for (i = frame_length_4kHz - 1; i > 0; i--)
{
frame_4kHz[i] = Inlines.silk_ADD_SAT16(frame_4kHz[i], frame_4kHz[i - 1]);
}
/*******************************************************************************
** Scale 4 kHz signal down to prevent correlations measures from overflowing
** find scaling as max scaling for each 8kHz(?) subframe
*******************************************************************************/
/* Inner product is calculated with different lengths, so scale for the worst case */
SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_4kHz, frame_length_4kHz);
if (shift > 0)
{
shift = Inlines.silk_RSHIFT(shift, 1);
for (i = 0; i < frame_length_4kHz; i++)
{
frame_4kHz[i] = Inlines.silk_RSHIFT16(frame_4kHz[i], shift);
}
}
/******************************************************************************
* FIRST STAGE, operating in 4 khz
******************************************************************************/
C = new short[nb_subfr * CSTRIDE_8KHZ];
xcorr32 = new int[MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1];
Arrays.MemSetShort(C, 0, (nb_subfr >> 1) * CSTRIDE_4KHZ);
target = frame_4kHz;
target_ptr = Inlines.silk_LSHIFT(SF_LENGTH_4KHZ, 2);
for (k = 0; k < nb_subfr >> 1; k++)
{
basis = target;
basis_ptr = target_ptr - MIN_LAG_4KHZ;
CeltPitchXCorr.pitch_xcorr(target, target_ptr, target, target_ptr - MAX_LAG_4KHZ, xcorr32, SF_LENGTH_8KHZ, MAX_LAG_4KHZ - MIN_LAG_4KHZ + 1);
/* Calculate first vector products before loop */
cross_corr = xcorr32[MAX_LAG_4KHZ - MIN_LAG_4KHZ];
normalizer = Inlines.silk_inner_prod_self(target, target_ptr, SF_LENGTH_8KHZ);
normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ));
normalizer = Inlines.silk_ADD32(normalizer, Inlines.silk_SMULBB(SF_LENGTH_8KHZ, 4000));
Inlines.MatrixSet(C, k, 0, CSTRIDE_4KHZ,
(short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */
/* From now on normalizer is computed recursively */
for (d = MIN_LAG_4KHZ + 1; d <= MAX_LAG_4KHZ; d++)
{
basis_ptr--;
cross_corr = xcorr32[MAX_LAG_4KHZ - d];
/* Add contribution of new sample and remove contribution from oldest sample */
normalizer = Inlines.silk_ADD32(normalizer,
Inlines.silk_SMULBB(basis[basis_ptr], basis[basis_ptr]) -
Inlines.silk_SMULBB(basis[basis_ptr + SF_LENGTH_8KHZ], basis[basis_ptr + SF_LENGTH_8KHZ]));
Inlines.MatrixSet(C, k, d - MIN_LAG_4KHZ, CSTRIDE_4KHZ,
(short)Inlines.silk_DIV32_varQ(cross_corr, normalizer, 13 + 1)); /* Q13 */
}
/* Update target pointer */
target_ptr += SF_LENGTH_8KHZ;
}
/* Combine two subframes into single correlation measure and apply short-lag bias */
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--)
{
sum = (int)Inlines.MatrixGet(C, 0, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ)
+ (int)Inlines.MatrixGet(C, 1, i - MIN_LAG_4KHZ, CSTRIDE_4KHZ); /* Q14 */
sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */
C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */
}
}
else {
/* Only short-lag bias */
for (i = MAX_LAG_4KHZ; i >= MIN_LAG_4KHZ; i--)
{
sum = Inlines.silk_LSHIFT((int)C[i - MIN_LAG_4KHZ], 1); /* Q14 */
sum = Inlines.silk_SMLAWB(sum, sum, Inlines.silk_LSHIFT(-i, 4)); /* Q14 */
C[i - MIN_LAG_4KHZ] = (short)sum; /* Q14 */
}
}
/* Sort */
length_d_srch = Inlines.silk_ADD_LSHIFT32(4, complexity, 1);
Inlines.OpusAssert(3 * length_d_srch <= SilkConstants.PE_D_SRCH_LENGTH);
Sort.silk_insertion_sort_decreasing_int16(C, d_srch, CSTRIDE_4KHZ, length_d_srch);
/* Escape if correlation is very low already here */
Cmax = (int)C[0]; /* Q14 */
if (Cmax < ((int)((0.2f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.2f, 14)*/)
{
Arrays.MemSetInt(pitch_out, 0, nb_subfr);
LTPCorr_Q15.Val = 0;
lagIndex.Val = 0;
contourIndex.Val = 0;
return 1;
}
threshold = Inlines.silk_SMULWB(search_thres1_Q16, Cmax);
for (i = 0; i < length_d_srch; i++)
{
/* Convert to 8 kHz indices for the sorted correlation that exceeds the threshold */
if (C[i] > threshold)
{
d_srch[i] = Inlines.silk_LSHIFT(d_srch[i] + MIN_LAG_4KHZ, 1);
}
else {
length_d_srch = i;
break;
}
}
Inlines.OpusAssert(length_d_srch > 0);
d_comp = new short[D_COMP_STRIDE];
for (i = D_COMP_MIN; i < D_COMP_MAX; i++)
{
d_comp[i - D_COMP_MIN] = 0;
}
for (i = 0; i < length_d_srch; i++)
{
d_comp[d_srch[i] - D_COMP_MIN] = 1;
}
/* Convolution */
for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--)
{
d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN]);
}
length_d_srch = 0;
for (i = MIN_LAG_8KHZ; i < MAX_LAG_8KHZ + 1; i++)
{
if (d_comp[i + 1 - D_COMP_MIN] > 0)
{
d_srch[length_d_srch] = i;
length_d_srch++;
}
}
/* Convolution */
for (i = D_COMP_MAX - 1; i >= MIN_LAG_8KHZ; i--)
{
d_comp[i - D_COMP_MIN] += (short)(d_comp[i - 1 - D_COMP_MIN] + d_comp[i - 2 - D_COMP_MIN] + d_comp[i - 3 - D_COMP_MIN]);
}
length_d_comp = 0;
for (i = MIN_LAG_8KHZ; i < D_COMP_MAX; i++)
{
if (d_comp[i - D_COMP_MIN] > 0)
{
d_comp[length_d_comp] = (short)(i - 2);
length_d_comp++;
}
}
/**********************************************************************************
** SECOND STAGE, operating at 8 kHz, on lag sections with high correlation
*************************************************************************************/
/******************************************************************************
** Scale signal down to avoid correlations measures from overflowing
*******************************************************************************/
/* find scaling as max scaling for each subframe */
SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame_8kHz, frame_length_8kHz);
if (shift > 0)
{
shift = Inlines.silk_RSHIFT(shift, 1);
for (i = 0; i < frame_length_8kHz; i++)
{
frame_8kHz[i] = Inlines.silk_RSHIFT16(frame_8kHz[i], shift);
}
}
/*********************************************************************************
* Find energy of each subframe projected onto its history, for a range of delays
*********************************************************************************/
Arrays.MemSetShort(C, 0, nb_subfr * CSTRIDE_8KHZ );
target = frame_8kHz;
target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * 8;
for (k = 0; k < nb_subfr; k++)
{
energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod(target, target_ptr, target, target_ptr, SF_LENGTH_8KHZ), 1);
for (j = 0; j < length_d_comp; j++)
{
d = d_comp[j];
basis = target;
basis_ptr = target_ptr - d;
cross_corr = Inlines.silk_inner_prod(target, target_ptr, basis, basis_ptr, SF_LENGTH_8KHZ);
if (cross_corr > 0)
{
energy_basis = Inlines.silk_inner_prod_self(basis, basis_ptr, SF_LENGTH_8KHZ);
Inlines.MatrixSet(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ,
(short)Inlines.silk_DIV32_varQ(cross_corr,
Inlines.silk_ADD32(energy_target,
energy_basis),
13 + 1)); /* Q13 */
}
else {
Inlines.MatrixSet<short>(C, k, d - (MIN_LAG_8KHZ - 2), CSTRIDE_8KHZ, 0);
}
}
target_ptr += SF_LENGTH_8KHZ;
}
/* search over lag range and lags codebook */
/* scale factor for lag codebook, as a function of center lag */
CCmax = int.MinValue;
CCmax_b = int.MinValue;
CBimax = 0; /* To avoid returning undefined lag values */
lag = -1; /* To check if lag with strong enough correlation has been found */
if (prevLag > 0)
{
if (Fs_kHz == 12)
{
prevLag = Inlines.silk_DIV32_16(Inlines.silk_LSHIFT(prevLag, 1), 3);
}
else if (Fs_kHz == 16)
{
prevLag = Inlines.silk_RSHIFT(prevLag, 1);
}
prevLag_log2_Q7 = Inlines.silk_lin2log((int)prevLag);
}
else {
prevLag_log2_Q7 = 0;
}
Inlines.OpusAssert(search_thres2_Q13 == Inlines.silk_SAT16(search_thres2_Q13));
/* Set up stage 2 codebook based on number of subframes */
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_CB_ptr = Tables.silk_CB_lags_stage2;
if (Fs_kHz == 8 && complexity > SilkConstants.SILK_PE_MIN_COMPLEX)
{
/* If input is 8 khz use a larger codebook here because it is last stage */
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_EXT;
}
else {
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2;
}
}
else {
Lag_CB_ptr = Tables.silk_CB_lags_stage2_10_ms;
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE2_10MS;
}
for (k = 0; k < length_d_srch; k++)
{
d = d_srch[k];
for (j = 0; j < nb_cbk_search; j++)
{
CC[j] = 0;
for (i = 0; i < nb_subfr; i++)
{
int d_subfr;
/* Try all codebooks */
d_subfr = d + Lag_CB_ptr[i][j];
CC[j] = CC[j]
+ (int)Inlines.MatrixGet(C, i,
d_subfr - (MIN_LAG_8KHZ - 2),
CSTRIDE_8KHZ);
}
}
/* Find best codebook */
CCmax_new = int.MinValue;
CBimax_new = 0;
for (i = 0; i < nb_cbk_search; i++)
{
if (CC[i] > CCmax_new)
{
CCmax_new = CC[i];
CBimax_new = i;
}
}
/* Bias towards shorter lags */
lag_log2_Q7 = Inlines.silk_lin2log(d); /* Q7 */
Inlines.OpusAssert(lag_log2_Q7 == Inlines.silk_SAT16(lag_log2_Q7));
Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/));
CCmax_new_b = CCmax_new - Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_SHORTLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_SHORTLAG_BIAS, 13)*/, lag_log2_Q7), 7); /* Q13 */
/* Bias towards previous lag */
Inlines.OpusAssert(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/ == Inlines.silk_SAT16(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/));
if (prevLag > 0)
{
delta_lag_log2_sqr_Q7 = lag_log2_Q7 - prevLag_log2_Q7;
Inlines.OpusAssert(delta_lag_log2_sqr_Q7 == Inlines.silk_SAT16(delta_lag_log2_sqr_Q7));
delta_lag_log2_sqr_Q7 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(delta_lag_log2_sqr_Q7, delta_lag_log2_sqr_Q7), 7);
prev_lag_bias_Q13 = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(nb_subfr * ((int)((SilkConstants.PE_PREVLAG_BIAS) * ((long)1 << (13)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_PREVLAG_BIAS, 13)*/, LTPCorr_Q15.Val), 15); /* Q13 */
prev_lag_bias_Q13 = Inlines.silk_DIV32(Inlines.silk_MUL(prev_lag_bias_Q13, delta_lag_log2_sqr_Q7), delta_lag_log2_sqr_Q7 + ((int)((0.5f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.5f, 7)*/);
CCmax_new_b -= prev_lag_bias_Q13; /* Q13 */
}
if (CCmax_new_b > CCmax_b && /* Find maximum biased correlation */
CCmax_new > Inlines.silk_SMULBB(nb_subfr, search_thres2_Q13) && /* Correlation needs to be high enough to be voiced */
Tables.silk_CB_lags_stage2[0][CBimax_new] <= MIN_LAG_8KHZ /* Lag must be in range */
)
{
CCmax_b = CCmax_new_b;
CCmax = CCmax_new;
lag = d;
CBimax = CBimax_new;
}
}
if (lag == -1)
{
/* No suitable candidate found */
Arrays.MemSetInt(pitch_out, 0, nb_subfr);
LTPCorr_Q15.Val = 0;
lagIndex.Val = 0;
contourIndex.Val = 0;
return 1;
}
/* Output normalized correlation */
LTPCorr_Q15.Val = (int)Inlines.silk_LSHIFT(Inlines.silk_DIV32_16(CCmax, nb_subfr), 2);
Inlines.OpusAssert(LTPCorr_Q15.Val >= 0);
if (Fs_kHz > 8)
{
short[] scratch_mem;
/***************************************************************************/
/* Scale input signal down to avoid correlations measures from overflowing */
/***************************************************************************/
/* find scaling as max scaling for each subframe */
SumSqrShift.silk_sum_sqr_shift(out energy, out shift, frame, frame_length);
if (shift > 0)
{
scratch_mem = new short[frame_length];
/* Move signal to scratch mem because the input signal should be unchanged */
shift = Inlines.silk_RSHIFT(shift, 1);
for (i = 0; i < frame_length; i++)
{
scratch_mem[i] = Inlines.silk_RSHIFT16(frame[i], shift);
}
input_frame_ptr = scratch_mem;
}
else {
input_frame_ptr = frame;
}
/* Search in original signal */
CBimax_old = CBimax;
/* Compensate for decimation */
Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag));
if (Fs_kHz == 12)
{
lag = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(lag, 3), 1);
}
else if (Fs_kHz == 16)
{
lag = Inlines.silk_LSHIFT(lag, 1);
}
else {
lag = Inlines.silk_SMULBB(lag, 3);
}
lag = Inlines.silk_LIMIT_int(lag, min_lag, max_lag);
start_lag = Inlines.silk_max_int(lag - 2, min_lag);
end_lag = Inlines.silk_min_int(lag + 2, max_lag);
lag_new = lag; /* to avoid undefined lag */
CBimax = 0; /* to avoid undefined lag */
CCmax = int.MinValue;
/* pitch lags according to second stage */
for (k = 0; k < nb_subfr; k++)
{
pitch_out[k] = lag + 2 * Tables.silk_CB_lags_stage2[k][CBimax_old];
}
/* Set up codebook parameters according to complexity setting and frame length */
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
nb_cbk_search = (int)Tables.silk_nb_cbk_searchs_stage3[complexity];
Lag_CB_ptr = Tables.silk_CB_lags_stage3;
}
else {
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS;
Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms;
}
/* Calculate the correlations and energies needed in stage 3 */
energies_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search];
cross_corr_st3 = new silk_pe_stage3_vals[nb_subfr * nb_cbk_search];
for (int c = 0; c < nb_subfr * nb_cbk_search; c++)
{
energies_st3[c] = new silk_pe_stage3_vals(); // fixme: these can be replaced with a linearized array probably, or at least a struct
cross_corr_st3[c] = new silk_pe_stage3_vals();
}
silk_P_Ana_calc_corr_st3(cross_corr_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity);
silk_P_Ana_calc_energy_st3(energies_st3, input_frame_ptr, start_lag, sf_length, nb_subfr, complexity);
lag_counter = 0;
Inlines.OpusAssert(lag == Inlines.silk_SAT16(lag));
contour_bias_Q15 = Inlines.silk_DIV32_16(((int)((SilkConstants.PE_FLATCONTOUR_BIAS) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.PE_FLATCONTOUR_BIAS, 15)*/, lag);
target = input_frame_ptr;
target_ptr = SilkConstants.PE_LTP_MEM_LENGTH_MS * Fs_kHz;
energy_target = Inlines.silk_ADD32(Inlines.silk_inner_prod_self(target, target_ptr, nb_subfr * sf_length), 1);
for (d = start_lag; d <= end_lag; d++)
{
for (j = 0; j < nb_cbk_search; j++)
{
cross_corr = 0;
energy = energy_target;
for (k = 0; k < nb_subfr; k++)
{
cross_corr = Inlines.silk_ADD32(cross_corr,
Inlines.MatrixGet(cross_corr_st3, k, j,
nb_cbk_search).Values[lag_counter]);
energy = Inlines.silk_ADD32(energy,
Inlines.MatrixGet(energies_st3, k, j,
nb_cbk_search).Values[lag_counter]);
Inlines.OpusAssert(energy >= 0);
}
if (cross_corr > 0)
{
CCmax_new = Inlines.silk_DIV32_varQ(cross_corr, energy, 13 + 1); /* Q13 */
/* Reduce depending on flatness of contour */
diff = short.MaxValue - Inlines.silk_MUL(contour_bias_Q15, j); /* Q15 */
Inlines.OpusAssert(diff == Inlines.silk_SAT16(diff));
CCmax_new = Inlines.silk_SMULWB(CCmax_new, diff); /* Q14 */
}
else {
CCmax_new = 0;
}
if (CCmax_new > CCmax && (d + Tables.silk_CB_lags_stage3[0][j]) <= max_lag)
{
CCmax = CCmax_new;
lag_new = d;
CBimax = j;
}
}
lag_counter++;
}
for (k = 0; k < nb_subfr; k++)
{
pitch_out[k] = lag_new + Lag_CB_ptr[k][CBimax];
pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], min_lag, SilkConstants.PE_MAX_LAG_MS * Fs_kHz);
}
lagIndex.Val = (short)(lag_new - min_lag);
contourIndex.Val = (sbyte)CBimax;
}
else { /* Fs_kHz == 8 */
/* Save Lags */
for (k = 0; k < nb_subfr; k++)
{
pitch_out[k] = lag + Lag_CB_ptr[k][CBimax];
pitch_out[k] = Inlines.silk_LIMIT(pitch_out[k], MIN_LAG_8KHZ, SilkConstants.PE_MAX_LAG_MS * 8);
}
lagIndex.Val = (short)(lag - MIN_LAG_8KHZ);
contourIndex.Val = (sbyte)CBimax;
}
Inlines.OpusAssert(lagIndex.Val >= 0);
/* return as voiced */
return 0;
}
/***********************************************************************
* Calculates the correlations used in stage 3 search. In order to cover
* the whole lag codebook for all the searched offset lags (lag +- 2),
* the following correlations are needed in each sub frame:
*
* sf1: lag range [-8,...,7] total 16 correlations
* sf2: lag range [-4,...,4] total 9 correlations
* sf3: lag range [-3,....4] total 8 correltions
* sf4: lag range [-6,....8] total 15 correlations
*
* In total 48 correlations. The direct implementation computed in worst
* case 4*12*5 = 240 correlations, but more likely around 120.
***********************************************************************/
private static void silk_P_Ana_calc_corr_st3(
silk_pe_stage3_vals[] cross_corr_st3, /* O 3 DIM correlation array */
short[] frame, /* I vector to correlate */
int start_lag, /* I lag offset to search around */
int sf_length, /* I length of a 5 ms subframe */
int nb_subfr, /* I number of subframes */
int complexity /* I Complexity setting */
)
{
int target_ptr;
int i, j, k, lag_counter, lag_low, lag_high;
int nb_cbk_search, delta, idx;
int[] scratch_mem;
int[] xcorr32;
sbyte[][] Lag_range_ptr;
sbyte[][] Lag_CB_ptr;
Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX);
Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX);
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_range_ptr = Tables.silk_Lag_range_stage3[complexity];
Lag_CB_ptr = Tables.silk_CB_lags_stage3;
nb_cbk_search = Tables.silk_nb_cbk_searchs_stage3[complexity];
}
else {
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_range_ptr = Tables.silk_Lag_range_stage3_10_ms;
Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms;
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS;
}
scratch_mem = new int[SCRATCH_SIZE];
xcorr32 = new int[SCRATCH_SIZE];
target_ptr = Inlines.silk_LSHIFT(sf_length, 2); /* Pointer to middle of frame */
for (k = 0; k < nb_subfr; k++)
{
lag_counter = 0;
/* Calculate the correlations for each subframe */
lag_low = Lag_range_ptr[k][0];
lag_high = Lag_range_ptr[k][1];
Inlines.OpusAssert(lag_high - lag_low + 1 <= SCRATCH_SIZE);
CeltPitchXCorr.pitch_xcorr(frame, target_ptr, frame, target_ptr - start_lag - lag_high, xcorr32, sf_length, lag_high - lag_low + 1);
for (j = lag_low; j <= lag_high; j++)
{
Inlines.OpusAssert(lag_counter < SCRATCH_SIZE);
scratch_mem[lag_counter] = xcorr32[lag_high - j];
lag_counter++;
}
delta = Lag_range_ptr[k][0];
for (i = 0; i < nb_cbk_search; i++)
{
/* Fill out the 3 dim array that stores the correlations for */
/* each code_book vector for each start lag */
idx = Lag_CB_ptr[k][i] - delta;
for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++)
{
Inlines.OpusAssert(idx + j < SCRATCH_SIZE);
Inlines.OpusAssert(idx + j < lag_counter);
Inlines.MatrixGet(cross_corr_st3, k, i, nb_cbk_search).Values[j] =
scratch_mem[idx + j];
}
}
target_ptr += sf_length;
}
}
/********************************************************************/
/* Calculate the energies for first two subframes. The energies are */
/* calculated recursively. */
/********************************************************************/
static void silk_P_Ana_calc_energy_st3(
silk_pe_stage3_vals[] energies_st3, /* O 3 DIM energy array */
short[] frame, /* I vector to calc energy in */
int start_lag, /* I lag offset to search around */
int sf_length, /* I length of one 5 ms subframe */
int nb_subfr, /* I number of subframes */
int complexity /* I Complexity setting */
)
{
int target_ptr, basis_ptr;
int energy;
int k, i, j, lag_counter;
int nb_cbk_search, delta, idx, lag_diff;
int[] scratch_mem;
sbyte[][] Lag_range_ptr;
sbyte[][] Lag_CB_ptr;
Inlines.OpusAssert(complexity >= SilkConstants.SILK_PE_MIN_COMPLEX);
Inlines.OpusAssert(complexity <= SilkConstants.SILK_PE_MAX_COMPLEX);
if (nb_subfr == SilkConstants.PE_MAX_NB_SUBFR)
{
Lag_range_ptr = Tables.silk_Lag_range_stage3[complexity];
Lag_CB_ptr = Tables.silk_CB_lags_stage3;
nb_cbk_search = Tables.silk_nb_cbk_searchs_stage3[complexity];
}
else {
Inlines.OpusAssert(nb_subfr == SilkConstants.PE_MAX_NB_SUBFR >> 1);
Lag_range_ptr = Tables.silk_Lag_range_stage3_10_ms;
Lag_CB_ptr = Tables.silk_CB_lags_stage3_10_ms;
nb_cbk_search = SilkConstants.PE_NB_CBKS_STAGE3_10MS;
}
scratch_mem = new int[SCRATCH_SIZE];
target_ptr = Inlines.silk_LSHIFT(sf_length, 2);
for (k = 0; k < nb_subfr; k++)
{
lag_counter = 0;
/* Calculate the energy for first lag */
basis_ptr = target_ptr - (start_lag + Lag_range_ptr[k][0]);
energy = Inlines.silk_inner_prod_self(frame, basis_ptr, sf_length);
Inlines.OpusAssert(energy >= 0);
scratch_mem[lag_counter] = energy;
lag_counter++;
lag_diff = (Lag_range_ptr[k][1] - Lag_range_ptr[k][0] + 1);
for (i = 1; i < lag_diff; i++)
{
/* remove part outside new window */
energy -= Inlines.silk_SMULBB(frame[basis_ptr + sf_length - i], frame[basis_ptr + sf_length - i]);
Inlines.OpusAssert(energy >= 0);
/* add part that comes into window */
energy = Inlines.silk_ADD_SAT32(energy, Inlines.silk_SMULBB(frame[basis_ptr - i], frame[basis_ptr - i]));
Inlines.OpusAssert(energy >= 0);
Inlines.OpusAssert(lag_counter < SCRATCH_SIZE);
scratch_mem[lag_counter] = energy;
lag_counter++;
}
delta = Lag_range_ptr[k][0];
for (i = 0; i < nb_cbk_search; i++)
{
/* Fill out the 3 dim array that stores the correlations for */
/* each code_book vector for each start lag */
idx = Lag_CB_ptr[k][i] - delta;
for (j = 0; j < SilkConstants.PE_NB_STAGE3_LAGS; j++)
{
Inlines.OpusAssert(idx + j < SCRATCH_SIZE);
Inlines.OpusAssert(idx + j < lag_counter);
Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] = scratch_mem[idx + j];
Inlines.OpusAssert(Inlines.MatrixGet(energies_st3, k, i, nb_cbk_search).Values[j] >= 0);
}
}
target_ptr += sf_length;
}
}
}
}
@@ -0,0 +1,143 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class ProcessGains
{
/* Processing of gains */
internal static void silk_process_gains(
SilkChannelEncoder psEnc, /* I/O Encoder state */
SilkEncoderControl psEncCtrl, /* I/O Encoder control */
int condCoding /* I The type of conditional coding to use */
)
{
SilkShapeState psShapeSt = psEnc.sShape;
int k;
int s_Q16, InvMaxSqrVal_Q16, gain, gain_squared, ResNrg, ResNrgPart, quant_offset_Q10;
/* Gain reduction when LTP coding gain is high */
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
/*s = -0.5f * silk_sigmoid( 0.25f * ( psEncCtrl.LTPredCodGain - 12.0f ) ); */
s_Q16 = 0 - Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT_ROUND(psEncCtrl.LTPredCodGain_Q7 - ((int)((12.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(12.0f, 7)*/, 4));
for (k = 0; k < psEnc.nb_subfr; k++)
{
psEncCtrl.Gains_Q16[k] = Inlines.silk_SMLAWB(psEncCtrl.Gains_Q16[k], psEncCtrl.Gains_Q16[k], s_Q16);
}
}
/* Limit the quantized signal */
/* InvMaxSqrVal = pow( 2.0f, 0.33f * ( 21.0f - SNR_dB ) ) / subfr_length; */
InvMaxSqrVal_Q16 = Inlines.silk_DIV32_16(Inlines.silk_log2lin(
Inlines.silk_SMULWB(((int)((21 + 16 / 0.33f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(21 + 16 / 0.33f, 7)*/ - psEnc.SNR_dB_Q7, ((int)((0.33f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.33f, 16)*/)), psEnc.subfr_length);
for (k = 0; k < psEnc.nb_subfr; k++)
{
/* Soft limit on ratio residual energy and squared gains */
ResNrg = psEncCtrl.ResNrg[k];
ResNrgPart = Inlines.silk_SMULWW(ResNrg, InvMaxSqrVal_Q16);
if (psEncCtrl.ResNrgQ[k] > 0)
{
ResNrgPart = Inlines.silk_RSHIFT_ROUND(ResNrgPart, psEncCtrl.ResNrgQ[k]);
}
else {
if (ResNrgPart >= Inlines.silk_RSHIFT(int.MaxValue, -psEncCtrl.ResNrgQ[k]))
{
ResNrgPart = int.MaxValue;
}
else {
ResNrgPart = Inlines.silk_LSHIFT(ResNrgPart, -psEncCtrl.ResNrgQ[k]);
}
}
gain = psEncCtrl.Gains_Q16[k];
gain_squared = Inlines.silk_ADD_SAT32(ResNrgPart, Inlines.silk_SMMUL(gain, gain));
if (gain_squared < short.MaxValue)
{
/* recalculate with higher precision */
gain_squared = Inlines.silk_SMLAWW(Inlines.silk_LSHIFT(ResNrgPart, 16), gain, gain);
Inlines.OpusAssert(gain_squared > 0);
gain = Inlines.silk_SQRT_APPROX(gain_squared); /* Q8 */
gain = Inlines.silk_min(gain, int.MaxValue >> 8);
psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 8); /* Q16 */
}
else {
gain = Inlines.silk_SQRT_APPROX(gain_squared); /* Q0 */
gain = Inlines.silk_min(gain, int.MaxValue >> 16);
psEncCtrl.Gains_Q16[k] = Inlines.silk_LSHIFT_SAT32(gain, 16); /* Q16 */
}
}
/* Save unquantized gains and gain Index */
Array.Copy(psEncCtrl.Gains_Q16, psEncCtrl.GainsUnq_Q16, psEnc.nb_subfr);
psEncCtrl.lastGainIndexPrev = psShapeSt.LastGainIndex;
/* Quantize gains */
BoxedValueSbyte boxed_lastGainIndex = new BoxedValueSbyte(psShapeSt.LastGainIndex);
GainQuantization.silk_gains_quant(psEnc.indices.GainsIndices, psEncCtrl.Gains_Q16,
boxed_lastGainIndex, condCoding == SilkConstants.CODE_CONDITIONALLY ? 1 : 0, psEnc.nb_subfr);
psShapeSt.LastGainIndex = boxed_lastGainIndex.Val;
/* Set quantizer offset for voiced signals. Larger offset when LTP coding gain is low or tilt is high (ie low-pass) */
if (psEnc.indices.signalType == SilkConstants.TYPE_VOICED)
{
if (psEncCtrl.LTPredCodGain_Q7 + Inlines.silk_RSHIFT(psEnc.input_tilt_Q15, 8) > ((int)((1.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(1.0f, 7)*/)
{
psEnc.indices.quantOffsetType = 0;
}
else {
psEnc.indices.quantOffsetType = 1;
}
}
/* Quantizer boundary adjustment */
quant_offset_Q10 = Tables.silk_Quantization_Offsets_Q10[psEnc.indices.signalType >> 1][psEnc.indices.quantOffsetType];
psEncCtrl.Lambda_Q10 = ((int)((TuningParameters.LAMBDA_OFFSET) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_OFFSET, 10)*/
+ Inlines.silk_SMULBB(((int)((TuningParameters.LAMBDA_DELAYED_DECISIONS) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_DELAYED_DECISIONS, 10)*/, psEnc.nStatesDelayedDecision)
+ Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_SPEECH_ACT) * ((long)1 << (18)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_SPEECH_ACT, 18)*/, psEnc.speech_activity_Q8)
+ Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_INPUT_QUALITY) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_INPUT_QUALITY, 12)*/, psEncCtrl.input_quality_Q14)
+ Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_CODING_QUALITY) * ((long)1 << (12)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_CODING_QUALITY, 12)*/, psEncCtrl.coding_quality_Q14)
+ Inlines.silk_SMULWB(((int)((TuningParameters.LAMBDA_QUANT_OFFSET) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.LAMBDA_QUANT_OFFSET, 16)*/, quant_offset_Q10);
Inlines.OpusAssert(psEncCtrl.Lambda_Q10 > 0);
Inlines.OpusAssert(psEncCtrl.Lambda_Q10 < ((int)((2) * ((long)1 << (10)) + 0.5))/*Inlines.SILK_CONST(2, 10)*/);
}
}
}
@@ -0,0 +1,153 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class QuantizeLTPGains
{
internal static void silk_quant_LTP_gains(
short[] B_Q14, /* I/O (un)quantized LTP gains [MAX_NB_SUBFR * LTP_ORDER] */
sbyte[] cbk_index, /* O Codebook Index [MAX_NB_SUBFR] */
BoxedValueSbyte periodicity_index, /* O Periodicity Index */
BoxedValueInt sum_log_gain_Q7, /* I/O Cumulative max prediction gain */
int[] W_Q18, /* I Error Weights in Q18 [MAX_NB_SUBFR * LTP_ORDER * LTP_ORDER] */
int mu_Q9, /* I Mu value (R/D tradeoff) */
int lowComplexity, /* I Flag for low complexity */
int nb_subfr /* I number of subframes */
)
{
int j, k, cbk_size;
sbyte[] temp_idx = new sbyte[SilkConstants.MAX_NB_SUBFR];
byte[] cl_ptr_Q5;
sbyte[][] cbk_ptr_Q7;
byte[] cbk_gain_ptr_Q7;
int b_Q14_ptr;
int W_Q18_ptr;
int rate_dist_Q14_subfr, rate_dist_Q14, min_rate_dist_Q14;
int sum_log_gain_tmp_Q7, best_sum_log_gain_Q7, max_gain_Q7, gain_Q7;
/***************************************************/
/* iterate over different codebooks with different */
/* rates/distortions, and choose best */
/***************************************************/
min_rate_dist_Q14 = int.MaxValue;
best_sum_log_gain_Q7 = 0;
for (k = 0; k < 3; k++)
{
/* Safety margin for pitch gain control, to take into account factors
such as state rescaling/rewhitening. */
int gain_safety = ((int)((0.4f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(0.4f, 7)*/;
cl_ptr_Q5 = Tables.silk_LTP_gain_BITS_Q5_ptrs[k];
cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[k];
cbk_gain_ptr_Q7 = Tables.silk_LTP_vq_gain_ptrs_Q7[k];
cbk_size = Tables.silk_LTP_vq_sizes[k];
/* Set up pointer to first subframe */
W_Q18_ptr = 0;
b_Q14_ptr = 0;
rate_dist_Q14 = 0;
sum_log_gain_tmp_Q7 = sum_log_gain_Q7.Val;
for (j = 0; j < nb_subfr; j++)
{
max_gain_Q7 = Inlines.silk_log2lin((((int)((TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.MAX_SUM_LOG_GAIN_DB / 6.0f, 7)*/ - sum_log_gain_tmp_Q7)
+ ((int)((7) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/) - gain_safety;
BoxedValueSbyte temp_idx_box = new BoxedValueSbyte(temp_idx[j]);
BoxedValueInt rate_dist_Q14_subfr_box = new BoxedValueInt();
BoxedValueInt gain_Q7_box = new BoxedValueInt();
VQ_WMat_EC.silk_VQ_WMat_EC(
temp_idx_box, /* O index of best codebook vector */
rate_dist_Q14_subfr_box, /* O best weighted quantization error + mu * rate */
gain_Q7_box, /* O sum of absolute LTP coefficients */
B_Q14,
b_Q14_ptr, /* I input vector to be quantized */
W_Q18,
W_Q18_ptr, /* I weighting matrix */
cbk_ptr_Q7, /* I codebook */
cbk_gain_ptr_Q7, /* I codebook effective gains */
cl_ptr_Q5, /* I code length for each codebook vector */
mu_Q9, /* I tradeoff between weighted error and rate */
max_gain_Q7, /* I maximum sum of absolute LTP coefficients */
cbk_size /* I number of vectors in codebook */
);
rate_dist_Q14_subfr = rate_dist_Q14_subfr_box.Val;
gain_Q7 = gain_Q7_box.Val;
temp_idx[j] = temp_idx_box.Val;
rate_dist_Q14 = Inlines.silk_ADD_POS_SAT32(rate_dist_Q14, rate_dist_Q14_subfr);
sum_log_gain_tmp_Q7 = Inlines.silk_max(0, sum_log_gain_tmp_Q7
+ Inlines.silk_lin2log(gain_safety + gain_Q7) - ((int)((7) * ((long)1 << (7)) + 0.5))/*Inlines.SILK_CONST(7, 7)*/);
b_Q14_ptr += SilkConstants.LTP_ORDER;
W_Q18_ptr += SilkConstants.LTP_ORDER * SilkConstants.LTP_ORDER;
}
/* Avoid never finding a codebook */
rate_dist_Q14 = Inlines.silk_min(int.MaxValue - 1, rate_dist_Q14);
if (rate_dist_Q14 < min_rate_dist_Q14)
{
min_rate_dist_Q14 = rate_dist_Q14;
periodicity_index.Val = (sbyte)k;
Array.Copy(temp_idx, 0, cbk_index, 0, nb_subfr);
best_sum_log_gain_Q7 = sum_log_gain_tmp_Q7;
}
/* Break early in low-complexity mode if rate distortion is below threshold */
if (lowComplexity != 0 && (rate_dist_Q14 < Tables.silk_LTP_gain_middle_avg_RD_Q14))
{
break;
}
}
cbk_ptr_Q7 = Tables.silk_LTP_vq_ptrs_Q7[periodicity_index.Val];
for (j = 0; j < nb_subfr; j++)
{
for (k = 0; k < SilkConstants.LTP_ORDER; k++)
{
B_Q14[j * SilkConstants.LTP_ORDER + k] = (short)(Inlines.silk_LSHIFT(cbk_ptr_Q7[cbk_index[j]][k], 7));
}
}
sum_log_gain_Q7.Val = best_sum_log_gain_Q7;
}
}
}
@@ -0,0 +1,61 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class RegularizeCorrelations
{
/* Add noise to matrix diagonal */
internal static void silk_regularize_correlations(
int[] XX, /* I/O Correlation matrices */
int XX_ptr,
int[] xx, /* I/O Correlation values */
int xx_ptr,
int noise, /* I Noise to add */
int D /* I Dimension of XX */
)
{
int i;
for (i = 0; i < D; i++)
{
Inlines.MatrixSet(XX, XX_ptr, i, i, D, Inlines.silk_ADD32(Inlines.MatrixGet(XX, XX_ptr, i, i, D), noise));
}
xx[xx_ptr] += noise;
}
}
}
@@ -0,0 +1,744 @@
/* 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.
*/
namespace Concentus.Silk
{
// fixme: merge with resampler state
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
/*
* Matrix of resampling methods used:
* Fs_out (kHz)
* 8 12 16 24 48
*
* 8 C UF U UF UF
* 12 AF C UF U UF
* Fs_in (kHz) 16 D AF C UF UF
* 24 AF D AF C U
* 48 AF AF AF D C
*
* C . Copy (no resampling)
* D . Allpass-based 2x downsampling
* U . Allpass-based 2x upsampling
* UF . Allpass-based 2x upsampling followed by FIR interpolation
* AF . AR2 filter followed by FIR interpolation
*/
internal static class Resampler
{
private const int USE_silk_resampler_copy = 0;
private const int USE_silk_resampler_private_up2_HQ_wrapper = 1;
private const int USE_silk_resampler_private_IIR_FIR = 2;
private const int USE_silk_resampler_private_down_FIR = 3;
private const int ORDER_FIR = 4;
/// <summary>
/// Simple way to make [8000, 12000, 16000, 24000, 48000] to [0, 1, 2, 3, 4]
/// </summary>
/// <param name="R"></param>
/// <returns></returns>
private static int rateID(int R)
{
return (((((R) >> 12) - ((R > 16000) ? 1 : 0)) >> ((R > 24000) ? 1 : 0)) - 1);
}
/// <summary>
/// Initialize/reset the resampler state for a given pair of input/output sampling rates
/// </summary>
/// <param name="S">I/O Resampler state</param>
/// <param name="Fs_Hz_in">I Input sampling rate (Hz)</param>
/// <param name="Fs_Hz_out">I Output sampling rate (Hz)</param>
/// <param name="forEnc">I If 1: encoder; if 0: decoder</param>
/// <returns></returns>
internal static int silk_resampler_init(
SilkResamplerState S,
int Fs_Hz_in,
int Fs_Hz_out,
int forEnc)
{
int up2x;
/* Clear state */
S.Reset();
/* Input checking */
if (forEnc != 0)
{
if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000 && Fs_Hz_in != 24000 && Fs_Hz_in != 48000) ||
(Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000))
{
Inlines.OpusAssert(false);
return -1;
}
S.inputDelay = Tables.delay_matrix_enc[rateID(Fs_Hz_in),rateID(Fs_Hz_out)];
}
else {
if ((Fs_Hz_in != 8000 && Fs_Hz_in != 12000 && Fs_Hz_in != 16000) ||
(Fs_Hz_out != 8000 && Fs_Hz_out != 12000 && Fs_Hz_out != 16000 && Fs_Hz_out != 24000 && Fs_Hz_out != 48000))
{
Inlines.OpusAssert(false);
return -1;
}
S.inputDelay = Tables.delay_matrix_dec[rateID(Fs_Hz_in),rateID(Fs_Hz_out)];
}
S.Fs_in_kHz = Inlines.silk_DIV32_16(Fs_Hz_in, 1000);
S.Fs_out_kHz = Inlines.silk_DIV32_16(Fs_Hz_out, 1000);
/* Number of samples processed per batch */
S.batchSize = S.Fs_in_kHz * SilkConstants.RESAMPLER_MAX_BATCH_SIZE_MS;
/* Find resampler with the right sampling ratio */
up2x = 0;
if (Fs_Hz_out > Fs_Hz_in)
{
/* Upsample */
if (Fs_Hz_out == Inlines.silk_MUL(Fs_Hz_in, 2))
{ /* Fs_out : Fs_in = 2 : 1 */
/* Special case: directly use 2x upsampler */
S.resampler_function = USE_silk_resampler_private_up2_HQ_wrapper;
}
else {
/* Default resampler */
S.resampler_function = USE_silk_resampler_private_IIR_FIR;
up2x = 1;
}
}
else if (Fs_Hz_out < Fs_Hz_in)
{
/* Downsample */
S.resampler_function = USE_silk_resampler_private_down_FIR;
if (Inlines.silk_MUL(Fs_Hz_out, 4) == Inlines.silk_MUL(Fs_Hz_in, 3))
{ /* Fs_out : Fs_in = 3 : 4 */
S.FIR_Fracs = 3;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0;
S.Coefs = Tables.silk_Resampler_3_4_COEFS;
}
else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Inlines.silk_MUL(Fs_Hz_in, 2))
{ /* Fs_out : Fs_in = 2 : 3 */
S.FIR_Fracs = 2;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR0;
S.Coefs = Tables.silk_Resampler_2_3_COEFS;
}
else if (Inlines.silk_MUL(Fs_Hz_out, 2) == Fs_Hz_in)
{ /* Fs_out : Fs_in = 1 : 2 */
S.FIR_Fracs = 1;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR1;
S.Coefs = Tables.silk_Resampler_1_2_COEFS;
}
else if (Inlines.silk_MUL(Fs_Hz_out, 3) == Fs_Hz_in)
{ /* Fs_out : Fs_in = 1 : 3 */
S.FIR_Fracs = 1;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2;
S.Coefs = Tables.silk_Resampler_1_3_COEFS;
}
else if (Inlines.silk_MUL(Fs_Hz_out, 4) == Fs_Hz_in)
{ /* Fs_out : Fs_in = 1 : 4 */
S.FIR_Fracs = 1;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2;
S.Coefs = Tables.silk_Resampler_1_4_COEFS;
}
else if (Inlines.silk_MUL(Fs_Hz_out, 6) == Fs_Hz_in)
{ /* Fs_out : Fs_in = 1 : 6 */
S.FIR_Fracs = 1;
S.FIR_Order = SilkConstants.RESAMPLER_DOWN_ORDER_FIR2;
S.Coefs = Tables.silk_Resampler_1_6_COEFS;
}
else
{
/* None available */
Inlines.OpusAssert(false);
return -1;
}
}
else
{
/* Input and output sampling rates are equal: copy */
S.resampler_function = USE_silk_resampler_copy;
}
/* Ratio of input/output samples */
S.invRatio_Q16 = Inlines.silk_LSHIFT32(Inlines.silk_DIV32(Inlines.silk_LSHIFT32(Fs_Hz_in, 14 + up2x), Fs_Hz_out), 2);
/* Make sure the ratio is rounded up */
while (Inlines.silk_SMULWW(S.invRatio_Q16, Fs_Hz_out) < Inlines.silk_LSHIFT32(Fs_Hz_in, up2x))
{
S.invRatio_Q16++;
}
return 0;
}
/// <summary>
/// Resampler: convert from one sampling rate to another
/// Input and output sampling rate are at most 48000 Hz
/// </summary>
/// <param name="S">I/O Resampler state</param>
/// <param name="output">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="inLen">I Number of input samples</param>
/// <returns></returns>
internal static int silk_resampler(
SilkResamplerState S,
short[] output,
int output_ptr,
short[] input,
int input_ptr,
int inLen)
{
int nSamples;
/* Need at least 1 ms of input data */
Inlines.OpusAssert(inLen >= S.Fs_in_kHz);
/* Delay can't exceed the 1 ms of buffering */
Inlines.OpusAssert(S.inputDelay <= S.Fs_in_kHz);
nSamples = S.Fs_in_kHz - S.inputDelay;
short[] delayBufPtr = S.delayBuf;
/* Copy to delay buffer */
Array.Copy(input, input_ptr, delayBufPtr, S.inputDelay, nSamples);
switch (S.resampler_function)
{
case USE_silk_resampler_private_up2_HQ_wrapper:
silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz);
silk_resampler_private_up2_HQ(S.sIIR, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz);
break;
case USE_silk_resampler_private_IIR_FIR:
silk_resampler_private_IIR_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz);
silk_resampler_private_IIR_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz);
break;
case USE_silk_resampler_private_down_FIR:
silk_resampler_private_down_FIR(S, output, output_ptr, delayBufPtr, 0, S.Fs_in_kHz);
silk_resampler_private_down_FIR(S, output, output_ptr + S.Fs_out_kHz, input, input_ptr + nSamples, inLen - S.Fs_in_kHz);
break;
default:
Array.Copy(delayBufPtr, 0, output, output_ptr, S.Fs_in_kHz);
Array.Copy(input, input_ptr + nSamples, output, output_ptr + S.Fs_out_kHz, inLen - S.Fs_in_kHz);
break;
}
/* Copy to delay buffer */
Array.Copy(input, input_ptr + inLen - S.inputDelay, delayBufPtr, 0, S.inputDelay);
return SilkError.SILK_NO_ERROR;
}
/// <summary>
/// Downsample by a factor 2
/// </summary>
/// <param name="S">I/O State vector [ 2 ]</param>
/// <param name="output">O Output signal [ floor(len/2) ]</param>
/// <param name="input">I Input signal [ len ]</param>
/// <param name="inLen">I Number of input samples</param>
internal static void silk_resampler_down2(
int[] S,
short[] output,
short[] input,
int inLen)
{
int k, len2 = Inlines.silk_RSHIFT32(inLen, 1);
int in32, out32, Y, X;
Inlines.OpusAssert(Tables.silk_resampler_down2_0 > 0);
Inlines.OpusAssert(Tables.silk_resampler_down2_1 < 0);
/* Internal variables and state are in Q10 format */
for (k = 0; k < len2; k++)
{
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[2 * k], 10);
/* All-pass section for even input sample */
Y = Inlines.silk_SUB32(in32, S[0]);
X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_down2_1);
out32 = Inlines.silk_ADD32(S[0], X);
S[0] = Inlines.silk_ADD32(in32, X);
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[2 * k + 1], 10);
/* All-pass section for odd input sample, and add to output of previous section */
Y = Inlines.silk_SUB32(in32, S[1]);
X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_down2_0);
out32 = Inlines.silk_ADD32(out32, S[1]);
out32 = Inlines.silk_ADD32(out32, X);
S[1] = Inlines.silk_ADD32(in32, X);
/* Add, convert back to int16 and store to output */
output[k] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32, 11));
}
}
/// <summary>
/// Downsample by a factor 2/3, low quality
/// </summary>
/// <param name="S">I/O State vector [ 6 ]</param>
/// <param name="output">O Output signal [ floor(2*inLen/3) ]</param>
/// <param name="input">I Input signal [ inLen ]</param>
/// <param name="inLen">I Number of input samples</param>
internal static void silk_resampler_down2_3(
int[] S,
short[] output,
short[] input,
int inLen)
{
int nSamplesIn, counter, res_Q6;
int[] buf = new int[SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN + ORDER_FIR];
int buf_ptr;
int input_ptr = 0;
int output_ptr = 0;
/* Copy buffered samples to start of buffer */
Array.Copy(S, 0, buf, 0, ORDER_FIR);
/* Iterate over blocks of frameSizeIn input samples */
while (true)
{
nSamplesIn = Inlines.silk_min(inLen, SilkConstants.RESAMPLER_MAX_BATCH_SIZE_IN);
/* Second-order AR filter (output in Q8) */
silk_resampler_private_AR2(S, ORDER_FIR, buf, ORDER_FIR, input, input_ptr,
Tables.silk_Resampler_2_3_COEFS_LQ, nSamplesIn);
/* Interpolate filtered signal */
buf_ptr = 0;
counter = nSamplesIn;
while (counter > 2)
{
/* Inner product */
res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr], Tables.silk_Resampler_2_3_COEFS_LQ[2]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], Tables.silk_Resampler_2_3_COEFS_LQ[3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], Tables.silk_Resampler_2_3_COEFS_LQ[5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], Tables.silk_Resampler_2_3_COEFS_LQ[4]);
/* Scale down, saturate and store in output array */
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6));
res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 1], Tables.silk_Resampler_2_3_COEFS_LQ[4]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], Tables.silk_Resampler_2_3_COEFS_LQ[5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], Tables.silk_Resampler_2_3_COEFS_LQ[3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], Tables.silk_Resampler_2_3_COEFS_LQ[2]);
/* Scale down, saturate and store in output array */
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6));
buf_ptr += 3;
counter -= 3;
}
input_ptr += nSamplesIn;
inLen -= nSamplesIn;
if (inLen > 0)
{
/* More iterations to do; copy last part of filtered signal to beginning of buffer */
Array.Copy(buf, nSamplesIn, buf, 0, ORDER_FIR);
}
else
{
break;
}
}
/* Copy last part of filtered signal to the state for the next call */
Array.Copy(buf, nSamplesIn, S, 0, ORDER_FIR);
}
/// <summary>
/// Second order AR filter with single delay elements
/// </summary>
/// <param name="S">I/O State vector [ 2 ]</param>
/// <param name="out_Q8">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="A_Q14">I AR coefficients, Q14</param>
/// <param name="len">I Signal length</param>
internal static void silk_resampler_private_AR2(
int[] S,
int S_ptr,
int[] out_Q8,
int out_Q8_ptr,
short[] input,
int input_ptr,
short[] A_Q14,
int len)
{
int k, out32;
for (k = 0; k < len; k++)
{
out32 = Inlines.silk_ADD_LSHIFT32(S[S_ptr], (int)input[input_ptr + k], 8);
out_Q8[out_Q8_ptr + k] = out32;
out32 = Inlines.silk_LSHIFT(out32, 2);
S[S_ptr] = Inlines.silk_SMLAWB(S[S_ptr + 1], out32, A_Q14[0]);
S[S_ptr + 1] = Inlines.silk_SMULWB(out32, A_Q14[1]);
}
}
internal static int silk_resampler_private_down_FIR_INTERPOL(
short[] output,
int output_ptr,
int[] buf,
short[] FIR_Coefs,
int FIR_Coefs_ptr,
int FIR_Order,
int FIR_Fracs,
int max_index_Q16,
int index_increment_Q16)
{
int index_Q16, res_Q6;
int buf_ptr;
int interpol_ind;
int interpol_ptr;
switch (FIR_Order)
{
case SilkConstants.RESAMPLER_DOWN_ORDER_FIR0:
for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16)
{
/* Integer part gives pointer to buffered input */
buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16);
/* Fractional part gives interpolation coefficients */
interpol_ind = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, FIR_Fracs);
/* Inner product */
interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * interpol_ind);
res_Q6 = Inlines.silk_SMULWB(buf[buf_ptr + 0], FIR_Coefs[interpol_ptr + 0]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 1], FIR_Coefs[interpol_ptr + 1]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 2], FIR_Coefs[interpol_ptr + 2]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 3], FIR_Coefs[interpol_ptr + 3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 4], FIR_Coefs[interpol_ptr + 4]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 5], FIR_Coefs[interpol_ptr + 5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 6], FIR_Coefs[interpol_ptr + 6]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 7], FIR_Coefs[interpol_ptr + 7]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 8], FIR_Coefs[interpol_ptr + 8]);
interpol_ptr = FIR_Coefs_ptr + (SilkConstants.RESAMPLER_DOWN_ORDER_FIR0 / 2 * (FIR_Fracs - 1 - interpol_ind));
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 17], FIR_Coefs[interpol_ptr + 0]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 16], FIR_Coefs[interpol_ptr + 1]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 15], FIR_Coefs[interpol_ptr + 2]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 14], FIR_Coefs[interpol_ptr + 3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 13], FIR_Coefs[interpol_ptr + 4]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 12], FIR_Coefs[interpol_ptr + 5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 11], FIR_Coefs[interpol_ptr + 6]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 10], FIR_Coefs[interpol_ptr + 7]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, buf[buf_ptr + 9], FIR_Coefs[interpol_ptr + 8]);
/* Scale down, saturate and store in output array */
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6));
}
break;
case SilkConstants.RESAMPLER_DOWN_ORDER_FIR1:
for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16)
{
/* Integer part gives pointer to buffered input */
buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16);
/* Inner product */
res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 0]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 1]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 2]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 4]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 17]), FIR_Coefs[FIR_Coefs_ptr + 6]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 16]), FIR_Coefs[FIR_Coefs_ptr + 7]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 15]), FIR_Coefs[FIR_Coefs_ptr + 8]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 14]), FIR_Coefs[FIR_Coefs_ptr + 9]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 13]), FIR_Coefs[FIR_Coefs_ptr + 10]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 12]), FIR_Coefs[FIR_Coefs_ptr + 11]);
/* Scale down, saturate and store in output array */
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6));
}
break;
case SilkConstants.RESAMPLER_DOWN_ORDER_FIR2:
for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16)
{
/* Integer part gives pointer to buffered input */
buf_ptr = Inlines.silk_RSHIFT(index_Q16, 16);
/* Inner product */
res_Q6 = Inlines.silk_SMULWB(Inlines.silk_ADD32(buf[buf_ptr + 0], buf[buf_ptr + 35]), FIR_Coefs[FIR_Coefs_ptr + 0]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 1], buf[buf_ptr + 34]), FIR_Coefs[FIR_Coefs_ptr + 1]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 2], buf[buf_ptr + 33]), FIR_Coefs[FIR_Coefs_ptr + 2]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 3], buf[buf_ptr + 32]), FIR_Coefs[FIR_Coefs_ptr + 3]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 4], buf[buf_ptr + 31]), FIR_Coefs[FIR_Coefs_ptr + 4]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 5], buf[buf_ptr + 30]), FIR_Coefs[FIR_Coefs_ptr + 5]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 6], buf[buf_ptr + 29]), FIR_Coefs[FIR_Coefs_ptr + 6]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 7], buf[buf_ptr + 28]), FIR_Coefs[FIR_Coefs_ptr + 7]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 8], buf[buf_ptr + 27]), FIR_Coefs[FIR_Coefs_ptr + 8]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 9], buf[buf_ptr + 26]), FIR_Coefs[FIR_Coefs_ptr + 9]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 10], buf[buf_ptr + 25]), FIR_Coefs[FIR_Coefs_ptr + 10]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 11], buf[buf_ptr + 24]), FIR_Coefs[FIR_Coefs_ptr + 11]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 12], buf[buf_ptr + 23]), FIR_Coefs[FIR_Coefs_ptr + 12]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 13], buf[buf_ptr + 22]), FIR_Coefs[FIR_Coefs_ptr + 13]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 14], buf[buf_ptr + 21]), FIR_Coefs[FIR_Coefs_ptr + 14]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 15], buf[buf_ptr + 20]), FIR_Coefs[FIR_Coefs_ptr + 15]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 16], buf[buf_ptr + 19]), FIR_Coefs[FIR_Coefs_ptr + 16]);
res_Q6 = Inlines.silk_SMLAWB(res_Q6, Inlines.silk_ADD32(buf[buf_ptr + 17], buf[buf_ptr + 18]), FIR_Coefs[FIR_Coefs_ptr + 17]);
/* Scale down, saturate and store in output array */
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q6, 6));
}
break;
default:
Inlines.OpusAssert(false);
break;
}
return output_ptr;
}
/// <summary>
/// Resample with a 2nd order AR filter followed by FIR interpolation
/// </summary>
/// <param name="S">I/O Resampler state</param>
/// <param name="output">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="inLen">I Number of input samples</param>
internal static void silk_resampler_private_down_FIR(
SilkResamplerState S,
short[] output,
int output_ptr,
short[] input,
int input_ptr,
int inLen)
{
int nSamplesIn;
int max_index_Q16, index_increment_Q16;
int[] buf = new int[S.batchSize + S.FIR_Order];
/* Copy buffered samples to start of buffer */
Array.Copy(S.sFIR_i32, buf, S.FIR_Order);
/* Iterate over blocks of frameSizeIn input samples */
index_increment_Q16 = S.invRatio_Q16;
while (true)
{
nSamplesIn = Inlines.silk_min(inLen, S.batchSize);
/* Second-order AR filter (output in Q8) */
silk_resampler_private_AR2(S.sIIR, 0, buf, S.FIR_Order, input, input_ptr, S.Coefs, nSamplesIn);
max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16);
/* Interpolate filtered signal */
output_ptr = silk_resampler_private_down_FIR_INTERPOL(output, output_ptr, buf, S.Coefs, 2, S.FIR_Order,
S.FIR_Fracs, max_index_Q16, index_increment_Q16);
input_ptr += nSamplesIn;
inLen -= nSamplesIn;
if (inLen > 1)
{
/* More iterations to do; copy last part of filtered signal to beginning of buffer */
Array.Copy(buf, nSamplesIn, buf, 0, S.FIR_Order);
}
else
{
break;
}
}
/* Copy last part of filtered signal to the state for the next call */
Array.Copy(buf, nSamplesIn, S.sFIR_i32, 0, S.FIR_Order);
}
internal static int silk_resampler_private_IIR_FIR_INTERPOL(
short[] output,
int output_ptr,
short[] buf,
int max_index_Q16,
int index_increment_Q16)
{
int index_Q16, res_Q15;
int buf_ptr;
int table_index;
/* Interpolate upsampled signal and store in output array */
for (index_Q16 = 0; index_Q16 < max_index_Q16; index_Q16 += index_increment_Q16)
{
table_index = Inlines.silk_SMULWB(index_Q16 & 0xFFFF, 12);
buf_ptr = index_Q16 >> 16;
res_Q15 = Inlines.silk_SMULBB(buf[buf_ptr], Tables.silk_resampler_frac_FIR_12[table_index, 0]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 1], Tables.silk_resampler_frac_FIR_12[table_index, 1]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 2], Tables.silk_resampler_frac_FIR_12[table_index, 2]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 3], Tables.silk_resampler_frac_FIR_12[table_index, 3]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 4], Tables.silk_resampler_frac_FIR_12[11 - table_index, 3]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 5], Tables.silk_resampler_frac_FIR_12[11 - table_index, 2]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 6], Tables.silk_resampler_frac_FIR_12[11 - table_index, 1]);
res_Q15 = Inlines.silk_SMLABB(res_Q15, buf[buf_ptr + 7], Tables.silk_resampler_frac_FIR_12[11 - table_index, 0]);
output[output_ptr++] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(res_Q15, 15));
}
return output_ptr;
}
/// <summary>
/// Upsample using a combination of allpass-based 2x upsampling and FIR interpolation
/// </summary>
/// <param name="S">I/O Resampler state</param>
/// <param name="output">O Output signal</param>
/// <param name="input">I Input signal</param>
/// <param name="inLen">I Number of input samples</param>
internal static void silk_resampler_private_IIR_FIR(
SilkResamplerState S,
short[] output,
int output_ptr,
short[] input,
int input_ptr,
int inLen)
{
int nSamplesIn;
int max_index_Q16, index_increment_Q16;
short[] buf = new short[2 * S.batchSize + SilkConstants.RESAMPLER_ORDER_FIR_12];
/* Copy buffered samples to start of buffer */
Array.Copy(S.sFIR_i16, 0, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12);
/* Iterate over blocks of frameSizeIn input samples */
index_increment_Q16 = S.invRatio_Q16;
while (true)
{
nSamplesIn = Inlines.silk_min(inLen, S.batchSize);
/* Upsample 2x */
silk_resampler_private_up2_HQ(S.sIIR, buf, SilkConstants.RESAMPLER_ORDER_FIR_12, input, input_ptr, nSamplesIn);
max_index_Q16 = Inlines.silk_LSHIFT32(nSamplesIn, 16 + 1); /* + 1 because 2x upsampling */
output_ptr = silk_resampler_private_IIR_FIR_INTERPOL(output, output_ptr, buf, max_index_Q16, index_increment_Q16);
input_ptr += nSamplesIn;
inLen -= nSamplesIn;
if (inLen > 0)
{
/* More iterations to do; copy last part of filtered signal to beginning of buffer */
Array.Copy(buf, nSamplesIn << 1, buf, 0, SilkConstants.RESAMPLER_ORDER_FIR_12);
}
else
{
break;
}
}
/* Copy last part of filtered signal to the state for the next call */
Array.Copy(buf, nSamplesIn << 1, S.sFIR_i16, 0, SilkConstants.RESAMPLER_ORDER_FIR_12);
}
/// <summary>
/// Upsample by a factor 2, high quality
/// Uses 2nd order allpass filters for the 2x upsampling, followed by a
/// notch filter just above Nyquist.
/// </summary>
/// <param name="S">I/O Resampler state [ 6 ]</param>
/// <param name="output">O Output signal [ 2 * len ]</param>
/// <param name="input">I Input signal [ len ]</param>
/// <param name="len">I Number of input samples</param>
internal static void silk_resampler_private_up2_HQ(
int[] S,
short[] output,
int output_ptr,
short[] input,
int input_ptr,
int len)
{
int k;
int in32, out32_1, out32_2, Y, X;
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[0] > 0);
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[1] > 0);
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_0[2] < 0);
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[0] > 0);
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[1] > 0);
Inlines.OpusAssert(Tables.silk_resampler_up2_hq_1[2] < 0);
/* Internal variables and state are in Q10 format */
for (k = 0; k < len; k++)
{
/* Convert to Q10 */
in32 = Inlines.silk_LSHIFT((int)input[input_ptr + k], 10);
/* First all-pass section for even output sample */
Y = Inlines.silk_SUB32(in32, S[0]);
X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_0[0]);
out32_1 = Inlines.silk_ADD32(S[0], X);
S[0] = Inlines.silk_ADD32(in32, X);
/* Second all-pass section for even output sample */
Y = Inlines.silk_SUB32(out32_1, S[1]);
X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_0[1]);
out32_2 = Inlines.silk_ADD32(S[1], X);
S[1] = Inlines.silk_ADD32(out32_1, X);
/* Third all-pass section for even output sample */
Y = Inlines.silk_SUB32(out32_2, S[2]);
X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_up2_hq_0[2]);
out32_1 = Inlines.silk_ADD32(S[2], X);
S[2] = Inlines.silk_ADD32(out32_2, X);
/* Apply gain in Q15, convert back to int16 and store to output */
output[output_ptr + (2 * k)] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10));
/* First all-pass section for odd output sample */
Y = Inlines.silk_SUB32(in32, S[3]);
X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_1[0]);
out32_1 = Inlines.silk_ADD32(S[3], X);
S[3] = Inlines.silk_ADD32(in32, X);
/* Second all-pass section for odd output sample */
Y = Inlines.silk_SUB32(out32_1, S[4]);
X = Inlines.silk_SMULWB(Y, Tables.silk_resampler_up2_hq_1[1]);
out32_2 = Inlines.silk_ADD32(S[4], X);
S[4] = Inlines.silk_ADD32(out32_1, X);
/* Third all-pass section for odd output sample */
Y = Inlines.silk_SUB32(out32_2, S[5]);
X = Inlines.silk_SMLAWB(Y, Y, Tables.silk_resampler_up2_hq_1[2]);
out32_1 = Inlines.silk_ADD32(S[5], X);
S[5] = Inlines.silk_ADD32(out32_2, X);
/* Apply gain in Q15, convert back to int16 and store to output */
output[output_ptr + (2 * k) + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(out32_1, 10));
}
}
}
}
@@ -0,0 +1,193 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class ResidualEnergy
{
/* Calculates residual energies of input subframes where all subframes have LPC_order */
/* of preceding samples */
internal static void silk_residual_energy(
int[] nrgs, /* O Residual energy per subframe [MAX_NB_SUBFR] */
int[] nrgsQ, /* O Q value per subframe [MAX_NB_SUBFR] */
short[] x, /* I Input signal */
short[][] a_Q12, /* I AR coefs for each frame half [2][MAX_LPC_ORDER] */
int[] gains, /* I Quantization gains [SilkConstants.MAX_NB_SUBFR] */
int subfr_length, /* I Subframe length */
int nb_subfr, /* I Number of subframes */
int LPC_order /* I LPC order */
)
{
int offset, i, j, lz1, lz2;
int rshift, energy;
int LPC_res_ptr;
short[] LPC_res;
int x_ptr;
int tmp32;
x_ptr = 0;
offset = LPC_order + subfr_length;
/* Filter input to create the LPC residual for each frame half, and measure subframe energies */
LPC_res = new short[(SilkConstants.MAX_NB_SUBFR >> 1) * offset];
Inlines.OpusAssert((nb_subfr >> 1) * (SilkConstants.MAX_NB_SUBFR >> 1) == nb_subfr);
for (i = 0; i < nb_subfr >> 1; i++)
{
/* Calculate half frame LPC residual signal including preceding samples */
Filters.silk_LPC_analysis_filter(LPC_res, 0, x, x_ptr, a_Q12[i], 0, (SilkConstants.MAX_NB_SUBFR >> 1) * offset, LPC_order);
/* Point to first subframe of the just calculated LPC residual signal */
LPC_res_ptr = LPC_order;
for (j = 0; j < (SilkConstants.MAX_NB_SUBFR >> 1); j++)
{
/* Measure subframe energy */
SumSqrShift.silk_sum_sqr_shift(out energy, out rshift, LPC_res, LPC_res_ptr, subfr_length);
nrgs[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = energy;
/* Set Q values for the measured energy */
nrgsQ[i * (SilkConstants.MAX_NB_SUBFR >> 1) + j] = 0 - rshift;
/* Move to next subframe */
LPC_res_ptr += offset;
}
/* Move to next frame half */
x_ptr += (SilkConstants.MAX_NB_SUBFR >> 1) * offset;
}
/* Apply the squared subframe gains */
for (i = 0; i < nb_subfr; i++)
{
/* Fully upscale gains and energies */
lz1 = Inlines.silk_CLZ32(nrgs[i]) - 1;
lz2 = Inlines.silk_CLZ32(gains[i]) - 1;
tmp32 = Inlines.silk_LSHIFT32(gains[i], lz2);
/* Find squared gains */
tmp32 = Inlines.silk_SMMUL(tmp32, tmp32); /* Q( 2 * lz2 - 32 )*/
/* Scale energies */
nrgs[i] = Inlines.silk_SMMUL(tmp32, Inlines.silk_LSHIFT32(nrgs[i], lz1)); /* Q( nrgsQ[ i ] + lz1 + 2 * lz2 - 32 - 32 )*/
nrgsQ[i] += lz1 + 2 * lz2 - 32 - 32;
}
}
/* Residual energy: nrg = wxx - 2 * wXx * c + c' * wXX * c */
internal static int silk_residual_energy16_covar(
short[] c, /* I Prediction vector */
int c_ptr,
int[] wXX, /* I Correlation matrix */
int wXX_ptr,
int[] wXx, /* I Correlation vector */
int wxx, /* I Signal energy */
int D, /* I Dimension */
int cQ /* I Q value for c vector 0 - 15 */
)
{
int i, j, lshifts, Qxtra;
int c_max, w_max, tmp, tmp2, nrg;
int[] cn = new int[D]; //SilkConstants.MAX_MATRIX_SIZE
int pRow;
/* Safety checks */
Inlines.OpusAssert(D >= 0);
Inlines.OpusAssert(D <= 16);
Inlines.OpusAssert(cQ > 0);
Inlines.OpusAssert(cQ < 16);
lshifts = 16 - cQ;
Qxtra = lshifts;
c_max = 0;
for (i = c_ptr; i < c_ptr + D; i++)
{
c_max = Inlines.silk_max_32(c_max, Inlines.silk_abs((int)c[i]));
}
Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(c_max) - 17);
w_max = Inlines.silk_max_32(wXX[wXX_ptr], wXX[wXX_ptr + (D * D) - 1]);
Qxtra = Inlines.silk_min_int(Qxtra, Inlines.silk_CLZ32(Inlines.silk_MUL(D, Inlines.silk_RSHIFT(Inlines.silk_SMULWB(w_max, c_max), 4))) - 5);
Qxtra = Inlines.silk_max_int(Qxtra, 0);
for (i = 0; i < D; i++)
{
cn[i] = Inlines.silk_LSHIFT((int)c[c_ptr + i], Qxtra);
Inlines.OpusAssert(Inlines.silk_abs(cn[i]) <= (short.MaxValue + 1)); /* Check that Inlines.silk_SMLAWB can be used */
}
lshifts -= Qxtra;
/* Compute wxx - 2 * wXx * c */
tmp = 0;
for (i = 0; i < D; i++)
{
tmp = Inlines.silk_SMLAWB(tmp, wXx[i], cn[i]);
}
nrg = Inlines.silk_RSHIFT(wxx, 1 + lshifts) - tmp; /* Q: -lshifts - 1 */
/* Add c' * wXX * c, assuming wXX is symmetric */
tmp2 = 0;
for (i = 0; i < D; i++)
{
tmp = 0;
pRow = wXX_ptr + (i * D);
for (j = i + 1; j < D; j++)
{
tmp = Inlines.silk_SMLAWB(tmp, wXX[pRow + j], cn[j]);
}
tmp = Inlines.silk_SMLAWB(tmp, Inlines.silk_RSHIFT(wXX[pRow + i], 1), cn[i]);
tmp2 = Inlines.silk_SMLAWB(tmp2, tmp, cn[i]);
}
nrg = Inlines.silk_ADD_LSHIFT32(nrg, tmp2, lshifts); /* Q: -lshifts - 1 */
/* Keep one bit free always, because we add them for LSF interpolation */
if (nrg < 1)
{
nrg = 1;
}
else if (nrg > Inlines.silk_RSHIFT(int.MaxValue, lshifts + 2))
{
nrg = int.MaxValue >> 1;
}
else {
nrg = Inlines.silk_LSHIFT(nrg, lshifts + 1); /* Q0 */
}
return nrg;
}
}
}
@@ -0,0 +1,198 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class Schur
{
/* Faster than schur64(), but much less accurate. */
/* uses SMLAWB(), requiring armv5E and higher. */
internal static int silk_schur( /* O Returns residual energy */
short[] rc_Q15, /* O reflection coefficients [order] Q15 */
int[] c, /* I correlations [order+1] */
int order /* I prediction order */
)
{
int k, n, lz;
int[][] C = Arrays.InitTwoDimensionalArray<int>(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2);
int Ctmp1, Ctmp2, rc_tmp_Q15;
Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16);
/* Get number of leading zeros */
lz = Inlines.silk_CLZ32(c[0]);
/* Copy correlations and adjust level to Q30 */
if (lz < 2)
{
/* lz must be 1, so shift one to the right */
for (k = 0; k < order + 1; k++)
{
C[k][0] = C[k][1] = Inlines.silk_RSHIFT(c[k], 1);
}
}
else if (lz > 2)
{
/* Shift to the left */
lz -= 2;
for (k = 0; k < order + 1; k++)
{
C[k][0] = C[k][1] = Inlines.silk_LSHIFT(c[k], lz);
}
}
else {
/* No need to shift */
for (k = 0; k < order + 1; k++)
{
C[k][0] = C[k][1] = c[k];
}
}
for (k = 0; k < order; k++)
{
/* Check that we won't be getting an unstable rc, otherwise stop here. */
if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1])
{
if (C[k + 1][0] > 0)
{
rc_Q15[k] = (short)(0 - ((int)((.99f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/);
}
else {
rc_Q15[k] = (short)(((int)((.99f) * ((long)1 << (15)) + 0.5))/*Inlines.SILK_CONST(.99f, 15)*/);
}
k++;
break;
}
/* Get reflection coefficient */
rc_tmp_Q15 = 0 - Inlines.silk_DIV32_16(C[k + 1][0], Inlines.silk_max_32(Inlines.silk_RSHIFT(C[0][1], 15), 1));
/* Clip (shouldn't happen for properly conditioned inputs) */
rc_tmp_Q15 = Inlines.silk_SAT16(rc_tmp_Q15);
/* Store */
rc_Q15[k] = (short)rc_tmp_Q15;
/* Update correlations */
for (n = 0; n < order - k; n++)
{
Ctmp1 = C[n + k + 1][0];
Ctmp2 = C[n][1];
C[n + k + 1][0] = Inlines.silk_SMLAWB(Ctmp1, Inlines.silk_LSHIFT(Ctmp2, 1), rc_tmp_Q15);
C[n][1] = Inlines.silk_SMLAWB(Ctmp2, Inlines.silk_LSHIFT(Ctmp1, 1), rc_tmp_Q15);
}
}
for (; k < order; k++)
{
rc_Q15[k] = 0;
}
/* return residual energy */
return Inlines.silk_max_32(1, C[0][1]);
}
/* Slower than schur(), but more accurate. */
/* Uses SMULL(), available on armv4 */
internal static int silk_schur64( /* O returns residual energy */
int[] rc_Q16, /* O Reflection coefficients [order] Q16 */
int[] c, /* I Correlations [order+1] */
int order /* I Prediction order */
)
{
int k, n;
int[][] C = Arrays.InitTwoDimensionalArray<int>(SilkConstants.SILK_MAX_ORDER_LPC + 1, 2);
int Ctmp1_Q30, Ctmp2_Q30, rc_tmp_Q31;
Inlines.OpusAssert(order == 6 || order == 8 || order == 10 || order == 12 || order == 14 || order == 16);
/* Check for invalid input */
if (c[0] <= 0)
{
Arrays.MemSetInt(rc_Q16, 0, order);
return 0;
}
for (k = 0; k < order + 1; k++)
{
C[k][0] = C[k][1] = c[k];
}
for (k = 0; k < order; k++)
{
/* Check that we won't be getting an unstable rc, otherwise stop here. */
if (Inlines.silk_abs_int32(C[k + 1][0]) >= C[0][1])
{
if (C[k + 1][0] > 0)
{
rc_Q16[k] = -((int)((.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/;
}
else {
rc_Q16[k] = ((int)((.99f) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(.99f, 16)*/;
}
k++;
break;
}
/* Get reflection coefficient: divide two Q30 values and get result in Q31 */
rc_tmp_Q31 = Inlines.silk_DIV32_varQ(-C[k + 1][0], C[0][1], 31);
/* Save the output */
rc_Q16[k] = Inlines.silk_RSHIFT_ROUND(rc_tmp_Q31, 15);
/* Update correlations */
for (n = 0; n < order - k; n++)
{
Ctmp1_Q30 = C[n + k + 1][0];
Ctmp2_Q30 = C[n][1];
/* Multiply and add the highest int32 */
C[n + k + 1][0] = Ctmp1_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp2_Q30, 1), rc_tmp_Q31);
C[n][1] = Ctmp2_Q30 + Inlines.silk_SMMUL(Inlines.silk_LSHIFT(Ctmp1_Q30, 1), rc_tmp_Q31);
}
}
for (; k < order; k++)
{
rc_Q16[k] = 0;
}
return Inlines.silk_max_32(1, C[0][1]);
}
}
}
@@ -0,0 +1,206 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
/// <summary>
/// shell coder; pulse-subframe length is hardcoded
/// </summary>
internal static class ShellCoder
{
/// <summary>
/// </summary>
/// <param name="output">O combined pulses vector [len]</param>
/// <param name="input">I input vector [2 * len]</param>
/// <param name="len">I number of OUTPUT samples</param>
internal static void combine_pulses(
int[] output,
int[] input,
int input_ptr,
int len)
{
int k;
for (k = 0; k < len; k++)
{
output[k] = input[input_ptr + (2 * k)] + input[input_ptr + (2 * k) + 1];
}
}
/// <summary>
/// </summary>
/// <param name="output">O combined pulses vector [len]</param>
/// <param name="input">I input vector [2 * len]</param>
/// <param name="len">I number of OUTPUT samples</param>
internal static void combine_pulses(
int[] output,
int[] input,
int len)
{
int k;
for (k = 0; k < len; k++)
{
output[k] = input[2 * k] + input[2 * k + 1];
}
}
internal static void encode_split(
EntropyCoder psRangeEnc, /* I/O compressor data structure */
int p_child1, /* I pulse amplitude of first child subframe */
int p, /* I pulse amplitude of current subframe */
byte[] shell_table /* I table of shell cdfs */
)
{
if (p > 0)
{
psRangeEnc.enc_icdf( p_child1, shell_table, Tables.silk_shell_code_table_offsets[p], 8);
}
}
/// <summary>
///
/// </summary>
/// <param name="p_child1">O pulse amplitude of first child subframe</param>
/// <param name="p_child2">O pulse amplitude of second child subframe</param>
/// <param name="psRangeDec">I/O Compressor data structure</param>
/// <param name="p">I pulse amplitude of current subframe</param>
/// <param name="shell_table">I table of shell cdfs</param>
internal static void decode_split(
short[] p_child1,
int child1_ptr,
short[] p_child2,
int p_child2_ptr,
EntropyCoder psRangeDec,
int p,
byte[] shell_table)
{
if (p > 0)
{
p_child1[child1_ptr] = (short)(psRangeDec.dec_icdf(shell_table, (Tables.silk_shell_code_table_offsets[p]), 8));
p_child2[p_child2_ptr] = (short)(p - p_child1[child1_ptr]);
}
else
{
p_child1[child1_ptr] = 0;
p_child2[p_child2_ptr] = 0;
}
}
/// <summary>
/// Shell encoder, operates on one shell code frame of 16 pulses
/// </summary>
/// <param name="psRangeEnc">I/O compressor data structure</param>
/// <param name="pulses0">I data: nonnegative pulse amplitudes</param>
internal static void silk_shell_encoder(EntropyCoder psRangeEnc, int[] pulses0, int pulses0_ptr)
{
int[] pulses1 = new int[8];
int[] pulses2 = new int[4];
int[] pulses3 = new int[2];
int[] pulses4 = new int[1];
/* this function operates on one shell code frame of 16 pulses */
Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16);
/* tree representation per pulse-subframe */
combine_pulses(pulses1, pulses0, pulses0_ptr, 8);
combine_pulses(pulses2, pulses1, 4);
combine_pulses(pulses3, pulses2, 2);
combine_pulses(pulses4, pulses3, 1);
encode_split(psRangeEnc, pulses3[0], pulses4[0], Tables.silk_shell_code_table3);
encode_split(psRangeEnc, pulses2[0], pulses3[0], Tables.silk_shell_code_table2);
encode_split(psRangeEnc, pulses1[0], pulses2[0], Tables.silk_shell_code_table1);
encode_split(psRangeEnc, pulses0[pulses0_ptr], pulses1[0], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 2], pulses1[1], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses1[2], pulses2[1], Tables.silk_shell_code_table1);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 4], pulses1[2], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 6], pulses1[3], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses2[2], pulses3[1], Tables.silk_shell_code_table2);
encode_split(psRangeEnc, pulses1[4], pulses2[2], Tables.silk_shell_code_table1);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 8], pulses1[4], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 10], pulses1[5], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses1[6], pulses2[3], Tables.silk_shell_code_table1);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 12], pulses1[6], Tables.silk_shell_code_table0);
encode_split(psRangeEnc, pulses0[pulses0_ptr + 14], pulses1[7], Tables.silk_shell_code_table0);
}
/* Shell decoder, operates on one shell code frame of 16 pulses */
internal static void silk_shell_decoder(
short[] pulses0, /* O data: nonnegative pulse amplitudes */
int pulses0_ptr,
EntropyCoder psRangeDec, /* I/O Compressor data structure */
int pulses4 /* I number of pulses per pulse-subframe */
)
{
short[] pulses1 = new short[8];
short[] pulses2 = new short[4];
short[] pulses3 = new short[2];
/* this function operates on one shell code frame of 16 pulses */
Inlines.OpusAssert(SilkConstants.SHELL_CODEC_FRAME_LENGTH == 16);
decode_split(pulses3, 0, pulses3, 1, psRangeDec, pulses4, Tables.silk_shell_code_table3);
decode_split(pulses2, 0, pulses2, 1, psRangeDec, pulses3[0], Tables.silk_shell_code_table2);
decode_split(pulses1, 0, pulses1 ,1, psRangeDec, pulses2[0], Tables.silk_shell_code_table1);
decode_split(pulses0, pulses0_ptr, pulses0, pulses0_ptr + 1, psRangeDec, pulses1[0], Tables.silk_shell_code_table0);
decode_split(pulses0, pulses0_ptr + 2, pulses0, pulses0_ptr + 3, psRangeDec, pulses1[1], Tables.silk_shell_code_table0);
decode_split(pulses1, 2, pulses1, 3, psRangeDec, pulses2[1], Tables.silk_shell_code_table1);
decode_split(pulses0, pulses0_ptr + 4, pulses0, pulses0_ptr + 5, psRangeDec, pulses1[2], Tables.silk_shell_code_table0);
decode_split(pulses0, pulses0_ptr + 6, pulses0, pulses0_ptr + 7, psRangeDec, pulses1[3], Tables.silk_shell_code_table0);
decode_split(pulses2, 2, pulses2, 3, psRangeDec, pulses3[1], Tables.silk_shell_code_table2);
decode_split(pulses1, 4, pulses1 ,5, psRangeDec, pulses2[2], Tables.silk_shell_code_table1);
decode_split(pulses0, pulses0_ptr + 8, pulses0, pulses0_ptr + 9, psRangeDec, pulses1[4], Tables.silk_shell_code_table0);
decode_split(pulses0, pulses0_ptr + 10, pulses0, pulses0_ptr + 11, psRangeDec, pulses1[5], Tables.silk_shell_code_table0);
decode_split(pulses1, 6, pulses1, 7, psRangeDec, pulses2[3], Tables.silk_shell_code_table1);
decode_split(pulses0, pulses0_ptr + 12, pulses0, pulses0_ptr + 13, psRangeDec, pulses1[6], Tables.silk_shell_code_table0);
decode_split(pulses0, pulses0_ptr + 14, pulses0, pulses0_ptr + 15, psRangeDec, pulses1[7], Tables.silk_shell_code_table0);
}
}
}
@@ -0,0 +1,93 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
/// <summary>
/// Approximate sigmoid function
/// </summary>
internal static class Sigmoid
{
private static readonly int[] sigm_LUT_slope_Q10 = {
237, 153, 73, 30, 12, 7
};
private static readonly int[] sigm_LUT_pos_Q15 = {
16384, 23955, 28861, 31213, 32178, 32548
};
private static readonly int[] sigm_LUT_neg_Q15 = {
16384, 8812, 3906, 1554, 589, 219
};
internal static int silk_sigm_Q15(int in_Q5)
{
int ind;
if (in_Q5 < 0)
{
/* Negative input */
in_Q5 = -in_Q5;
if (in_Q5 >= 6 * 32)
{
return 0; /* Clip */
}
else
{
/* Linear interpolation of look up table */
ind = Inlines.silk_RSHIFT(in_Q5, 5);
return (sigm_LUT_neg_Q15[ind] - Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F));
}
}
else
{
/* Positive input */
if (in_Q5 >= 6 * 32)
{
return 32767; /* clip */
}
else
{
/* Linear interpolation of look up table */
ind = Inlines.silk_RSHIFT(in_Q5, 5);
return (sigm_LUT_pos_Q15[ind] + Inlines.silk_SMULBB(sigm_LUT_slope_Q10[ind], in_Q5 & 0x1F));
}
}
}
}
}
@@ -0,0 +1,311 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class SilkConstants
{
/* Max number of encoder channels (1/2) */
public const int ENCODER_NUM_CHANNELS = 2;
/* Number of decoder channels (1/2) */
public const int DECODER_NUM_CHANNELS = 2;
public const int MAX_FRAMES_PER_PACKET = 3;
/* Limits on bitrate */
public const int MIN_TARGET_RATE_BPS = 5000;
public const int MAX_TARGET_RATE_BPS = 80000;
public const int TARGET_RATE_TAB_SZ = 8;
/* LBRR thresholds */
public const int LBRR_NB_MIN_RATE_BPS = 12000;
public const int LBRR_MB_MIN_RATE_BPS = 14000;
public const int LBRR_WB_MIN_RATE_BPS = 16000;
/* DTX settings */
public const int NB_SPEECH_FRAMES_BEFORE_DTX = 10; /* eq 200 ms */
public const int MAX_CONSECUTIVE_DTX = 20; /* eq 400 ms */
/* Maximum sampling frequency */
public const int MAX_FS_KHZ = 16;
public const int MAX_API_FS_KHZ = 48;
// FIXME can use enums here
/* Signal types */
public const int TYPE_NO_VOICE_ACTIVITY = 0;
public const int TYPE_UNVOICED = 1;
public const int TYPE_VOICED = 2;
/* Conditional coding types */
public const int CODE_INDEPENDENTLY = 0;
public const int CODE_INDEPENDENTLY_NO_LTP_SCALING = 1;
public const int CODE_CONDITIONALLY = 2;
/* Settings for stereo processing */
public const int STEREO_QUANT_TAB_SIZE = 16;
public const int STEREO_QUANT_SUB_STEPS = 5;
public const int STEREO_INTERP_LEN_MS = 8; /* must be even */
public const float STEREO_RATIO_SMOOTH_COEF = 0.01f; /* smoothing coef for signal norms and stereo width */
/* Range of pitch lag estimates */
public const int PITCH_EST_MIN_LAG_MS = 2; /* 2 ms . 500 Hz */
public const int PITCH_EST_MAX_LAG_MS = 18; /* 18 ms . 56 Hz */
/* Maximum number of subframes */
public const int MAX_NB_SUBFR = 4;
/* Number of samples per frame */
public const int LTP_MEM_LENGTH_MS = 20;
public const int SUB_FRAME_LENGTH_MS = 5;
public const int MAX_SUB_FRAME_LENGTH = (SUB_FRAME_LENGTH_MS * MAX_FS_KHZ);
public const int MAX_FRAME_LENGTH_MS = (SUB_FRAME_LENGTH_MS * MAX_NB_SUBFR);
public const int MAX_FRAME_LENGTH = (MAX_FRAME_LENGTH_MS * MAX_FS_KHZ);
/* Milliseconds of lookahead for pitch analysis */
public const int LA_PITCH_MS = 2;
public const int LA_PITCH_MAX = (LA_PITCH_MS * MAX_FS_KHZ);
/* Order of LPC used in find pitch */
public const int MAX_FIND_PITCH_LPC_ORDER = 16;
/* Length of LPC window used in find pitch */
public const int FIND_PITCH_LPC_WIN_MS = (20 + (LA_PITCH_MS << 1));
public const int FIND_PITCH_LPC_WIN_MS_2_SF = (10 + (LA_PITCH_MS << 1));
public const int FIND_PITCH_LPC_WIN_MAX = (FIND_PITCH_LPC_WIN_MS * MAX_FS_KHZ);
/* Milliseconds of lookahead for noise shape analysis */
public const int LA_SHAPE_MS = 5;
public const int LA_SHAPE_MAX = (LA_SHAPE_MS * MAX_FS_KHZ);
/* Maximum length of LPC window used in noise shape analysis */
public const int SHAPE_LPC_WIN_MAX = (15 * MAX_FS_KHZ);
/* dB level of lowest gain quantization level */
public const int MIN_QGAIN_DB = 2;
/* dB level of highest gain quantization level */
public const int MAX_QGAIN_DB = 88;
/* Number of gain quantization levels */
public const int N_LEVELS_QGAIN = 64;
/* Max increase in gain quantization index */
public const int MAX_DELTA_GAIN_QUANT = 36;
/* Max decrease in gain quantization index */
public const int MIN_DELTA_GAIN_QUANT = -4;
/* Quantization offsets (multiples of 4) */
public const short OFFSET_VL_Q10 = 32;
public const short OFFSET_VH_Q10 = 100;
public const short OFFSET_UVL_Q10 = 100;
public const short OFFSET_UVH_Q10 = 240;
public const int QUANT_LEVEL_ADJUST_Q10 = 80;
/* Maximum numbers of iterations used to stabilize an LPC vector */
public const int MAX_LPC_STABILIZE_ITERATIONS = 16;
public const float MAX_PREDICTION_POWER_GAIN = 1e4f;
public const float MAX_PREDICTION_POWER_GAIN_AFTER_RESET = 1e2f;
public const int SILK_MAX_ORDER_LPC = 16;
public const int MAX_LPC_ORDER = 16;
public const int MIN_LPC_ORDER = 10;
/* Find Pred Coef defines */
public const int LTP_ORDER = 5;
/* LTP quantization settings */
public const int NB_LTP_CBKS = 3;
/* Flag to use harmonic noise shaping */
public const int USE_HARM_SHAPING = 1;
/* Max LPC order of noise shaping filters */
public const int MAX_SHAPE_LPC_ORDER = 16;
public const int HARM_SHAPE_FIR_TAPS = 3;
/* Maximum number of delayed decision states */
public const int MAX_DEL_DEC_STATES = 4;
public const int LTP_BUF_LENGTH = 512;
public const int LTP_MASK = (LTP_BUF_LENGTH - 1);
public const int DECISION_DELAY = 32;
public const int DECISION_DELAY_MASK = (DECISION_DELAY - 1);
/* Number of subframes for excitation entropy coding */
public const int SHELL_CODEC_FRAME_LENGTH = 16;
public const int LOG2_SHELL_CODEC_FRAME_LENGTH = 4;
public const int MAX_NB_SHELL_BLOCKS = (MAX_FRAME_LENGTH / SHELL_CODEC_FRAME_LENGTH);
/* Number of rate levels, for entropy coding of excitation */
public const int N_RATE_LEVELS = 10;
/* Maximum sum of pulses per shell coding frame */
public const int SILK_MAX_PULSES = 16;
public const int MAX_MATRIX_SIZE = MAX_LPC_ORDER; /* Max of LPC Order and LTP order */
internal static readonly int NSQ_LPC_BUF_LENGTH = Math.Max(MAX_LPC_ORDER, DECISION_DELAY);
/***************************/
/* Voice activity detector */
/***************************/
public const int VAD_N_BANDS = 4;
public const int VAD_INTERNAL_SUBFRAMES_LOG2 = 2;
public const int VAD_INTERNAL_SUBFRAMES = (1 << VAD_INTERNAL_SUBFRAMES_LOG2);
public const int VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 = 1024; /* Must be < 4096 */
public const int VAD_NOISE_LEVELS_BIAS = 50;
/* Sigmoid settings */
public const int VAD_NEGATIVE_OFFSET_Q5 = 128; /* sigmoid is 0 at -128 */
public const int VAD_SNR_FACTOR_Q16 = 45000;
/* smoothing for SNR measurement */
public const int VAD_SNR_SMOOTH_COEF_Q18 = 4096;
/* Size of the piecewise linear cosine approximation table for the LSFs */
public const int LSF_COS_TAB_SZ = 128;
/******************/
/* NLSF quantizer */
/******************/
public const int NLSF_W_Q = 2;
public const int NLSF_VQ_MAX_VECTORS = 32;
public const int NLSF_VQ_MAX_SURVIVORS = 32;
public const int NLSF_QUANT_MAX_AMPLITUDE = 4;
public const int NLSF_QUANT_MAX_AMPLITUDE_EXT = 10;
public const float NLSF_QUANT_LEVEL_ADJ = 0.1f;
public const int NLSF_QUANT_DEL_DEC_STATES_LOG2 = 2;
public const int NLSF_QUANT_DEL_DEC_STATES = (1 << NLSF_QUANT_DEL_DEC_STATES_LOG2);
/* Transition filtering for mode switching */
public const int TRANSITION_TIME_MS = 5120; /* 5120 = 64 * FRAME_LENGTH_MS * ( TRANSITION_INT_NUM - 1 ) = 64*(20*4)*/
public const int TRANSITION_NB = 3; /* Hardcoded in tables */
public const int TRANSITION_NA = 2; /* Hardcoded in tables */
public const int TRANSITION_INT_NUM = 5; /* Hardcoded in tables */
public const int TRANSITION_FRAMES = (TRANSITION_TIME_MS / MAX_FRAME_LENGTH_MS);
public const int TRANSITION_INT_STEPS = (TRANSITION_FRAMES / (TRANSITION_INT_NUM - 1));
/* BWE factors to apply after packet loss */
public const int BWE_AFTER_LOSS_Q16 = 63570;
/* Defines for CN generation */
public const int CNG_BUF_MASK_MAX = 255; /* 2^floor(log2(MAX_FRAME_LENGTH))-1 */
public const int CNG_GAIN_SMTH_Q16 = 4634; /* 0.25^(1/4) */
public const int CNG_NLSF_SMTH_Q16 = 16348; /* 0.25 */
/********************************************************/
/* Definitions for pitch estimator (from pitch_est_defines.h) */
/********************************************************/
public const int PE_MAX_FS_KHZ = 16; /* Maximum sampling frequency used */
public const int PE_MAX_NB_SUBFR = 4;
public const int PE_SUBFR_LENGTH_MS = 5; /* 5 ms */
public const int PE_LTP_MEM_LENGTH_MS = (4 * PE_SUBFR_LENGTH_MS);
public const int PE_MAX_FRAME_LENGTH_MS = (PE_LTP_MEM_LENGTH_MS + PE_MAX_NB_SUBFR * PE_SUBFR_LENGTH_MS);
public const int PE_MAX_FRAME_LENGTH = (PE_MAX_FRAME_LENGTH_MS * PE_MAX_FS_KHZ );
public const int PE_MAX_FRAME_LENGTH_ST_1 = (PE_MAX_FRAME_LENGTH >> 2);
public const int PE_MAX_FRAME_LENGTH_ST_2 = (PE_MAX_FRAME_LENGTH >> 1);
public const int PE_MAX_LAG_MS = 18; /* 18 ms . 56 Hz */
public const int PE_MIN_LAG_MS = 2; /* 2 ms . 500 Hz */
public const int PE_MAX_LAG = (PE_MAX_LAG_MS * PE_MAX_FS_KHZ);
public const int PE_MIN_LAG = (PE_MIN_LAG_MS * PE_MAX_FS_KHZ);
public const int PE_D_SRCH_LENGTH = 24;
public const int PE_NB_STAGE3_LAGS = 5;
public const int PE_NB_CBKS_STAGE2 = 3;
public const int PE_NB_CBKS_STAGE2_EXT = 11;
public const int PE_NB_CBKS_STAGE3_MAX = 34;
public const int PE_NB_CBKS_STAGE3_MID = 24;
public const int PE_NB_CBKS_STAGE3_MIN = 16;
public const int PE_NB_CBKS_STAGE3_10MS = 12;
public const int PE_NB_CBKS_STAGE2_10MS = 3;
public const float PE_SHORTLAG_BIAS = 0.2f; /* for logarithmic weighting */
public const float PE_PREVLAG_BIAS = 0.2f; /* for logarithmic weighting */
public const float PE_FLATCONTOUR_BIAS = 0.05f;
public const int SILK_PE_MIN_COMPLEX = 0;
public const int SILK_PE_MID_COMPLEX = 1;
public const int SILK_PE_MAX_COMPLEX = 2;
// Definitions for PLC (from plc.h)
public const float BWE_COEF = 0.99f;
public const int V_PITCH_GAIN_START_MIN_Q14 = 11469; /* 0.7 in Q14 */
public const int V_PITCH_GAIN_START_MAX_Q14 = 15565; /* 0.95 in Q14 */
public const int MAX_PITCH_LAG_MS = 18;
public const int RAND_BUF_SIZE = 128;
public const int RAND_BUF_MASK = (RAND_BUF_SIZE - 1);
public const int LOG2_INV_LPC_GAIN_HIGH_THRES = 3; /* 2^3 = 8 dB LPC gain */
public const int LOG2_INV_LPC_GAIN_LOW_THRES = 8; /* 2^8 = 24 dB LPC gain */
public const int PITCH_DRIFT_FAC_Q16 = 655; /* 0.01 in Q16 */
// Definitions for resampler (from resampler_structs.h)
public const int SILK_RESAMPLER_MAX_FIR_ORDER = 36;
public const int SILK_RESAMPLER_MAX_IIR_ORDER = 6;
// from resampler_rom.h
public const int RESAMPLER_DOWN_ORDER_FIR0 = 18;
public const int RESAMPLER_DOWN_ORDER_FIR1 = 24;
public const int RESAMPLER_DOWN_ORDER_FIR2 = 36;
public const int RESAMPLER_ORDER_FIR_12 = 8;
// from resampler_private.h
/* Number of input samples to process in the inner loop */
public const int RESAMPLER_MAX_BATCH_SIZE_MS = 10;
public const int RESAMPLER_MAX_FS_KHZ = 48;
public const int RESAMPLER_MAX_BATCH_SIZE_IN = (RESAMPLER_MAX_BATCH_SIZE_MS * RESAMPLER_MAX_FS_KHZ);
// from api.h
public const int SILK_MAX_FRAMES_PER_PACKET = 3;
}
}
@@ -0,0 +1,184 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class Sort
{
/// <summary>
///
/// </summary>
/// <param name="a">(I/O) Unsorted / Sorted vector</param>
/// <param name="idx">(O) Index vector for the sorted elements</param>
/// <param name="L">(I) Vector length</param>
/// <param name="K">(I) Number of correctly sorted positions</param>
internal static void silk_insertion_sort_increasing(int[] a, int[] idx, int L, int K)
{
int value;
int i, j;
// Safety checks
Inlines.OpusAssert(K > 0);
Inlines.OpusAssert(L > 0);
Inlines.OpusAssert(L >= K);
// Write start indices in index vector
for (i = 0; i < K; i++)
{
idx[i] = i;
}
// Sort vector elements by value, increasing order
for (i = 1; i < K; i++)
{
value = a[i];
for (j = i - 1; (j >= 0) && (value < a[j]); j--)
{
a[j + 1] = a[j]; /* Shift value */
idx[j + 1] = idx[j]; /* Shift index */
}
a[j + 1] = value; /* Write value */
idx[j + 1] = i; /* Write index */
}
// If less than L values are asked for, check the remaining values,
// but only spend CPU to ensure that the K first values are correct
for (i = K; i < L; i++)
{
value = a[i];
if (value < a[K - 1])
{
for (j = K - 2; (j >= 0) && (value < a[j]); j--)
{
a[j + 1] = a[j]; /* Shift value */
idx[j + 1] = idx[j]; /* Shift index */
}
a[j + 1] = value; /* Write value */
idx[j + 1] = i; /* Write index */
}
}
}
/// <summary>
/// Insertion sort (fast for already almost sorted arrays):
/// Best case: O(n) for an already sorted array
/// Worst case: O(n^2) for an inversely sorted array
/// </summary>
/// <param name="a">(I/O) Unsorted / Sorted vector</param>
/// <param name="L">(I) Vector length</param>
internal static void silk_insertion_sort_increasing_all_values_int16(short[] a, int L)
{
// FIXME: Could just use Array.Sort(a.Array, a.Offset, L);
short value;
int i, j;
// Safety checks
Inlines.OpusAssert(L > 0);
// Sort vector elements by value, increasing order
for (i = 1; i < L; i++)
{
value = a[i];
for (j = i - 1; (j >= 0) && (value < a[j]); j--)
{
a[j + 1] = a[j]; // Shift value
}
a[j + 1] = value; // Write value
}
}
/* This function is only used by the fixed-point build */
internal static void silk_insertion_sort_decreasing_int16(
short[] a, /* I/O Unsorted / Sorted vector */
int[] idx, /* O Index vector for the sorted elements */
int L, /* I Vector length */
int K /* I Number of correctly sorted positions */
)
{
int i, j;
short value;
/* Safety checks */
Inlines.OpusAssert(K > 0);
Inlines.OpusAssert(L > 0);
Inlines.OpusAssert(L >= K);
/* Write start indices in index vector */
for (i = 0; i < K; i++)
{
idx[i] = i;
}
/* Sort vector elements by value, decreasing order */
for (i = 1; i < K; i++)
{
value = a[i];
for (j = i - 1; (j >= 0) && (value > a[j]); j--)
{
a[j + 1] = a[j]; /* Shift value */
idx[j + 1] = idx[j]; /* Shift index */
}
a[j + 1] = value; /* Write value */
idx[j + 1] = i; /* Write index */
}
/* If less than L values are asked for, check the remaining values, */
/* but only spend CPU to ensure that the K first values are correct */
for (i = K; i < L; i++)
{
value = a[i];
if (value > a[K - 1])
{
for (j = K - 2; (j >= 0) && (value > a[j]); j--)
{
a[j + 1] = a[j]; /* Shift value */
idx[j + 1] = idx[j]; /* Shift index */
}
a[j + 1] = value; /* Write value */
idx[j + 1] = i; /* Write index */
}
}
}
}
}
@@ -0,0 +1,543 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System;
using System.Diagnostics;
internal static class Stereo
{
/// <summary>
/// Decode mid/side predictors
/// </summary>
/// <param name="psRangeDec">I/O Compressor data structure</param>
/// <param name="pred_Q13">O Predictors</param>
internal static void silk_stereo_decode_pred(
EntropyCoder psRangeDec,
int[] pred_Q13)
{
int n;
int[][] ix = Arrays.InitTwoDimensionalArray<int>(2, 3);
int low_Q13, step_Q13;
// Entropy decoding
n = psRangeDec.dec_icdf(Tables.silk_stereo_pred_joint_iCDF, 8);
ix[0][2] = Inlines.silk_DIV32_16(n, 5);
ix[1][2] = n - 5 * ix[0][2];
for (n = 0; n < 2; n++)
{
ix[n][0] = psRangeDec.dec_icdf(Tables.silk_uniform3_iCDF, 8);
ix[n][1] = psRangeDec.dec_icdf(Tables.silk_uniform5_iCDF, 8);
}
// Dequantize
for (n = 0; n < 2; n++)
{
ix[n][0] += 3 * ix[n][2];
low_Q13 = Tables.silk_stereo_pred_quant_Q13[ix[n][0]];
step_Q13 = Inlines.silk_SMULWB(Tables.silk_stereo_pred_quant_Q13[ix[n][0] + 1] - low_Q13,
((int)((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/);
pred_Q13[n] = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * ix[n][1] + 1);
}
/* Subtract second from first predictor (helps when actually applying these) */
pred_Q13[0] -= pred_Q13[1];
}
/// <summary>
/// Decode mid-only flag
/// </summary>
/// <param name="psRangeDec">I/O Compressor data structure</param>
/// <param name="decode_only_mid">O Flag that only mid channel has been coded</param>
internal static void silk_stereo_decode_mid_only(
EntropyCoder psRangeDec,
BoxedValueInt decode_only_mid
)
{
/* Decode flag that only mid channel is coded */
decode_only_mid.Val = psRangeDec.dec_icdf(Tables.silk_stereo_only_code_mid_iCDF, 8);
}
/// <summary>
/// Entropy code the mid/side quantization indices
/// </summary>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="ix">I Quantization indices [ 2 ][ 3 ]</param>
internal static void silk_stereo_encode_pred(EntropyCoder psRangeEnc, sbyte[][] ix)
{
int n;
/* Entropy coding */
n = 5 * ix[0][2] + ix[1][2];
Inlines.OpusAssert(n < 25);
psRangeEnc.enc_icdf( n, Tables.silk_stereo_pred_joint_iCDF, 8);
for (n = 0; n < 2; n++)
{
Inlines.OpusAssert(ix[n][0] < 3);
Inlines.OpusAssert(ix[n][1] < SilkConstants.STEREO_QUANT_SUB_STEPS);
psRangeEnc.enc_icdf( ix[n][0], Tables.silk_uniform3_iCDF, 8);
psRangeEnc.enc_icdf( ix[n][1], Tables.silk_uniform5_iCDF, 8);
}
}
/// <summary>
/// Entropy code the mid-only flag
/// </summary>
/// <param name="psRangeEnc">I/O Compressor data structure</param>
/// <param name="mid_only_flag"></param>
internal static void silk_stereo_encode_mid_only(EntropyCoder psRangeEnc, sbyte mid_only_flag)
{
/* Encode flag that only mid channel is coded */
psRangeEnc.enc_icdf( mid_only_flag, Tables.silk_stereo_only_code_mid_iCDF, 8);
}
/// <summary>
/// Find least-squares prediction gain for one signal based on another and quantize it
/// </summary>
/// <param name="ratio_Q14">O Ratio of residual and mid energies</param>
/// <param name="x">I Basis signal</param>
/// <param name="y">I Target signal</param>
/// <param name="mid_res_amp_Q0">I/O Smoothed mid, residual norms</param>
/// <param name="length">I Number of samples</param>
/// <param name="smooth_coef_Q16">I Smoothing coefficient</param>
/// <returns>O Returns predictor in Q13</returns>
internal static int silk_stereo_find_predictor(
BoxedValueInt ratio_Q14,
short[] x,
short[] y,
int[] mid_res_amp_Q0,
int mid_res_amp_Q0_ptr,
int length,
int smooth_coef_Q16)
{
int scale;
int nrgx, nrgy, scale1, scale2;
int corr, pred_Q13, pred2_Q10;
/* Find predictor */
SumSqrShift.silk_sum_sqr_shift(out nrgx, out scale1, x, length);
SumSqrShift.silk_sum_sqr_shift(out nrgy, out scale2, y, length);
scale = Inlines.silk_max_int(scale1, scale2);
scale = scale + (scale & 1); /* make even */
nrgy = Inlines.silk_RSHIFT32(nrgy, scale - scale2);
nrgx = Inlines.silk_RSHIFT32(nrgx, scale - scale1);
nrgx = Inlines.silk_max_int(nrgx, 1);
corr = Inlines.silk_inner_prod_aligned_scale(x, y, scale, length);
pred_Q13 = Inlines.silk_DIV32_varQ(corr, nrgx, 13);
pred_Q13 = Inlines.silk_LIMIT(pred_Q13, -(1 << 14), 1 << 14);
pred2_Q10 = Inlines.silk_SMULWB(pred_Q13, pred_Q13);
/* Faster update for signals with large prediction parameters */
smooth_coef_Q16 = (int)Inlines.silk_max_int(smooth_coef_Q16, Inlines.silk_abs(pred2_Q10));
/* Smoothed mid and residual norms */
Inlines.OpusAssert(smooth_coef_Q16 < 32768);
scale = Inlines.silk_RSHIFT(scale, 1);
mid_res_amp_Q0[mid_res_amp_Q0_ptr] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr],
Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgx), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr], smooth_coef_Q16);
/* Residual energy = nrgy - 2 * pred * corr + pred^2 * nrgx */
nrgy = Inlines.silk_SUB_LSHIFT32(nrgy, Inlines.silk_SMULWB(corr, pred_Q13), 3 + 1);
nrgy = Inlines.silk_ADD_LSHIFT32(nrgy, Inlines.silk_SMULWB(nrgx, pred2_Q10), 6);
mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1] = Inlines.silk_SMLAWB(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1],
Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(nrgy), scale) - mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], smooth_coef_Q16);
/* Ratio of smoothed residual and mid norms */
ratio_Q14.Val = Inlines.silk_DIV32_varQ(mid_res_amp_Q0[mid_res_amp_Q0_ptr + 1], Inlines.silk_max(mid_res_amp_Q0[mid_res_amp_Q0_ptr], 1), 14);
ratio_Q14.Val = Inlines.silk_LIMIT(ratio_Q14.Val, 0, 32767);
return pred_Q13;
}
/// <summary>
/// Convert Left/Right stereo signal to adaptive Mid/Side representation
/// </summary>
/// <param name="state">I/O State</param>
/// <param name="x1">I/O Left input signal, becomes mid signal</param>
/// <param name="x2">I/O Right input signal, becomes side signal</param>
/// <param name="ix">O Quantization indices [ 2 ][ 3 ]</param>
/// <param name="mid_only_flag">O Flag: only mid signal coded</param>
/// <param name="mid_side_rates_bps">O Bitrates for mid and side signals</param>
/// <param name="total_rate_bps">I Total bitrate</param>
/// <param name="prev_speech_act_Q8">I Speech activity level in previous frame</param>
/// <param name="toMono">I Last frame before a stereo.mono transition</param>
/// <param name="fs_kHz">I Sample rate (kHz)</param>
/// <param name="frame_length">I Number of samples</param>
internal static void silk_stereo_LR_to_MS(
StereoEncodeState state,
short[] x1,
int x1_ptr,
short[] x2,
int x2_ptr,
sbyte[][] ix,
BoxedValueSbyte mid_only_flag,
int[] mid_side_rates_bps,
int total_rate_bps,
int prev_speech_act_Q8,
int toMono,
int fs_kHz,
int frame_length)
{
int n, is10msFrame, denom_Q16, delta0_Q13, delta1_Q13;
int sum, diff, smooth_coef_Q16, pred0_Q13, pred1_Q13;
int[] pred_Q13 = new int[2];
int frac_Q16, frac_3_Q16, min_mid_rate_bps, width_Q14, w_Q24, deltaw_Q24;
BoxedValueInt LP_ratio_Q14 = new BoxedValueInt();
BoxedValueInt HP_ratio_Q14 = new BoxedValueInt();
short[] side;
short[] LP_mid;
short[] HP_mid;
short[] LP_side;
short[] HP_side;
int mid = x1_ptr - 2;
side = new short[frame_length + 2];
/* Convert to basic mid/side signals */
for (n = 0; n < frame_length + 2; n++)
{
sum = x1[x1_ptr + n - 2] + (int)x2[x2_ptr + n - 2];
diff = x1[x1_ptr + n - 2] - (int)x2[x2_ptr + n - 2];
x1[mid + n] = (short)Inlines.silk_RSHIFT_ROUND(sum, 1);
side[n] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(diff, 1));
}
/* Buffering */
Array.Copy(state.sMid, 0, x1, mid, 2);
Array.Copy(state.sSide, side, 2);
Array.Copy(x1, mid + frame_length, state.sMid, 0, 2);
Array.Copy(side, frame_length, state.sSide, 0, 2);
/* LP and HP filter mid signal */
LP_mid = new short[frame_length];
HP_mid = new short[frame_length];
for (n = 0; n < frame_length; n++)
{
sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 2);
LP_mid[n] = (short)(sum);
HP_mid[n] = (short)(x1[mid + n + 1] - sum);
}
/* LP and HP filter side signal */
LP_side = new short[frame_length];
HP_side = new short[frame_length];
for (n = 0; n < frame_length; n++)
{
sum = Inlines.silk_RSHIFT_ROUND(Inlines.silk_ADD_LSHIFT32(side[n] + side[n + 2], side[n + 1], 1), 2);
LP_side[n] = (short)(sum);
HP_side[n] = (short)(side[n + 1] - sum);
}
/* Find energies and predictors */
is10msFrame = (frame_length == 10 * fs_kHz ? 1 : 0);
smooth_coef_Q16 = is10msFrame != 0 ?
((int)((SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF / 2, 16)*/ :
((int)((SilkConstants.STEREO_RATIO_SMOOTH_COEF) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(SilkConstants.STEREO_RATIO_SMOOTH_COEF, 16)*/;
smooth_coef_Q16 = Inlines.silk_SMULWB(Inlines.silk_SMULBB(prev_speech_act_Q8, prev_speech_act_Q8), smooth_coef_Q16);
pred_Q13[0] = silk_stereo_find_predictor(LP_ratio_Q14, LP_mid, LP_side, state.mid_side_amp_Q0, 0, frame_length, smooth_coef_Q16);
pred_Q13[1] = silk_stereo_find_predictor(HP_ratio_Q14, HP_mid, HP_side, state.mid_side_amp_Q0, 2, frame_length, smooth_coef_Q16);
/* Ratio of the norms of residual and mid signals */
frac_Q16 = Inlines.silk_SMLABB(HP_ratio_Q14.Val, LP_ratio_Q14.Val, 3);
frac_Q16 = Inlines.silk_min(frac_Q16, ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/);
/* Determine bitrate distribution between mid and side, and possibly reduce stereo width */
total_rate_bps -= is10msFrame != 0 ? 1200 : 600; /* Subtract approximate bitrate for coding stereo parameters */
if (total_rate_bps < 1)
{
total_rate_bps = 1;
}
min_mid_rate_bps = Inlines.silk_SMLABB(2000, fs_kHz, 900);
Inlines.OpusAssert(min_mid_rate_bps < 32767);
/* Default bitrate distribution: 8 parts for Mid and (5+3*frac) parts for Side. so: mid_rate = ( 8 / ( 13 + 3 * frac ) ) * total_ rate */
frac_3_Q16 = Inlines.silk_MUL(3, frac_Q16);
mid_side_rates_bps[0] = Inlines.silk_DIV32_varQ(total_rate_bps, ((int)((8 + 5) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(8 + 5, 16)*/ + frac_3_Q16, 16 + 3);
/* If Mid bitrate below minimum, reduce stereo width */
if (mid_side_rates_bps[0] < min_mid_rate_bps)
{
mid_side_rates_bps[0] = min_mid_rate_bps;
mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0];
/* width = 4 * ( 2 * side_rate - min_rate ) / ( ( 1 + 3 * frac ) * min_rate ) */
width_Q14 = Inlines.silk_DIV32_varQ(Inlines.silk_LSHIFT(mid_side_rates_bps[1], 1) - min_mid_rate_bps,
Inlines.silk_SMULWB(((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/ + frac_3_Q16, min_mid_rate_bps), 14 + 2);
width_Q14 = Inlines.silk_LIMIT(width_Q14, 0, ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/);
}
else {
mid_side_rates_bps[1] = total_rate_bps - mid_side_rates_bps[0];
width_Q14 = ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/;
}
/* Smoother */
state.smth_width_Q14 = (short)Inlines.silk_SMLAWB(state.smth_width_Q14, width_Q14 - state.smth_width_Q14, smooth_coef_Q16);
/* At very low bitrates or for inputs that are nearly amplitude panned, switch to panned-mono coding */
mid_only_flag.Val = 0;
if (toMono != 0)
{
/* Last frame before stereo.mono transition; collapse stereo width */
width_Q14 = 0;
pred_Q13[0] = 0;
pred_Q13[1] = 0;
silk_stereo_quant_pred(pred_Q13, ix);
}
else if (state.width_prev_Q14 == 0 &&
(8 * total_rate_bps < 13 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int)((0.05f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.05f, 14)*/))
{
/* Code as panned-mono; previous frame already had zero width */
/* Scale down and quantize predictors */
pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14);
pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14);
silk_stereo_quant_pred(pred_Q13, ix);
/* Collapse stereo width */
width_Q14 = 0;
pred_Q13[0] = 0;
pred_Q13[1] = 0;
mid_side_rates_bps[0] = total_rate_bps;
mid_side_rates_bps[1] = 0;
mid_only_flag.Val = 1;
}
else if (state.width_prev_Q14 != 0 &&
(8 * total_rate_bps < 11 * min_mid_rate_bps || Inlines.silk_SMULWB(frac_Q16, state.smth_width_Q14) < ((int)((0.02f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.02f, 14)*/))
{
/* Transition to zero-width stereo */
/* Scale down and quantize predictors */
pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14);
pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14);
silk_stereo_quant_pred(pred_Q13, ix);
/* Collapse stereo width */
width_Q14 = 0;
pred_Q13[0] = 0;
pred_Q13[1] = 0;
}
else if (state.smth_width_Q14 > ((int)((0.95f) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(0.95f, 14)*/)
{
/* Full-width stereo coding */
silk_stereo_quant_pred(pred_Q13, ix);
width_Q14 = ((int)((1) * ((long)1 << (14)) + 0.5))/*Inlines.SILK_CONST(1, 14)*/;
}
else
{
/* Reduced-width stereo coding; scale down and quantize predictors */
pred_Q13[0] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[0]), 14);
pred_Q13[1] = Inlines.silk_RSHIFT(Inlines.silk_SMULBB(state.smth_width_Q14, pred_Q13[1]), 14);
silk_stereo_quant_pred(pred_Q13, ix);
width_Q14 = state.smth_width_Q14;
}
/* Make sure to keep on encoding until the tapered output has been transmitted */
if (mid_only_flag.Val == 1)
{
state.silent_side_len += (short)(frame_length - SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz);
if (state.silent_side_len < SilkConstants.LA_SHAPE_MS * fs_kHz)
{
mid_only_flag.Val = 0;
}
else {
/* Limit to avoid wrapping around */
state.silent_side_len = 10000;
}
}
else {
state.silent_side_len = 0;
}
if (mid_only_flag.Val == 0 && mid_side_rates_bps[1] < 1)
{
mid_side_rates_bps[1] = 1;
mid_side_rates_bps[0] = Inlines.silk_max_int(1, total_rate_bps - mid_side_rates_bps[1]);
}
/* Interpolate predictors and subtract prediction from side channel */
pred0_Q13 = -state.pred_prev_Q13[0];
pred1_Q13 = -state.pred_prev_Q13[1];
w_Q24 = Inlines.silk_LSHIFT(state.width_prev_Q14, 10);
denom_Q16 = Inlines.silk_DIV32_16((int)1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz);
delta0_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16);
delta1_Q13 = 0 - Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16);
deltaw_Q24 = Inlines.silk_LSHIFT(Inlines.silk_SMULWB(width_Q14 - state.width_prev_Q14, denom_Q16), 10);
for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++)
{
pred0_Q13 += delta0_Q13;
pred1_Q13 += delta1_Q13;
w_Q24 += deltaw_Q24;
sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); /* Q11 */
sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); /* Q8 */
sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[mid + n + 1], 11), pred1_Q13); /* Q8 */
x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8));
}
pred0_Q13 = 0 - pred_Q13[0];
pred1_Q13 = 0 - pred_Q13[1];
w_Q24 = Inlines.silk_LSHIFT(width_Q14, 10);
for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++)
{
sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[mid + n] + x1[mid + n + 2], x1[mid + n + 1], 1), 9); /* Q11 */
sum = Inlines.silk_SMLAWB(Inlines.silk_SMULWB(w_Q24, side[n + 1]), sum, pred0_Q13); /* Q8 */
sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[mid + n + 1], 11), pred1_Q13); /* Q8 */
x2[x2_ptr + n - 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8));
}
state.pred_prev_Q13[0] = (short)pred_Q13[0];
state.pred_prev_Q13[1] = (short)pred_Q13[1];
state.width_prev_Q14 = (short)width_Q14;
}
/// <summary>
/// Convert adaptive Mid/Side representation to Left/Right stereo signal
/// </summary>
/// <param name="state">I/O State</param>
/// <param name="x1">I/O Left input signal, becomes mid signal</param>
/// <param name="x2">I/O Right input signal, becomes side signal</param>
/// <param name="pred_Q13">I Predictors</param>
/// <param name="fs_kHz">I Samples rate (kHz)</param>
/// <param name="frame_length">I Number of samples</param>
internal static void silk_stereo_MS_to_LR(
StereoDecodeState state,
short[] x1,
int x1_ptr,
short[] x2,
int x2_ptr,
int[] pred_Q13,
int fs_kHz,
int frame_length)
{
int n, denom_Q16, delta0_Q13, delta1_Q13;
int sum, diff, pred0_Q13, pred1_Q13;
/* Buffering */
Array.Copy(state.sMid, 0, x1, x1_ptr, 2);
Array.Copy(state.sSide, 0, x2, x2_ptr, 2);
Array.Copy(x1, x1_ptr + frame_length, state.sMid, 0, 2);
Array.Copy(x2, x2_ptr + frame_length, state.sSide, 0, 2);
/* Interpolate predictors and add prediction to side channel */
pred0_Q13 = state.pred_prev_Q13[0];
pred1_Q13 = state.pred_prev_Q13[1];
denom_Q16 = Inlines.silk_DIV32_16((int)1 << 16, SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz);
delta0_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[0] - state.pred_prev_Q13[0], denom_Q16), 16);
delta1_Q13 = Inlines.silk_RSHIFT_ROUND(Inlines.silk_SMULBB(pred_Q13[1] - state.pred_prev_Q13[1], denom_Q16), 16);
for (n = 0; n < SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n++)
{
pred0_Q13 += delta0_Q13;
pred1_Q13 += delta1_Q13;
sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); /* Q11 */
sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int)x2[x2_ptr + n + 1], 8), sum, pred0_Q13); /* Q8 */
sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[x1_ptr + n + 1], 11), pred1_Q13); /* Q8 */
x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8));
}
pred0_Q13 = pred_Q13[0];
pred1_Q13 = pred_Q13[1];
for (n = SilkConstants.STEREO_INTERP_LEN_MS * fs_kHz; n < frame_length; n++)
{
sum = Inlines.silk_LSHIFT(Inlines.silk_ADD_LSHIFT(x1[x1_ptr + n] + x1[x1_ptr + n + 2], x1[x1_ptr + n + 1], 1), 9); /* Q11 */
sum = Inlines.silk_SMLAWB(Inlines.silk_LSHIFT((int)x2[x2_ptr + n + 1], 8), sum, pred0_Q13); /* Q8 */
sum = Inlines.silk_SMLAWB(sum, Inlines.silk_LSHIFT((int)x1[x1_ptr + n + 1], 11), pred1_Q13); /* Q8 */
x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(Inlines.silk_RSHIFT_ROUND(sum, 8));
}
state.pred_prev_Q13[0] = (short)(pred_Q13[0]);
state.pred_prev_Q13[1] = (short)(pred_Q13[1]);
/* Convert to left/right signals */
for (n = 0; n < frame_length; n++)
{
sum = x1[x1_ptr + n + 1] + (int)x2[x2_ptr + n + 1];
diff = x1[x1_ptr + n + 1] - (int)x2[x2_ptr + n + 1];
x1[x1_ptr + n + 1] = (short)Inlines.silk_SAT16(sum);
x2[x2_ptr + n + 1] = (short)Inlines.silk_SAT16(diff);
}
}
/// <summary>
/// Quantize mid/side predictors
/// </summary>
/// <param name="pred_Q13">I/O Predictors (out: quantized)</param>
/// <param name="ix">O Quantization indices [ 2 ][ 3 ]</param>
internal static void silk_stereo_quant_pred(
int[] pred_Q13,
sbyte[][] ix)
{
sbyte i, j; // [porting note] these were originally ints
int n;
int low_Q13, step_Q13, lvl_Q13, err_min_Q13, err_Q13, quant_pred_Q13 = 0;
// FIXME: ix was formerly an out parameter that was newly allocated here
// but now it relies on the caller to initialize it
// clear ix
Arrays.MemSetSbyte(ix[0], 0, 3);
Arrays.MemSetSbyte(ix[1], 0, 3);
/* Quantize */
for (n = 0; n < 2; n++)
{
/* Brute-force search over quantization levels */
err_min_Q13 = int.MaxValue;
for (i = 0; i < SilkConstants.STEREO_QUANT_TAB_SIZE - 1; i++)
{
low_Q13 = Tables.silk_stereo_pred_quant_Q13[i];
step_Q13 = Inlines.silk_SMULWB(Tables.silk_stereo_pred_quant_Q13[i + 1] - low_Q13,
((int)((0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(0.5f / SilkConstants.STEREO_QUANT_SUB_STEPS, 16)*/);
for (j = 0; j < SilkConstants.STEREO_QUANT_SUB_STEPS; j++)
{
lvl_Q13 = Inlines.silk_SMLABB(low_Q13, step_Q13, 2 * j + 1);
err_Q13 = Inlines.silk_abs(pred_Q13[n] - lvl_Q13);
if (err_Q13 < err_min_Q13)
{
err_min_Q13 = err_Q13;
quant_pred_Q13 = lvl_Q13;
ix[n][0] = i;
ix[n][1] = j;
}
else
{
/* Error increasing, so we're past the optimum */
// FIXME: get this crap out of here
goto done;
}
}
}
done:
ix[n][2] = (sbyte)(Inlines.silk_DIV32_16(ix[n][0], 3));
ix[n][0] = (sbyte)(ix[n][0] - (sbyte)(ix[n][2] * 3));
pred_Q13[n] = quant_pred_Q13;
}
/* Subtract second from first predictor (helps when actually applying these) */
pred_Q13[0] -= pred_Q13[1];
}
}
}
@@ -0,0 +1,59 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common.CPlusPlus;
/// <summary>
/// Struct for CNG
/// </summary>
internal class CNGState
{
internal readonly int[] CNG_exc_buf_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH];
internal readonly short[] CNG_smth_NLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER];
internal readonly int[] CNG_synth_state = new int[SilkConstants.MAX_LPC_ORDER];
internal int CNG_smth_Gain_Q16 = 0;
internal int rand_seed = 0;
internal int fs_kHz = 0;
internal void Reset()
{
Arrays.MemSetInt(CNG_exc_buf_Q14, 0, SilkConstants.MAX_FRAME_LENGTH);
Arrays.MemSetShort(CNG_smth_NLSF_Q15, 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetInt(CNG_synth_state, 0, SilkConstants.MAX_LPC_ORDER);
CNG_smth_Gain_Q16 = 0;
rand_seed = 0;
fs_kHz = 0;
}
}
}
@@ -0,0 +1,72 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Structure for controlling decoder operation and reading decoder status
/// </summary>
internal class DecControlState
{
/* I: Number of channels; 1/2 */
internal int nChannelsAPI = 0;
/* I: Number of channels; 1/2 */
internal int nChannelsInternal = 0;
/* I: Output signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */
internal int API_sampleRate = 0;
/* I: Internal sampling rate used, in Hertz; 8000/12000/16000 */
internal int internalSampleRate = 0;
/* I: Number of samples per packet in milliseconds; 10/20/40/60 */
internal int payloadSize_ms = 0;
/* O: Pitch lag of previous frame (0 if unvoiced), measured in samples at 48 kHz */
internal int prevPitchLag = 0;
internal void Reset()
{
nChannelsAPI = 0;
nChannelsInternal = 0;
API_sampleRate = 0;
internalSampleRate = 0;
payloadSize_ms = 0;
prevPitchLag = 0;
}
}
}
@@ -0,0 +1,217 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Structure for controlling encoder operation
/// </summary>
internal class EncControlState
{
/* I: Number of channels; 1/2 */
internal int nChannelsAPI = 0;
/* I: Number of channels; 1/2 */
internal int nChannelsInternal = 0;
/* I: Input signal sampling rate in Hertz; 8000/12000/16000/24000/32000/44100/48000 */
internal int API_sampleRate = 0;
/* I: Maximum internal sampling rate in Hertz; 8000/12000/16000 */
internal int maxInternalSampleRate = 0;
/* I: Minimum internal sampling rate in Hertz; 8000/12000/16000 */
internal int minInternalSampleRate = 0;
/* I: Soft request for internal sampling rate in Hertz; 8000/12000/16000 */
internal int desiredInternalSampleRate = 0;
/* I: Number of samples per packet in milliseconds; 10/20/40/60 */
internal int payloadSize_ms = 0;
/* I: Bitrate during active speech in bits/second; internally limited */
internal int bitRate = 0;
/* I: Uplink packet loss in percent (0-100) */
internal int packetLossPercentage = 0;
/* I: Complexity mode; 0 is lowest, 10 is highest complexity */
internal int complexity = 0;
/* I: Flag to enable in-band Forward Error Correction (FEC); 0/1 */
internal int useInBandFEC = 0;
/* I: Flag to enable discontinuous transmission (DTX); 0/1 */
internal int useDTX = 0;
/* I: Flag to use constant bitrate */
internal int useCBR = 0;
/* I: Maximum number of bits allowed for the frame */
internal int maxBits = 0;
/* I: Causes a smooth downmix to mono */
internal int toMono = 0;
/* I: Opus encoder is allowing us to switch bandwidth */
internal int opusCanSwitch = 0;
/* I: Make frames as independent as possible (but still use LPC) */
internal int reducedDependency = 0;
/* O: Internal sampling rate used, in Hertz; 8000/12000/16000 */
internal int internalSampleRate = 0;
/* O: Flag that bandwidth switching is allowed (because low voice activity) */
internal int allowBandwidthSwitch = 0;
/* O: Flag that SILK runs in WB mode without variable LP filter (use for switching between WB/SWB/FB) */
internal int inWBmodeWithoutVariableLP = 0;
/* O: Stereo width */
internal int stereoWidth_Q14 = 0;
/* O: Tells the Opus encoder we're ready to switch */
internal int switchReady = 0;
internal void Reset()
{
nChannelsAPI = 0;
nChannelsInternal = 0;
API_sampleRate = 0;
maxInternalSampleRate = 0;
minInternalSampleRate = 0;
desiredInternalSampleRate = 0;
payloadSize_ms = 0;
bitRate = 0;
packetLossPercentage = 0;
complexity = 0;
useInBandFEC = 0;
useDTX = 0;
useCBR = 0;
maxBits = 0;
toMono = 0;
opusCanSwitch = 0;
reducedDependency = 0;
internalSampleRate = 0;
allowBandwidthSwitch = 0;
inWBmodeWithoutVariableLP = 0;
stereoWidth_Q14 = 0;
switchReady = 0;
}
/// <summary>
/// Checks this encoder control struct and returns error code, if any
/// </summary>
/// <returns></returns>
internal int check_control_input()
{
if (((API_sampleRate != 8000) &&
(API_sampleRate != 12000) &&
(API_sampleRate != 16000) &&
(API_sampleRate != 24000) &&
(API_sampleRate != 32000) &&
(API_sampleRate != 44100) &&
(API_sampleRate != 48000)) ||
((desiredInternalSampleRate != 8000) &&
(desiredInternalSampleRate != 12000) &&
(desiredInternalSampleRate != 16000)) ||
((maxInternalSampleRate != 8000) &&
(maxInternalSampleRate != 12000) &&
(maxInternalSampleRate != 16000)) ||
((minInternalSampleRate != 8000) &&
(minInternalSampleRate != 12000) &&
(minInternalSampleRate != 16000)) ||
(minInternalSampleRate > desiredInternalSampleRate) ||
(maxInternalSampleRate < desiredInternalSampleRate) ||
(minInternalSampleRate > maxInternalSampleRate))
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_FS_NOT_SUPPORTED;
}
if (payloadSize_ms != 10 &&
payloadSize_ms != 20 &&
payloadSize_ms != 40 &&
payloadSize_ms != 60)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_PACKET_SIZE_NOT_SUPPORTED;
}
if (packetLossPercentage < 0 || packetLossPercentage > 100)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_LOSS_RATE;
}
if (useDTX < 0 || useDTX > 1)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_DTX_SETTING;
}
if (useCBR < 0 || useCBR > 1)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_CBR_SETTING;
}
if (useInBandFEC < 0 || useInBandFEC > 1)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_INBAND_FEC_SETTING;
}
if (nChannelsAPI < 1 || nChannelsAPI > SilkConstants.ENCODER_NUM_CHANNELS)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
}
if (nChannelsInternal < 1 || nChannelsInternal > SilkConstants.ENCODER_NUM_CHANNELS)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
}
if (nChannelsInternal > nChannelsAPI)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_NUMBER_OF_CHANNELS_ERROR;
}
if (complexity < 0 || complexity > 10)
{
Inlines.OpusAssert(false);
return SilkError.SILK_ENC_INVALID_COMPLEXITY_SETTING;
}
return SilkError.SILK_NO_ERROR;
}
}
}
@@ -0,0 +1,108 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Structure containing NLSF codebook
/// </summary>
internal class NLSFCodebook
{
internal short nVectors = 0;
internal short order = 0;
/// <summary>
/// Quantization step size
/// </summary>
internal short quantStepSize_Q16 = 0;
/// <summary>
/// Inverse quantization step size
/// </summary>
internal short invQuantStepSize_Q6 = 0;
/// <summary>
/// POINTER
/// </summary>
internal byte[] CB1_NLSF_Q8 = null;
/// <summary>
/// POINTER
/// </summary>
internal byte[] CB1_iCDF = null;
/// <summary>
/// POINTER to Backward predictor coefs [ order ]
/// </summary>
internal byte[] pred_Q8 = null;
/// <summary>
/// POINTER to Indices to entropy coding tables [ order ]
/// </summary>
internal byte[] ec_sel = null;
/// <summary>
/// POINTER
/// </summary>
internal byte[] ec_iCDF = null;
/// <summary>
/// POINTER
/// </summary>
internal byte[] ec_Rates_Q5 = null;
/// <summary>
/// POINTER
/// </summary>
internal short[] deltaMin_Q15 = null;
internal void Reset()
{
nVectors = 0;
order = 0;
quantStepSize_Q16 = 0;
invQuantStepSize_Q6 = 0;
CB1_NLSF_Q8 = null;
CB1_iCDF = null;
pred_Q8 = null;
ec_sel = null;
ec_iCDF = null;
ec_Rates_Q5 = null;
deltaMin_Q15 = null;
}
}
}
@@ -0,0 +1,75 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Struct for Packet Loss Concealment
/// </summary>
internal class PLCStruct
{
internal int pitchL_Q8 = 0; /* Pitch lag to use for voiced concealment */
internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER]; /* LTP coeficients to use for voiced concealment */
internal readonly short[] prevLPC_Q12 = new short[SilkConstants.MAX_LPC_ORDER];
internal int last_frame_lost = 0; /* Was previous frame lost */
internal int rand_seed = 0; /* Seed for unvoiced signal generation */
internal short randScale_Q14 = 0; /* Scaling of unvoiced random signal */
internal int conc_energy = 0;
internal int conc_energy_shift = 0;
internal short prevLTP_scale_Q14 = 0;
internal readonly int[] prevGain_Q16 = new int[2];
internal int fs_kHz = 0;
internal int nb_subfr = 0;
internal int subfr_length = 0;
internal void Reset()
{
pitchL_Q8 = 0;
Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER);
Arrays.MemSetShort(prevLPC_Q12, 0, SilkConstants.MAX_LPC_ORDER);
last_frame_lost = 0;
rand_seed = 0;
randScale_Q14 = 0;
conc_energy = 0;
conc_energy_shift = 0;
prevLTP_scale_Q14 = 0;
Arrays.MemSetInt(prevGain_Q16, 0, 2);
fs_kHz = 0;
nb_subfr = 0;
subfr_length = 0;
}
}
}
@@ -0,0 +1,88 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using System;
internal class SideInfoIndices
{
internal readonly sbyte[] GainsIndices = new sbyte[SilkConstants.MAX_NB_SUBFR];
internal readonly sbyte[] LTPIndex = new sbyte[SilkConstants.MAX_NB_SUBFR];
internal readonly sbyte[] NLSFIndices = new sbyte[SilkConstants.MAX_LPC_ORDER + 1];
internal short lagIndex = 0;
internal sbyte contourIndex = 0;
internal sbyte signalType = 0;
internal sbyte quantOffsetType = 0;
internal sbyte NLSFInterpCoef_Q2 = 0;
internal sbyte PERIndex = 0;
internal sbyte LTP_scaleIndex = 0;
internal sbyte Seed = 0;
internal void Reset()
{
Arrays.MemSetSbyte(GainsIndices, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetSbyte(LTPIndex, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetSbyte(NLSFIndices, 0, SilkConstants.MAX_LPC_ORDER + 1);
lagIndex = 0;
contourIndex = 0;
signalType = 0;
quantOffsetType = 0;
NLSFInterpCoef_Q2 = 0;
PERIndex = 0;
LTP_scaleIndex = 0;
Seed = 0;
}
/// <summary>
/// Overwrites this struct with values from another one. Equivalent to C struct assignment this = other
/// </summary>
/// <param name="other"></param>
internal void Assign(SideInfoIndices other)
{
Array.Copy(other.GainsIndices, this.GainsIndices, SilkConstants.MAX_NB_SUBFR);
Array.Copy(other.LTPIndex, this.LTPIndex, SilkConstants.MAX_NB_SUBFR);
Array.Copy(other.NLSFIndices, this.NLSFIndices, SilkConstants.MAX_LPC_ORDER + 1);
this.lagIndex = other.lagIndex;
this.contourIndex = other.contourIndex;
this.signalType = other.signalType;
this.quantOffsetType = other.quantOffsetType;
this.NLSFInterpCoef_Q2 = other.NLSFInterpCoef_Q2;
this.PERIndex = other.PERIndex;
this.LTP_scaleIndex = other.LTP_scaleIndex;
this.Seed = other.Seed;
}
}
}
@@ -0,0 +1,359 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using System;
/// <summary>
/// Decoder state
/// </summary>
internal class SilkChannelDecoder
{
internal int prev_gain_Q16 = 0;
internal readonly int[] exc_Q14 = new int[SilkConstants.MAX_FRAME_LENGTH];
internal readonly int[] sLPC_Q14_buf = new int[SilkConstants.MAX_LPC_ORDER];
internal readonly short[] outBuf = new short[SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH]; /* Buffer for output signal */
internal int lagPrev = 0; /* Previous Lag */
internal sbyte LastGainIndex = 0; /* Previous gain index */
internal int fs_kHz = 0; /* Sampling frequency in kHz */
internal int fs_API_hz = 0; /* API sample frequency (Hz) */
internal int nb_subfr = 0; /* Number of 5 ms subframes in a frame */
internal int frame_length = 0; /* Frame length (samples) */
internal int subfr_length = 0; /* Subframe length (samples) */
internal int ltp_mem_length = 0; /* Length of LTP memory */
internal int LPC_order = 0; /* LPC order */
internal readonly short[] prevNLSF_Q15 = new short[SilkConstants.MAX_LPC_ORDER]; /* Used to interpolate LSFs */
internal int first_frame_after_reset = 0; /* Flag for deactivating NLSF interpolation */
internal byte[] pitch_lag_low_bits_iCDF; /* Pointer to iCDF table for low bits of pitch lag index */
internal byte[] pitch_contour_iCDF; /* Pointer to iCDF table for pitch contour index */
/* For buffering payload in case of more frames per packet */
internal int nFramesDecoded = 0;
internal int nFramesPerPacket = 0;
/* Specifically for entropy coding */
internal int ec_prevSignalType = 0;
internal short ec_prevLagIndex = 0;
internal readonly int[] VAD_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET];
internal int LBRR_flag = 0;
internal readonly int[] LBRR_flags = new int[SilkConstants.MAX_FRAMES_PER_PACKET];
internal readonly SilkResamplerState resampler_state = new SilkResamplerState();
internal NLSFCodebook psNLSF_CB = null; /* Pointer to NLSF codebook */
/* Quantization indices */
internal readonly SideInfoIndices indices = new SideInfoIndices();
/* CNG state */
internal readonly CNGState sCNG = new CNGState();
/* Stuff used for PLC */
internal int lossCnt = 0;
internal int prevSignalType = 0;
internal readonly PLCStruct sPLC = new PLCStruct();
internal void Reset()
{
prev_gain_Q16 = 0;
Arrays.MemSetInt(exc_Q14, 0, SilkConstants.MAX_FRAME_LENGTH);
Arrays.MemSetInt(sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetShort(outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH);
lagPrev = 0;
LastGainIndex = 0;
fs_kHz = 0;
fs_API_hz = 0;
nb_subfr = 0;
frame_length = 0;
subfr_length = 0;
ltp_mem_length = 0;
LPC_order = 0;
Arrays.MemSetShort(prevNLSF_Q15, 0, SilkConstants.MAX_LPC_ORDER);
first_frame_after_reset = 0;
pitch_lag_low_bits_iCDF = null;
pitch_contour_iCDF = null;
nFramesDecoded = 0;
nFramesPerPacket = 0;
ec_prevSignalType = 0;
ec_prevLagIndex = 0;
Arrays.MemSetInt(VAD_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
LBRR_flag = 0;
Arrays.MemSetInt(LBRR_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
resampler_state.Reset();
psNLSF_CB = null;
indices.Reset();
sCNG.Reset();
lossCnt = 0;
prevSignalType = 0;
sPLC.Reset();
}
/// <summary>
/// Init Decoder State
/// </summary>
/// <returns></returns>
internal int silk_init_decoder()
{
/* Clear the entire encoder state, except anything copied */
this.Reset();
/* Used to deactivate LSF interpolation */
this.first_frame_after_reset = 1;
this.prev_gain_Q16 = 65536;
/* Reset CNG state */
silk_CNG_Reset();
/* Reset PLC state */
silk_PLC_Reset();
return (0);
}
/// <summary>
/// Resets CNG state
/// </summary>
private void silk_CNG_Reset()
{
int i, NLSF_step_Q15, NLSF_acc_Q15;
NLSF_step_Q15 = Inlines.silk_DIV32_16(short.MaxValue, this.LPC_order + 1);
NLSF_acc_Q15 = 0;
for (i = 0; i < this.LPC_order; i++)
{
NLSF_acc_Q15 += NLSF_step_Q15;
this.sCNG.CNG_smth_NLSF_Q15[i] = (short)(NLSF_acc_Q15);
}
this.sCNG.CNG_smth_Gain_Q16 = 0;
this.sCNG.rand_seed = 3176576;
}
/// <summary>
/// Resets PLC state
/// </summary>
private void silk_PLC_Reset()
{
this.sPLC.pitchL_Q8 = Inlines.silk_LSHIFT(this.frame_length, 8 - 1);
this.sPLC.prevGain_Q16[0] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/;
this.sPLC.prevGain_Q16[1] = ((int)((1) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(1, 16)*/;
this.sPLC.subfr_length = 20;
this.sPLC.nb_subfr = 2;
}
/* Set decoder sampling rate */
internal int silk_decoder_set_fs(
int fs_kHz, /* I Sampling frequency (kHz) */
int fs_API_Hz /* I API Sampling frequency (Hz) */
)
{
int frame_length, ret = 0;
Inlines.OpusAssert(fs_kHz == 8 || fs_kHz == 12 || fs_kHz == 16);
Inlines.OpusAssert(this.nb_subfr == SilkConstants.MAX_NB_SUBFR || this.nb_subfr == SilkConstants.MAX_NB_SUBFR / 2);
/* New (sub)frame length */
this.subfr_length = Inlines.silk_SMULBB(SilkConstants.SUB_FRAME_LENGTH_MS, fs_kHz);
frame_length = Inlines.silk_SMULBB(this.nb_subfr, this.subfr_length);
/* Initialize resampler when switching internal or external sampling frequency */
if (this.fs_kHz != fs_kHz || this.fs_API_hz != fs_API_Hz)
{
/* Initialize the resampler for dec_API.c preparing resampling from fs_kHz to API_fs_Hz */
ret += Resampler.silk_resampler_init(this.resampler_state, Inlines.silk_SMULBB(fs_kHz, 1000), fs_API_Hz, 0);
this.fs_API_hz = fs_API_Hz;
}
if (this.fs_kHz != fs_kHz || frame_length != this.frame_length)
{
if (fs_kHz == 8)
{
if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
this.pitch_contour_iCDF = Tables.silk_pitch_contour_NB_iCDF;
}
else {
this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_NB_iCDF;
}
}
else {
if (this.nb_subfr == SilkConstants.MAX_NB_SUBFR)
{
this.pitch_contour_iCDF = Tables.silk_pitch_contour_iCDF;
}
else {
this.pitch_contour_iCDF = Tables.silk_pitch_contour_10_ms_iCDF;
}
}
if (this.fs_kHz != fs_kHz)
{
this.ltp_mem_length = Inlines.silk_SMULBB(SilkConstants.LTP_MEM_LENGTH_MS, fs_kHz);
if (fs_kHz == 8 || fs_kHz == 12)
{
this.LPC_order = SilkConstants.MIN_LPC_ORDER;
this.psNLSF_CB = Tables.silk_NLSF_CB_NB_MB;
}
else {
this.LPC_order = SilkConstants.MAX_LPC_ORDER;
this.psNLSF_CB = Tables.silk_NLSF_CB_WB;
}
if (fs_kHz == 16)
{
this.pitch_lag_low_bits_iCDF = Tables.silk_uniform8_iCDF;
}
else if (fs_kHz == 12)
{
this.pitch_lag_low_bits_iCDF = Tables.silk_uniform6_iCDF;
}
else if (fs_kHz == 8)
{
this.pitch_lag_low_bits_iCDF = Tables.silk_uniform4_iCDF;
}
else {
/* unsupported sampling rate */
Inlines.OpusAssert(false);
}
this.first_frame_after_reset = 1;
this.lagPrev = 100;
this.LastGainIndex = 10;
this.prevSignalType = SilkConstants.TYPE_NO_VOICE_ACTIVITY;
Arrays.MemSetShort(this.outBuf, 0, SilkConstants.MAX_FRAME_LENGTH + 2 * SilkConstants.MAX_SUB_FRAME_LENGTH);
Arrays.MemSetInt(this.sLPC_Q14_buf, 0, SilkConstants.MAX_LPC_ORDER);
}
this.fs_kHz = fs_kHz;
this.frame_length = frame_length;
}
/* Check that settings are valid */
Inlines.OpusAssert(this.frame_length > 0 && this.frame_length <= SilkConstants.MAX_FRAME_LENGTH);
return ret;
}
/****************/
/* Decode frame */
/****************/
internal int silk_decode_frame(
EntropyCoder psRangeDec, /* I/O Compressor data structure */
short[] pOut, /* O Pointer to output speech frame */
int pOut_ptr,
BoxedValueInt pN, /* O Pointer to size of output frame */
int lostFlag, /* I 0: no loss, 1 loss, 2 decode fec */
int condCoding /* I The type of conditional coding to use */
)
{
SilkDecoderControl thisCtrl = new SilkDecoderControl();
int L, mv_len, ret = 0;
L = this.frame_length;
thisCtrl.LTP_scale_Q14 = 0;
/* Safety checks */
Inlines.OpusAssert(L > 0 && L <= SilkConstants.MAX_FRAME_LENGTH);
if (lostFlag == DecoderAPIFlag.FLAG_DECODE_NORMAL ||
(lostFlag == DecoderAPIFlag.FLAG_DECODE_LBRR && this.LBRR_flags[this.nFramesDecoded] == 1))
{
short[] pulses = new short[(L + SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1) & ~(SilkConstants.SHELL_CODEC_FRAME_LENGTH - 1)];
/*********************************************/
/* Decode quantization indices of side info */
/*********************************************/
DecodeIndices.silk_decode_indices(this, psRangeDec, this.nFramesDecoded, lostFlag, condCoding);
/*********************************************/
/* Decode quantization indices of excitation */
/*********************************************/
DecodePulses.silk_decode_pulses(psRangeDec, pulses, this.indices.signalType,
this.indices.quantOffsetType, this.frame_length);
/********************************************/
/* Decode parameters and pulse signal */
/********************************************/
DecodeParameters.silk_decode_parameters(this, thisCtrl, condCoding);
/********************************************************/
/* Run inverse NSQ */
/********************************************************/
DecodeCore.silk_decode_core(this, thisCtrl, pOut, pOut_ptr, pulses);
/********************************************************/
/* Update PLC state */
/********************************************************/
PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 0);
this.lossCnt = 0;
this.prevSignalType = this.indices.signalType;
Inlines.OpusAssert(this.prevSignalType >= 0 && this.prevSignalType <= 2);
/* A frame has been decoded without errors */
this.first_frame_after_reset = 0;
}
else
{
/* Handle packet loss by extrapolation */
PLC.silk_PLC(this, thisCtrl, pOut, pOut_ptr, 1);
}
/*************************/
/* Update output buffer. */
/*************************/
Inlines.OpusAssert(this.ltp_mem_length >= this.frame_length);
mv_len = this.ltp_mem_length - this.frame_length;
Arrays.MemMoveShort(this.outBuf, this.frame_length, 0, mv_len);
Array.Copy(pOut, pOut_ptr, this.outBuf, mv_len, this.frame_length);
/************************************************/
/* Comfort noise generation / estimation */
/************************************************/
CNG.silk_CNG(this, thisCtrl, pOut, pOut_ptr, L);
/****************************************************************/
/* Ensure smooth connection of extrapolated and good frames */
/****************************************************************/
PLC.silk_PLC_glue_frames(this, pOut, pOut_ptr, L);
/* Update some decoder state variables */
this.lagPrev = thisCtrl.pitchL[this.nb_subfr - 1];
/* Set output frame length */
pN.Val = L;
return ret;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Decoder super struct
/// </summary>
internal class SilkDecoder
{
internal readonly SilkChannelDecoder[] channel_state = new SilkChannelDecoder[SilkConstants.DECODER_NUM_CHANNELS];
internal readonly StereoDecodeState sStereo = new StereoDecodeState();
internal int nChannelsAPI = 0;
internal int nChannelsInternal = 0;
internal int prev_decode_only_middle = 0;
internal SilkDecoder()
{
for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++)
{
channel_state[c] = new SilkChannelDecoder();
}
}
internal void Reset()
{
for (int c = 0; c < SilkConstants.DECODER_NUM_CHANNELS; c++)
{
channel_state[c].Reset();
}
sStereo.Reset();
nChannelsAPI = 0;
nChannelsInternal = 0;
prev_decode_only_middle = 0;
}
}
}
@@ -0,0 +1,63 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Decoder control
/// </summary>
internal class SilkDecoderControl
{
/* Prediction and coding parameters */
internal readonly int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR];
internal readonly int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR];
/* Holds interpolated and final coefficients */
internal readonly short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArray<short>(2, SilkConstants.MAX_LPC_ORDER);
internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR];
internal int LTP_scale_Q14 = 0;
internal void Reset()
{
Arrays.MemSetInt(pitchL, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetShort(PredCoef_Q12[0], 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetShort(PredCoef_Q12[1], 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR);
LTP_scale_Q14 = 0;
}
}
}
@@ -0,0 +1,105 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Encoder Super Struct
/// </summary>
internal class SilkEncoder
{
internal readonly SilkChannelEncoder[] state_Fxx = new SilkChannelEncoder[SilkConstants.ENCODER_NUM_CHANNELS];
internal readonly StereoEncodeState sStereo = new StereoEncodeState();
internal int nBitsUsedLBRR = 0;
internal int nBitsExceeded = 0;
internal int nChannelsAPI = 0;
internal int nChannelsInternal = 0;
internal int nPrevChannelsInternal = 0;
internal int timeSinceSwitchAllowed_ms = 0;
internal int allowBandwidthSwitch = 0;
internal int prev_decode_only_middle = 0;
internal SilkEncoder()
{
for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++)
{
state_Fxx[c] = new SilkChannelEncoder();
}
}
internal void Reset()
{
for (int c = 0; c < SilkConstants.ENCODER_NUM_CHANNELS; c++)
{
state_Fxx[c].Reset();
}
sStereo.Reset();
nBitsUsedLBRR = 0;
nBitsExceeded = 0;
nChannelsAPI = 0;
nChannelsInternal = 0;
nPrevChannelsInternal = 0;
timeSinceSwitchAllowed_ms = 0;
allowBandwidthSwitch = 0;
prev_decode_only_middle = 0;
}
/// <summary>
/// Initialize Silk Encoder state
/// </summary>
/// <param name="psEnc">I/O Pointer to Silk FIX encoder state</param>
/// <returns></returns>
internal static int silk_init_encoder(SilkChannelEncoder psEnc)
{
int ret = 0;
// Clear the entire encoder state
psEnc.Reset();
psEnc.variable_HP_smth1_Q15 = Inlines.silk_LSHIFT(Inlines.silk_lin2log(((int)((TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ) * ((long)1 << (16)) + 0.5))/*Inlines.SILK_CONST(TuningParameters.VARIABLE_HP_MIN_CUTOFF_HZ, 16)*/) - (16 << 7), 8);
psEnc.variable_HP_smth2_Q15 = psEnc.variable_HP_smth1_Q15;
// Used to deactivate LSF interpolation, pitch prediction
psEnc.first_frame_after_reset = 1;
// Initialize Silk VAD
ret += VoiceActivityDetection.silk_VAD_Init(psEnc.sVAD);
return ret;
}
}
}
@@ -0,0 +1,105 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/************************/
/* Encoder control FIX */
/************************/
internal class SilkEncoderControl
{
/* Prediction and coding parameters */
internal readonly int[] Gains_Q16 = new int[SilkConstants.MAX_NB_SUBFR];
internal readonly short[][] PredCoef_Q12 = Arrays.InitTwoDimensionalArray<short>(2, SilkConstants.MAX_LPC_ORDER); /* holds interpolated and final coefficients */
internal readonly short[] LTPCoef_Q14 = new short[SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR];
internal int LTP_scale_Q14 = 0;
internal readonly int[] pitchL = new int[SilkConstants.MAX_NB_SUBFR];
/* Noise shaping parameters */
internal readonly short[] AR1_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER];
internal readonly short[] AR2_Q13 = new short[SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER];
internal readonly int[] LF_shp_Q14 = new int[SilkConstants.MAX_NB_SUBFR]; /* Packs two int16 coefficients per int32 value */
internal readonly int[] GainsPre_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
internal readonly int[] HarmBoost_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
internal readonly int[] Tilt_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
internal readonly int[] HarmShapeGain_Q14 = new int[SilkConstants.MAX_NB_SUBFR];
internal int Lambda_Q10 = 0;
internal int input_quality_Q14 = 0;
internal int coding_quality_Q14 = 0;
/* Measures */
internal int sparseness_Q8 = 0;
internal int predGain_Q16 = 0;
internal int LTPredCodGain_Q7 = 0;
/* Residual energy per subframe */
internal readonly int[] ResNrg = new int[SilkConstants.MAX_NB_SUBFR];
/* Q domain for the residual energy > 0 */
internal readonly int[] ResNrgQ = new int[SilkConstants.MAX_NB_SUBFR];
/* Parameters for CBR mode */
internal readonly int[] GainsUnq_Q16 = new int[SilkConstants.MAX_NB_SUBFR];
internal sbyte lastGainIndexPrev = 0;
internal void Reset()
{
Arrays.MemSetInt(Gains_Q16, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetShort(PredCoef_Q12[0], 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetShort(PredCoef_Q12[1], 0, SilkConstants.MAX_LPC_ORDER);
Arrays.MemSetShort(LTPCoef_Q14, 0, SilkConstants.LTP_ORDER * SilkConstants.MAX_NB_SUBFR);
LTP_scale_Q14 = 0;
Arrays.MemSetInt(pitchL, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetShort(AR1_Q13, 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER);
Arrays.MemSetShort(AR2_Q13, 0, SilkConstants.MAX_NB_SUBFR * SilkConstants.MAX_SHAPE_LPC_ORDER);
Arrays.MemSetInt(LF_shp_Q14, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(GainsPre_Q14, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(HarmBoost_Q14, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(Tilt_Q14, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(HarmShapeGain_Q14, 0, SilkConstants.MAX_NB_SUBFR);
Lambda_Q10 = 0;
input_quality_Q14 = 0;
coding_quality_Q14 = 0;
sparseness_Q8 = 0;
predGain_Q16 = 0;
LTPredCodGain_Q7 = 0;
Arrays.MemSetInt(ResNrg, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(ResNrgQ, 0, SilkConstants.MAX_NB_SUBFR);
Arrays.MemSetInt(GainsUnq_Q16, 0, SilkConstants.MAX_NB_SUBFR);
lastGainIndexPrev = 0;
}
}
}
@@ -0,0 +1,108 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Variable cut-off low-pass filter state
/// </summary>
internal class SilkLPState
{
/// <summary>
/// Low pass filter state
/// </summary>
internal readonly int[] In_LP_State = new int[2];
/// <summary>
/// Counter which is mapped to a cut-off frequency
/// </summary>
internal int transition_frame_no = 0;
/// <summary>
/// Operating mode, &lt;0: switch down, &gt;0: switch up; 0: do nothing
/// </summary>
internal int mode = 0;
internal void Reset()
{
In_LP_State[0] = 0;
In_LP_State[1] = 0;
transition_frame_no = 0;
mode = 0;
}
/* Low-pass filter with variable cutoff frequency based on */
/* piece-wise linear interpolation between elliptic filters */
/* Start by setting psEncC.mode <> 0; */
/* Deactivate by setting psEncC.mode = 0; */
internal void silk_LP_variable_cutoff(
short[] frame, /* I/O Low-pass filtered output signal */
int frame_ptr,
int frame_length /* I Frame length */
)
{
int[] B_Q28 = new int[SilkConstants.TRANSITION_NB];
int[] A_Q28 = new int[SilkConstants.TRANSITION_NA];
int fac_Q16 = 0;
int ind = 0;
Inlines.OpusAssert(this.transition_frame_no >= 0 && this.transition_frame_no <= SilkConstants.TRANSITION_FRAMES);
/* Run filter if needed */
if (this.mode != 0)
{
/* Calculate index and interpolation factor for interpolation */
fac_Q16 = Inlines.silk_LSHIFT(SilkConstants.TRANSITION_FRAMES - this.transition_frame_no, 16 - 6);
ind = Inlines.silk_RSHIFT(fac_Q16, 16);
fac_Q16 -= Inlines.silk_LSHIFT(ind, 16);
Inlines.OpusAssert(ind >= 0);
Inlines.OpusAssert(ind < SilkConstants.TRANSITION_INT_NUM);
/* Interpolate filter coefficients */
Filters.silk_LP_interpolate_filter_taps(B_Q28, A_Q28, ind, fac_Q16);
/* Update transition frame number for next frame */
this.transition_frame_no = Inlines.silk_LIMIT(this.transition_frame_no + this.mode, 0, SilkConstants.TRANSITION_FRAMES);
/* ARMA low-pass filtering */
Inlines.OpusAssert(SilkConstants.TRANSITION_NB == 3 && SilkConstants.TRANSITION_NA == 2);
Filters.silk_biquad_alt(frame, frame_ptr, B_Q28, A_Q28, this.In_LP_State, frame, frame_ptr, frame_length, 1);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,70 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Prefilter state
/// </summary>
internal class SilkPrefilterState
{
internal readonly short[] sLTP_shp = new short[SilkConstants.LTP_BUF_LENGTH];
internal readonly int[] sAR_shp = new int[SilkConstants.MAX_SHAPE_LPC_ORDER + 1];
internal int sLTP_shp_buf_idx = 0;
internal int sLF_AR_shp_Q12 = 0;
internal int sLF_MA_shp_Q12 = 0;
internal int sHarmHP_Q2 = 0;
internal int rand_seed = 0;
internal int lagPrev = 0;
internal SilkPrefilterState()
{
}
internal void Reset()
{
Arrays.MemSetShort(sLTP_shp, 0, SilkConstants.LTP_BUF_LENGTH);
Arrays.MemSetInt(sAR_shp, 0, SilkConstants.MAX_SHAPE_LPC_ORDER + 1);
sLTP_shp_buf_idx = 0;
sLF_AR_shp_Q12 = 0;
sLF_MA_shp_Q12 = 0;
sHarmHP_Q2 = 0;
rand_seed = 0;
lagPrev = 0;
}
}
}
@@ -0,0 +1,94 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using System;
internal class SilkResamplerState
{
internal readonly int[] sIIR = new int[SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER]; /* this must be the first element of this struct FIXME why? */
internal readonly int[] sFIR_i32 = new int[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER]; // porting note: these two fields were originally a union, so that means only 1 will ever be used at a time.
internal readonly short[] sFIR_i16 = new short[SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER];
internal readonly short[] delayBuf = new short[48];
internal int resampler_function = 0;
internal int batchSize = 0;
internal int invRatio_Q16 = 0;
internal int FIR_Order = 0;
internal int FIR_Fracs = 0;
internal int Fs_in_kHz = 0;
internal int Fs_out_kHz = 0;
internal int inputDelay = 0;
/// <summary>
/// POINTER
/// </summary>
internal short[] Coefs = null;
internal void Reset()
{
Arrays.MemSetInt(sIIR, 0, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER);
Arrays.MemSetInt(sFIR_i32, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER);
Arrays.MemSetShort(sFIR_i16, 0, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER);
Arrays.MemSetShort(delayBuf, 0, 48);
resampler_function = 0;
batchSize = 0;
invRatio_Q16 = 0;
FIR_Order = 0;
FIR_Fracs = 0;
Fs_in_kHz = 0;
Fs_out_kHz = 0;
inputDelay = 0;
Coefs = null;
}
internal void Assign(SilkResamplerState other)
{
resampler_function = other.resampler_function;
batchSize = other.batchSize;
invRatio_Q16 = other.invRatio_Q16;
FIR_Order = other.FIR_Order;
FIR_Fracs = other.FIR_Fracs;
Fs_in_kHz = other.Fs_in_kHz;
Fs_out_kHz = other.Fs_out_kHz;
inputDelay = other.inputDelay;
Coefs = other.Coefs;
Array.Copy(other.sIIR, this.sIIR, SilkConstants.SILK_RESAMPLER_MAX_IIR_ORDER);
Array.Copy(other.sFIR_i32, this.sFIR_i32, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER);
Array.Copy(other.sFIR_i16, this.sFIR_i16, SilkConstants.SILK_RESAMPLER_MAX_FIR_ORDER);
Array.Copy(other.delayBuf, this.delayBuf, 48);
}
}
}
@@ -0,0 +1,57 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Noise shaping analysis state
/// </summary>
internal class SilkShapeState
{
internal sbyte LastGainIndex = 0;
internal int HarmBoost_smth_Q16 = 0;
internal int HarmShapeGain_smth_Q16 = 0;
internal int Tilt_smth_Q16 = 0;
internal void Reset()
{
LastGainIndex = 0;
HarmBoost_smth_Q16 = 0;
HarmShapeGain_smth_Q16 = 0;
Tilt_smth_Q16 = 0;
}
}
}
@@ -0,0 +1,108 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// VAD state
/// </summary>
internal class SilkVADState
{
/// <summary>
/// Analysis filterbank state: 0-8 kHz
/// </summary>
internal readonly int[] AnaState = new int[2];
/// <summary>
/// Analysis filterbank state: 0-4 kHz
/// </summary>
internal readonly int[] AnaState1 = new int[2];
/// <summary>
/// Analysis filterbank state: 0-2 kHz
/// </summary>
internal readonly int[] AnaState2 = new int[2];
/// <summary>
/// Subframe energies
/// </summary>
internal readonly int[] XnrgSubfr = new int[SilkConstants.VAD_N_BANDS];
/// <summary>
/// Smoothed energy level in each band
/// </summary>
internal readonly int[] NrgRatioSmth_Q8 = new int[SilkConstants.VAD_N_BANDS];
/// <summary>
/// State of differentiator in the lowest band
/// </summary>
internal short HPstate = 0;
/// <summary>
/// Noise energy level in each band
/// </summary>
internal readonly int[] NL = new int[SilkConstants.VAD_N_BANDS];
/// <summary>
/// Inverse noise energy level in each band
/// </summary>
internal readonly int[] inv_NL = new int[SilkConstants.VAD_N_BANDS];
/// <summary>
/// Noise level estimator bias/offset
/// </summary>
internal readonly int[] NoiseLevelBias = new int[SilkConstants.VAD_N_BANDS];
/// <summary>
/// Frame counter used in the initial phase
/// </summary>
internal int counter = 0;
internal void Reset()
{
Arrays.MemSetInt(AnaState, 0, 2);
Arrays.MemSetInt(AnaState1, 0, 2);
Arrays.MemSetInt(AnaState2, 0, 2);
Arrays.MemSetInt(XnrgSubfr, 0, SilkConstants.VAD_N_BANDS);
Arrays.MemSetInt(NrgRatioSmth_Q8, 0, SilkConstants.VAD_N_BANDS);
HPstate = 0;
Arrays.MemSetInt(NL, 0, SilkConstants.VAD_N_BANDS);
Arrays.MemSetInt(inv_NL, 0, SilkConstants.VAD_N_BANDS);
Arrays.MemSetInt(NoiseLevelBias, 0, SilkConstants.VAD_N_BANDS);
counter = 0;
}
}
}
@@ -0,0 +1,52 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
internal class StereoDecodeState
{
internal readonly short[] pred_prev_Q13 = new short[2];
internal readonly short[] sMid = new short[2];
internal readonly short[] sSide = new short[2];
internal void Reset()
{
Arrays.MemSetShort(pred_prev_Q13, 0, 2);
Arrays.MemSetShort(sMid, 0, 2);
Arrays.MemSetShort(sSide, 0, 2);
}
}
}
@@ -0,0 +1,71 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
internal class StereoEncodeState
{
internal readonly short[] pred_prev_Q13 = new short[2];
internal readonly short[] sMid = new short[2];
internal readonly short[] sSide = new short[2];
internal readonly int[] mid_side_amp_Q0 = new int[4];
internal short smth_width_Q14 = 0;
internal short width_prev_Q14 = 0;
internal short silent_side_len = 0;
internal readonly sbyte[][][] predIx = Arrays.InitThreeDimensionalArray<sbyte>(SilkConstants.MAX_FRAMES_PER_PACKET, 2, 3);
internal readonly sbyte[] mid_only_flags = new sbyte[SilkConstants.MAX_FRAMES_PER_PACKET];
internal void Reset()
{
Arrays.MemSetShort(pred_prev_Q13, 0, 2);
Arrays.MemSetShort(sMid, 0, 2);
Arrays.MemSetShort(sSide, 0, 2);
Arrays.MemSetInt(mid_side_amp_Q0, 0, 4);
smth_width_Q14 = 0;
width_prev_Q14 = 0;
silent_side_len = 0;
for (int x = 0; x < SilkConstants.MAX_FRAMES_PER_PACKET; x++)
{
for (int y= 0; y < 2; y++)
{
Arrays.MemSetSbyte(predIx[x][y], 0, 3);
}
}
Arrays.MemSetSbyte(mid_only_flags, 0, SilkConstants.MAX_FRAMES_PER_PACKET);
}
}
}
@@ -0,0 +1,66 @@
/* 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.
*/
namespace Concentus.Silk.Structs
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
/// <summary>
/// Struct for TOC (Table of Contents)
/// </summary>
internal class TOCStruct
{
/// <summary>
/// Voice activity for packet
/// </summary>
internal int VADFlag = 0;
/// <summary>
/// Voice activity for each frame in packet
/// </summary>
internal readonly int[] VADFlags = new int[SilkConstants.SILK_MAX_FRAMES_PER_PACKET];
/// <summary>
/// Flag indicating if packet contains in-band FEC
/// </summary>
internal int inbandFECFlag = 0;
internal void Reset()
{
VADFlag = 0;
Arrays.MemSetInt(VADFlags, 0, SilkConstants.SILK_MAX_FRAMES_PER_PACKET);
inbandFECFlag = 0;
}
}
}
@@ -0,0 +1,179 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class SumSqrShift
{
/// <summary>
/// Compute number of bits to right shift the sum of squares of a vector
/// of int16s to make it fit in an int32
/// </summary>
/// <param name="energy">O Energy of x, after shifting to the right</param>
/// <param name="shift">O Number of bits right shift applied to energy</param>
/// <param name="x">I Input vector</param>
/// <param name="len">I Length of input vector</param>
internal static void silk_sum_sqr_shift(
out int energy,
out int shift,
short[] x,
int x_ptr,
int len)
{
int i, shft;
int nrg_tmp, nrg;
nrg = 0;
shft = 0;
len--;
for (i = 0; i < len; i += 2)
{
nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i], x[x_ptr + i]);
nrg = Inlines.silk_SMLABB_ovflw(nrg, x[x_ptr + i + 1], x[x_ptr + i + 1]);
if (nrg < 0)
{
/* Scale down */
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft = 2;
i += 2;
break;
}
}
for (; i < len; i += 2)
{
nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]);
nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[x_ptr + i + 1], x[x_ptr + i + 1]);
nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft));
if (nrg < 0)
{
/* Scale down */
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft += 2;
}
}
if (i == len)
{
/* One sample left to process */
nrg_tmp = Inlines.silk_SMULBB(x[x_ptr + i], x[x_ptr + i]);
nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft));
}
/* Make sure to have at least one extra leading zero (two leading zeros in total) */
if ((nrg & 0xC0000000) != 0)
{
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft += 2;
}
/* Output arguments */
shift = shft;
energy = nrg;
}
/// <summary>
/// Zero-index variant
/// Compute number of bits to right shift the sum of squares of a vector
/// of int16s to make it fit in an int32
/// </summary>
/// <param name="energy">O Energy of x, after shifting to the right</param>
/// <param name="shift">O Number of bits right shift applied to energy</param>
/// <param name="x">I Input vector</param>
/// <param name="len">I Length of input vector</param>
internal static void silk_sum_sqr_shift(
out int energy,
out int shift,
short[] x,
int len)
{
int i, shft;
int nrg_tmp, nrg;
nrg = 0;
shft = 0;
len--;
for (i = 0; i < len; i += 2)
{
nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i], x[i]);
nrg = Inlines.silk_SMLABB_ovflw(nrg, x[i + 1], x[i + 1]);
if (nrg < 0)
{
/* Scale down */
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft = 2;
i += 2;
break;
}
}
for (; i < len; i += 2)
{
nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]);
nrg_tmp = Inlines.silk_SMLABB_ovflw(nrg_tmp, x[i + 1], x[i + 1]);
nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft));
if (nrg < 0)
{
/* Scale down */
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft += 2;
}
}
if (i == len)
{
/* One sample left to process */
nrg_tmp = Inlines.silk_SMULBB(x[i], x[i]);
nrg = unchecked((int)Inlines.silk_ADD_RSHIFT_uint(unchecked((uint)nrg), unchecked((uint)nrg_tmp), shft));
}
/* Make sure to have at least one extra leading zero (two leading zeros in total) */
if ((nrg & 0xC0000000) != 0)
{
nrg = unchecked((int)Inlines.silk_RSHIFT_uint(unchecked((uint)nrg), 2));
shft += 2;
}
/* Output arguments */
shift = shft;
energy = nrg;
}
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,174 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal class TuningParameters
{
/* Decay time for EntropyCoder.BITREServoir */
internal const int BITRESERVOIR_DECAY_TIME_MS = 500;
/*******************/
/* Pitch estimator */
/*******************/
/* Level of noise floor for whitening filter LPC analysis in pitch analysis */
internal const float FIND_PITCH_WHITE_NOISE_FRACTION = 1e-3f;
/* Bandwidth expansion for whitening filter in pitch analysis */
internal const float FIND_PITCH_BANDWIDTH_EXPANSION = 0.99f;
/*********************/
/* Linear prediction */
/*********************/
/* LPC analysis regularization */
internal const float FIND_LPC_COND_FAC = 1e-5f;
/* LTP analysis defines */
internal const float FIND_LTP_COND_FAC = 1e-5f;
internal const float LTP_DAMPING = 0.05f;
internal const float LTP_SMOOTHING = 0.1f;
/* LTP quantization settings */
internal const float MU_LTP_QUANT_NB = 0.03f;
internal const float MU_LTP_QUANT_MB = 0.025f;
internal const float MU_LTP_QUANT_WB = 0.02f;
/* Max cumulative LTP gain */
internal const float MAX_SUM_LOG_GAIN_DB = 250.0f;
/***********************/
/* High pass filtering */
/***********************/
/* Smoothing parameters for low end of pitch frequency range estimation */
internal const float VARIABLE_HP_SMTH_COEF1 = 0.1f;
internal const float VARIABLE_HP_SMTH_COEF2 = 0.015f;
internal const float VARIABLE_HP_MAX_DELTA_FREQ = 0.4f;
/* Min and max cut-off frequency values (-3 dB points) */
internal const int VARIABLE_HP_MIN_CUTOFF_HZ = 60;
internal const int VARIABLE_HP_MAX_CUTOFF_HZ = 100;
/***********/
/* Various */
/***********/
/* VAD threshold */
internal const float SPEECH_ACTIVITY_DTX_THRES = 0.05f;
/* Speech Activity LBRR enable threshold */
internal const float LBRR_SPEECH_ACTIVITY_THRES = 0.3f;
/*************************/
/* Perceptual parameters */
/*************************/
/* reduction in coding SNR during low speech activity */
internal const float BG_SNR_DECR_dB = 2.0f;
/* factor for reducing quantization noise during voiced speech */
internal const float HARM_SNR_INCR_dB = 2.0f;
/* factor for reducing quantization noise for unvoiced sparse signals */
internal const float SPARSE_SNR_INCR_dB = 2.0f;
/* threshold for sparseness measure above which to use lower quantization offset during unvoiced */
internal const float SPARSENESS_THRESHOLD_QNT_OFFSET = 0.75f;
/* warping control */
internal const float WARPING_MULTIPLIER = 0.015f;
/* fraction added to first autocorrelation value */
internal const float SHAPE_WHITE_NOISE_FRACTION = 5e-5f;
/* noise shaping filter chirp factor */
internal const float BANDWIDTH_EXPANSION = 0.95f;
/* difference between chirp factors for analysis and synthesis noise shaping filters at low bitrates */
internal const float LOW_RATE_BANDWIDTH_EXPANSION_DELTA = 0.01f;
/* extra harmonic boosting (signal shaping) at low bitrates */
internal const float LOW_RATE_HARMONIC_BOOST = 0.1f;
/* extra harmonic boosting (signal shaping) for noisy input signals */
internal const float LOW_INPUT_QUALITY_HARMONIC_BOOST = 0.1f;
/* harmonic noise shaping */
internal const float HARMONIC_SHAPING = 0.3f;
/* extra harmonic noise shaping for high bitrates or noisy input */
internal const float HIGH_RATE_OR_LOW_QUALITY_HARMONIC_SHAPING = 0.2f;
/* parameter for shaping noise towards higher frequencies */
internal const float HP_NOISE_COEF = 0.25f;
/* parameter for shaping noise even more towards higher frequencies during voiced speech */
internal const float HARM_HP_NOISE_COEF = 0.35f;
/* parameter for applying a high-pass tilt to the input signal */
internal const float INPUT_TILT = 0.05f;
/* parameter for extra high-pass tilt to the input signal at high rates */
internal const float HIGH_RATE_INPUT_TILT = 0.1f;
/* parameter for reducing noise at the very low frequencies */
internal const float LOW_FREQ_SHAPING = 4.0f;
/* less reduction of noise at the very low frequencies for signals with low SNR at low frequencies */
internal const float LOW_QUALITY_LOW_FREQ_SHAPING_DECR = 0.5f;
/* subframe smoothing coefficient for HarmBoost, HarmShapeGain, Tilt (lower . more smoothing) */
internal const float SUBFR_SMTH_COEF = 0.4f;
/* parameters defining the R/D tradeoff in the residual quantizer */
internal const float LAMBDA_OFFSET = 1.2f;
internal const float LAMBDA_SPEECH_ACT = -0.2f;
internal const float LAMBDA_DELAYED_DECISIONS = -0.05f;
internal const float LAMBDA_INPUT_QUALITY = -0.1f;
internal const float LAMBDA_CODING_QUALITY = -0.2f;
internal const float LAMBDA_QUANT_OFFSET = 0.8f;
/* Compensation in bitrate calculations for 10 ms modes */
internal const int REDUCE_BITRATE_10_MS_BPS = 2200;
/* Maximum time before allowing a bandwidth transition */
internal const int MAX_BANDWIDTH_SWITCH_DELAY_MS = 5000;
}
}
@@ -0,0 +1,134 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
internal static class VQ_WMat_EC
{
/* Entropy constrained matrix-weighted VQ, hard-coded to 5-element vectors, for a single input data vector */
internal static void silk_VQ_WMat_EC(
BoxedValueSbyte ind, /* O index of best codebook vector */
BoxedValueInt rate_dist_Q14, /* O best weighted quant error + mu * rate */
BoxedValueInt gain_Q7, /* O sum of absolute LTP coefficients */
short[] in_Q14, /* I input vector to be quantized */
int in_Q14_ptr,
int[] W_Q18, /* I weighting matrix */
int W_Q18_ptr,
sbyte[][] cb_Q7, /* I codebook */
byte[] cb_gain_Q7, /* I codebook effective gain */
byte[] cl_Q5, /* I code length for each codebook vector */
int mu_Q9, /* I tradeoff betw. weighted error and rate */
int max_gain_Q7, /* I maximum sum of absolute LTP coefficients */
int L /* I number of vectors in codebook */
)
{
int k, gain_tmp_Q7;
sbyte[] cb_row_Q7;
int cb_row_Q7_ptr = 0;
short[] diff_Q14 = new short[5];
int sum1_Q14, sum2_Q16;
/* Loop over codebook */
rate_dist_Q14.Val = int.MaxValue;
for (k = 0; k < L; k++)
{
/* Go to next cbk vector */
cb_row_Q7 = cb_Q7[cb_row_Q7_ptr++];
gain_tmp_Q7 = cb_gain_Q7[k];
diff_Q14[0] = (short)(in_Q14[in_Q14_ptr] - Inlines.silk_LSHIFT(cb_row_Q7[0], 7));
diff_Q14[1] = (short)(in_Q14[in_Q14_ptr + 1] - Inlines.silk_LSHIFT(cb_row_Q7[1], 7));
diff_Q14[2] = (short)(in_Q14[in_Q14_ptr + 2] - Inlines.silk_LSHIFT(cb_row_Q7[2], 7));
diff_Q14[3] = (short)(in_Q14[in_Q14_ptr + 3] - Inlines.silk_LSHIFT(cb_row_Q7[3], 7));
diff_Q14[4] = (short)(in_Q14[in_Q14_ptr + 4] - Inlines.silk_LSHIFT(cb_row_Q7[4], 7));
/* Weighted rate */
sum1_Q14 = Inlines.silk_SMULBB(mu_Q9, cl_Q5[k]);
/* Penalty for too large gain */
sum1_Q14 = Inlines.silk_ADD_LSHIFT32(sum1_Q14, Inlines.silk_max(Inlines.silk_SUB32(gain_tmp_Q7, max_gain_Q7), 0), 10);
Inlines.OpusAssert(sum1_Q14 >= 0);
/* first row of W_Q18 */
sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 1], diff_Q14[1]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 2], diff_Q14[2]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 3], diff_Q14[3]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 4], diff_Q14[4]);
sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr], diff_Q14[0]);
sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[0]);
/* second row of W_Q18 */
sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 7], diff_Q14[2]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 8], diff_Q14[3]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 9], diff_Q14[4]);
sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 6], diff_Q14[1]);
sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[1]);
/* third row of W_Q18 */
sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 13], diff_Q14[3]);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 14], diff_Q14[4]);
sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 12], diff_Q14[2]);
sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[2]);
/* fourth row of W_Q18 */
sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 19], diff_Q14[4]);
sum2_Q16 = Inlines.silk_LSHIFT(sum2_Q16, 1);
sum2_Q16 = Inlines.silk_SMLAWB(sum2_Q16, W_Q18[W_Q18_ptr + 18], diff_Q14[3]);
sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[3]);
/* last row of W_Q18 */
sum2_Q16 = Inlines.silk_SMULWB(W_Q18[W_Q18_ptr + 24], diff_Q14[4]);
sum1_Q14 = Inlines.silk_SMLAWB(sum1_Q14, sum2_Q16, diff_Q14[4]);
Inlines.OpusAssert(sum1_Q14 >= 0);
/* find best */
if (sum1_Q14 < rate_dist_Q14.Val)
{
rate_dist_Q14.Val = sum1_Q14;
ind.Val = (sbyte)k;
gain_Q7.Val = gain_tmp_Q7;
}
}
}
}
}
@@ -0,0 +1,409 @@
/* 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.
*/
namespace Concentus.Silk
{
using Concentus.Common;
using Concentus.Common.CPlusPlus;
using Concentus.Silk.Enums;
using Concentus.Silk.Structs;
using System.Diagnostics;
/// <summary>
/// Voice Activity Detection module for silk codec
/// </summary>
internal static class VoiceActivityDetection
{
/// <summary>
/// Weighting factors for tilt measure
/// </summary>
private static readonly int[] tiltWeights = { 30000, 6000, -12000, -12000 };
/// <summary>
/// Initialization of the Silk VAD
/// </summary>
/// <param name="psSilk_VAD">O Pointer to Silk VAD state. Cannot be nullptr</param>
/// <returns>0 if success</returns>
internal static int silk_VAD_Init(SilkVADState psSilk_VAD)
{
int b, ret = 0;
/* reset state memory */
psSilk_VAD.Reset();
/* init noise levels */
/* Initialize array with approx pink noise levels (psd proportional to inverse of frequency) */
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
psSilk_VAD.NoiseLevelBias[b] = Inlines.silk_max_32(Inlines.silk_DIV32_16(SilkConstants.VAD_NOISE_LEVELS_BIAS, (short)(b + 1)), 1);
}
/* Initialize state */
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
psSilk_VAD.NL[b] = Inlines.silk_MUL(100, psSilk_VAD.NoiseLevelBias[b]);
psSilk_VAD.inv_NL[b] = Inlines.silk_DIV32(int.MaxValue, psSilk_VAD.NL[b]);
}
psSilk_VAD.counter = 15;
/* init smoothed energy-to-noise ratio*/
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
psSilk_VAD.NrgRatioSmth_Q8[b] = 100 * 256; /* 100 * 256 -. 20 dB SNR */
}
return (ret);
}
/// <summary>
/// Get the speech activity level in Q8
/// </summary>
/// <param name="psEncC">I/O Encoder state</param>
/// <param name="pIn">I PCM input</param>
/// <returns>0 if success</returns>
internal static int silk_VAD_GetSA_Q8(
SilkChannelEncoder psEncC,
short[] pIn,
int pIn_ptr)
{
int SA_Q15, pSNR_dB_Q7, input_tilt;
int decimated_framelength1, decimated_framelength2;
int decimated_framelength;
int dec_subframe_length, dec_subframe_offset, SNR_Q7, i, b, s;
int sumSquared = 0, smooth_coef_Q16;
short HPstateTmp;
short[] X;
int[] Xnrg = new int[SilkConstants.VAD_N_BANDS];
int[] NrgToNoiseRatio_Q8 = new int[SilkConstants.VAD_N_BANDS];
int speech_nrg, x_tmp;
int[] X_offset = new int[SilkConstants.VAD_N_BANDS];
int ret = 0;
SilkVADState psSilk_VAD = psEncC.sVAD;
/* Safety checks */
Inlines.OpusAssert(SilkConstants.VAD_N_BANDS == 4);
Inlines.OpusAssert(SilkConstants.MAX_FRAME_LENGTH >= psEncC.frame_length);
Inlines.OpusAssert(psEncC.frame_length <= 512);
Inlines.OpusAssert(psEncC.frame_length == 8 * Inlines.silk_RSHIFT(psEncC.frame_length, 3));
/***********************/
/* Filter and Decimate */
/***********************/
decimated_framelength1 = Inlines.silk_RSHIFT(psEncC.frame_length, 1);
decimated_framelength2 = Inlines.silk_RSHIFT(psEncC.frame_length, 2);
decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, 3);
/* Decimate into 4 bands:
0 L 3L L 3L 5L
- -- - -- --
8 8 2 4 4
[0-1 kHz| temp. |1-2 kHz| 2-4 kHz | 4-8 kHz |
They're arranged to allow the minimal ( frame_length / 4 ) extra
scratch space during the downsampling process */
X_offset[0] = 0;
X_offset[1] = decimated_framelength + decimated_framelength2;
X_offset[2] = X_offset[1] + decimated_framelength;
X_offset[3] = X_offset[2] + decimated_framelength2;
X = new short[X_offset[3] + decimated_framelength1];
/* 0-8 kHz to 0-4 kHz and 4-8 kHz */
Filters.silk_ana_filt_bank_1(pIn, pIn_ptr, psSilk_VAD.AnaState,
X, X, X_offset[3], psEncC.frame_length);
/* 0-4 kHz to 0-2 kHz and 2-4 kHz */
Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState1,
X, X, X_offset[2], decimated_framelength1);
/* 0-2 kHz to 0-1 kHz and 1-2 kHz */
Filters.silk_ana_filt_bank_1(X, 0, psSilk_VAD.AnaState2,
X, X, X_offset[1], decimated_framelength2);
/*********************************************/
/* HP filter on lowest band (differentiator) */
/*********************************************/
X[decimated_framelength - 1] = (short)(Inlines.silk_RSHIFT(X[decimated_framelength - 1], 1));
HPstateTmp = X[decimated_framelength - 1];
for (i = decimated_framelength - 1; i > 0; i--)
{
X[i - 1] = (short)(Inlines.silk_RSHIFT(X[i - 1], 1));
X[i] -= X[i - 1];
}
X[0] -= psSilk_VAD.HPstate;
psSilk_VAD.HPstate = HPstateTmp;
/*************************************/
/* Calculate the energy in each band */
/*************************************/
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
/* Find the decimated framelength in the non-uniformly divided bands */
decimated_framelength = Inlines.silk_RSHIFT(psEncC.frame_length, Inlines.silk_min_int(SilkConstants.VAD_N_BANDS - b, SilkConstants.VAD_N_BANDS - 1));
/* Split length into subframe lengths */
dec_subframe_length = Inlines.silk_RSHIFT(decimated_framelength, SilkConstants.VAD_INTERNAL_SUBFRAMES_LOG2);
dec_subframe_offset = 0;
/* Compute energy per sub-frame */
/* initialize with summed energy of last subframe */
Xnrg[b] = psSilk_VAD.XnrgSubfr[b];
for (s = 0; s < SilkConstants.VAD_INTERNAL_SUBFRAMES; s++)
{
sumSquared = 0;
for (i = 0; i < dec_subframe_length; i++)
{
/* The energy will be less than dec_subframe_length * ( silk_int16_MIN / 8 ) ^ 2. */
/* Therefore we can accumulate with no risk of overflow (unless dec_subframe_length > 128) */
x_tmp = Inlines.silk_RSHIFT(
X[X_offset[b] + i + dec_subframe_offset], 3);
sumSquared = Inlines.silk_SMLABB(sumSquared, x_tmp, x_tmp);
/* Safety check */
Inlines.OpusAssert(sumSquared >= 0);
}
/* Add/saturate summed energy of current subframe */
if (s < SilkConstants.VAD_INTERNAL_SUBFRAMES - 1)
{
Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], sumSquared);
}
else
{
/* Look-ahead subframe */
Xnrg[b] = Inlines.silk_ADD_POS_SAT32(Xnrg[b], Inlines.silk_RSHIFT(sumSquared, 1));
}
dec_subframe_offset += dec_subframe_length;
}
psSilk_VAD.XnrgSubfr[b] = sumSquared;
}
/********************/
/* Noise estimation */
/********************/
silk_VAD_GetNoiseLevels(Xnrg, psSilk_VAD);
/***********************************************/
/* Signal-plus-noise to noise ratio estimation */
/***********************************************/
sumSquared = 0;
input_tilt = 0;
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
speech_nrg = Xnrg[b] - psSilk_VAD.NL[b];
if (speech_nrg > 0)
{
/* Divide, with sufficient resolution */
if ((Xnrg[b] & 0xFF800000) == 0)
{
NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Inlines.silk_LSHIFT(Xnrg[b], 8), psSilk_VAD.NL[b] + 1);
}
else {
NrgToNoiseRatio_Q8[b] = Inlines.silk_DIV32(Xnrg[b], Inlines.silk_RSHIFT(psSilk_VAD.NL[b], 8) + 1);
}
/* Convert to log domain */
SNR_Q7 = Inlines.silk_lin2log(NrgToNoiseRatio_Q8[b]) - 8 * 128;
/* Sum-of-squares */
sumSquared = Inlines.silk_SMLABB(sumSquared, SNR_Q7, SNR_Q7); /* Q14 */
/* Tilt measure */
if (speech_nrg < ((int)1 << 20))
{
/* Scale down SNR value for small subband speech energies */
SNR_Q7 = Inlines.silk_SMULWB(Inlines.silk_LSHIFT(Inlines.silk_SQRT_APPROX(speech_nrg), 6), SNR_Q7);
}
input_tilt = Inlines.silk_SMLAWB(input_tilt, tiltWeights[b], SNR_Q7);
}
else
{
NrgToNoiseRatio_Q8[b] = 256;
}
}
/* Mean-of-squares */
sumSquared = Inlines.silk_DIV32_16(sumSquared, SilkConstants.VAD_N_BANDS); /* Q14 */
/* Root-mean-square approximation, scale to dBs, and write to output pointer */
pSNR_dB_Q7 = (short)(3 * Inlines.silk_SQRT_APPROX(sumSquared)); /* Q7 */
/*********************************/
/* Speech Probability Estimation */
/*********************************/
SA_Q15 = Sigmoid.silk_sigm_Q15(Inlines.silk_SMULWB(SilkConstants.VAD_SNR_FACTOR_Q16, pSNR_dB_Q7) - SilkConstants.VAD_NEGATIVE_OFFSET_Q5);
/**************************/
/* Frequency Tilt Measure */
/**************************/
psEncC.input_tilt_Q15 = Inlines.silk_LSHIFT(Sigmoid.silk_sigm_Q15(input_tilt) - 16384, 1);
/**************************************************/
/* Scale the sigmoid output based on power levels */
/**************************************************/
speech_nrg = 0;
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
/* Accumulate signal-without-noise energies, higher frequency bands have more weight */
speech_nrg += (b + 1) * Inlines.silk_RSHIFT(Xnrg[b] - psSilk_VAD.NL[b], 4);
}
/* Power scaling */
if (speech_nrg <= 0)
{
SA_Q15 = Inlines.silk_RSHIFT(SA_Q15, 1);
}
else if (speech_nrg < 32768)
{
if (psEncC.frame_length == 10 * psEncC.fs_kHz)
{
speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 16);
}
else
{
speech_nrg = Inlines.silk_LSHIFT_SAT32(speech_nrg, 15);
}
/* square-root */
speech_nrg = Inlines.silk_SQRT_APPROX(speech_nrg);
SA_Q15 = Inlines.silk_SMULWB(32768 + speech_nrg, SA_Q15);
}
/* Copy the resulting speech activity in Q8 */
psEncC.speech_activity_Q8 = Inlines.silk_min_int(Inlines.silk_RSHIFT(SA_Q15, 7), byte.MaxValue);
/***********************************/
/* Energy Level and SNR estimation */
/***********************************/
/* Smoothing coefficient */
smooth_coef_Q16 = Inlines.silk_SMULWB(SilkConstants.VAD_SNR_SMOOTH_COEF_Q18, Inlines.silk_SMULWB((int)SA_Q15, SA_Q15));
if (psEncC.frame_length == 10 * psEncC.fs_kHz)
{
smooth_coef_Q16 >>= 1;
}
for (b = 0; b < SilkConstants.VAD_N_BANDS; b++)
{
/* compute smoothed energy-to-noise ratio per band */
psSilk_VAD.NrgRatioSmth_Q8[b] = Inlines.silk_SMLAWB(psSilk_VAD.NrgRatioSmth_Q8[b],
NrgToNoiseRatio_Q8[b] - psSilk_VAD.NrgRatioSmth_Q8[b], smooth_coef_Q16);
/* signal to noise ratio in dB per band */
SNR_Q7 = 3 * (Inlines.silk_lin2log(psSilk_VAD.NrgRatioSmth_Q8[b]) - 8 * 128);
/* quality = sigmoid( 0.25 * ( SNR_dB - 16 ) ); */
psEncC.input_quality_bands_Q15[b] = Sigmoid.silk_sigm_Q15(Inlines.silk_RSHIFT(SNR_Q7 - 16 * 128, 4));
}
return (ret);
}
/// <summary>
/// Noise level estimation
/// </summary>
/// <param name="pX">I subband energies [VAD_N_BANDS]</param>
/// <param name="psSilk_VAD">I/O Pointer to Silk VAD state</param>
internal static void silk_VAD_GetNoiseLevels(
int[] pX,
SilkVADState psSilk_VAD)
{
int k;
int nl, nrg, inv_nrg;
int coef, min_coef;
/* Initially faster smoothing */
if (psSilk_VAD.counter < 1000)
{ /* 1000 = 20 sec */
min_coef = Inlines.silk_DIV32_16(short.MaxValue, (short)(Inlines.silk_RSHIFT(psSilk_VAD.counter, 4) + 1));
}
else
{
min_coef = 0;
}
for (k = 0; k < SilkConstants.VAD_N_BANDS; k++)
{
/* Get old noise level estimate for current band */
nl = psSilk_VAD.NL[k];
Inlines.OpusAssert(nl >= 0);
/* Add bias */
nrg = Inlines.silk_ADD_POS_SAT32(pX[k], psSilk_VAD.NoiseLevelBias[k]);
Inlines.OpusAssert(nrg > 0);
/* Invert energies */
inv_nrg = Inlines.silk_DIV32(int.MaxValue, nrg);
Inlines.OpusAssert(inv_nrg >= 0);
/* Less update when subband energy is high */
if (nrg > Inlines.silk_LSHIFT(nl, 3))
{
coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 >> 3;
}
else if (nrg < nl)
{
coef = SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16;
}
else
{
coef = Inlines.silk_SMULWB(Inlines.silk_SMULWW(inv_nrg, nl), SilkConstants.VAD_NOISE_LEVEL_SMOOTH_COEF_Q16 << 1);
}
/* Initially faster smoothing */
coef = Inlines.silk_max_int(coef, min_coef);
/* Smooth inverse energies */
psSilk_VAD.inv_NL[k] = Inlines.silk_SMLAWB(psSilk_VAD.inv_NL[k], inv_nrg - psSilk_VAD.inv_NL[k], coef);
Inlines.OpusAssert(psSilk_VAD.inv_NL[k] >= 0);
/* Compute noise level by inverting again */
nl = Inlines.silk_DIV32(int.MaxValue, psSilk_VAD.inv_NL[k]);
Inlines.OpusAssert(nl >= 0);
/* Limit noise levels (guarantee 7 bits of head room) */
nl = Inlines.silk_min(nl, 0x00FFFFFF);
/* Store as part of state */
psSilk_VAD.NL[k] = nl;
}
/* Increment frame counter */
psSilk_VAD.counter++;
}
}
}