487 lines
23 KiB
C#
487 lines
23 KiB
C#
/* 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;
|
|
}
|
|
}
|
|
}
|
|
}
|