Files
LuaCsForBarotraumaEP/Libraries/Concentus/CSharp/Concentus/Silk/Stereo.cs
T

544 lines
27 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;
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];
}
}
}