/*
 * Copyright (c) 2012 Andrew D'Addesio
 * Copyright (c) 2013-2014 Mozilla Corporation
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

/**
 * @file
 * Opus SILK decoder
 */

#include <stdint.h>

#include "opus.h"

typedef struct SilkFrame {
    int coded;
    int log_gain;
    int16_t nlsf[16];
    float    lpc[16];

    float output     [2 * SILK_HISTORY];
    float lpc_history[2 * SILK_HISTORY];
    int primarylag;

    int prev_voiced;
} SilkFrame;

struct SilkContext {
    AVCodecContext *avctx;
    int output_channels;

    int midonly;
    int subframes;
    int sflength;
    int flength;
    int nlsf_interp_factor;

    enum OpusBandwidth bandwidth;
    int wb;

    SilkFrame frame[2];
    float prev_stereo_weights[2];
    float stereo_weights[2];

    int prev_coded_channels;
};

static const uint16_t silk_model_stereo_s1[] = {
    256,   7,   9,  10,  11,  12,  22,  46,  54,  55,  56,  59,  82, 174, 197, 200,
    201, 202, 210, 234, 244, 245, 246, 247, 249, 256
};

static const uint16_t silk_model_stereo_s2[] = {256, 85, 171, 256};

static const uint16_t silk_model_stereo_s3[] = {256, 51, 102, 154, 205, 256};

static const uint16_t silk_model_mid_only[] = {256, 192, 256};

static const uint16_t silk_model_frame_type_inactive[] = {256, 26, 256};

static const uint16_t silk_model_frame_type_active[] = {256, 24, 98, 246, 256};

static const uint16_t silk_model_gain_highbits[3][9] = {
    {256,  32, 144, 212, 241, 253, 254, 255, 256},
    {256,   2,  19,  64, 124, 186, 233, 252, 256},
    {256,   1,   4,  30, 101, 195, 245, 254, 256}
};

static const uint16_t silk_model_gain_lowbits[] = {256, 32, 64, 96, 128, 160, 192, 224, 256};

static const uint16_t silk_model_gain_delta[] = {
    256,   6,  11,  22,  53, 185, 206, 214, 218, 221, 223, 225, 227, 228, 229, 230,
    231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246,
    247, 248, 249, 250, 251, 252, 253, 254, 255, 256
};
static const uint16_t silk_model_lsf_s1[2][2][33] = {
    {
        {    // NB or MB, unvoiced
            256,  44,  78, 108, 127, 148, 160, 171, 174, 177, 179, 195, 197, 199, 200, 205,
            207, 208, 211, 214, 215, 216, 218, 220, 222, 225, 226, 235, 244, 246, 253, 255, 256
        }, { // NB or MB, voiced
            256,   1,  11,  12,  20,  23,  31,  39,  53,  66,  80,  81,  95, 107, 120, 131,
            142, 154, 165, 175, 185, 196, 204, 213, 221, 228, 236, 237, 238, 244, 245, 251, 256
        }
    }, {
        {    // WB, unvoiced
            256,  31,  52,  55,  72,  73,  81,  98, 102, 103, 121, 137, 141, 143, 146, 147,
            157, 158, 161, 177, 188, 204, 206, 208, 211, 213, 224, 225, 229, 238, 246, 253, 256
        }, { // WB, voiced
            256,   1,   5,  21,  26,  44,  55,  60,  74,  89,  90,  93, 105, 118, 132, 146,
            152, 166, 178, 180, 186, 187, 199, 211, 222, 232, 235, 245, 250, 251, 252, 253, 256
        }
    }
};

static const uint16_t silk_model_lsf_s2[32][10] = {
    // NB, MB
    { 256,   1,   2,   3,  18, 242, 253, 254, 255, 256 },
    { 256,   1,   2,   4,  38, 221, 253, 254, 255, 256 },
    { 256,   1,   2,   6,  48, 197, 252, 254, 255, 256 },
    { 256,   1,   2,  10,  62, 185, 246, 254, 255, 256 },
    { 256,   1,   4,  20,  73, 174, 248, 254, 255, 256 },
    { 256,   1,   4,  21,  76, 166, 239, 254, 255, 256 },
    { 256,   1,   8,  32,  85, 159, 226, 252, 255, 256 },
    { 256,   1,   2,  20,  83, 161, 219, 249, 255, 256 },

    // WB
    { 256,   1,   2,   3,  12, 244, 253, 254, 255, 256 },
    { 256,   1,   2,   4,  32, 218, 253, 254, 255, 256 },
    { 256,   1,   2,   5,  47, 199, 252, 254, 255, 256 },
    { 256,   1,   2,  12,  61, 187, 252, 254, 255, 256 },
    { 256,   1,   5,  24,  72, 172, 249, 254, 255, 256 },
    { 256,   1,   2,  16,  70, 170, 242, 254, 255, 256 },
    { 256,   1,   2,  17,  78, 165, 226, 251, 255, 256 },
    { 256,   1,   8,  29,  79, 156, 237, 254, 255, 256 }
};

static const uint16_t silk_model_lsf_s2_ext[] = { 256, 156, 216, 240, 249, 253, 255, 256 };

static const uint16_t silk_model_lsf_interpolation_offset[] = { 256, 13, 35, 64, 75, 256 };

static const uint16_t silk_model_pitch_highbits[] = {
    256,   3,   6,  12,  23,  44,  74, 106, 125, 136, 146, 158, 171, 184, 196, 207,
    216, 224, 231, 237, 241, 243, 245, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256
};

static const uint16_t silk_model_pitch_lowbits_nb[]= { 256, 64, 128, 192, 256 };

static const uint16_t silk_model_pitch_lowbits_mb[]= { 256, 43, 85, 128, 171, 213, 256 };

static const uint16_t silk_model_pitch_lowbits_wb[]= { 256, 32, 64, 96, 128, 160, 192, 224, 256 };

static const uint16_t silk_model_pitch_delta[] = {
    256,  46,  48,  50,  53,  57,  63,  73,  88, 114, 152, 182, 204, 219, 229, 236,
    242, 246, 250, 252, 254, 256
};

static const uint16_t silk_model_pitch_contour_nb10ms[] = { 256, 143, 193, 256 };

static const uint16_t silk_model_pitch_contour_nb20ms[] = {
    256,  68,  80, 101, 118, 137, 159, 189, 213, 230, 246, 256
};

static const uint16_t silk_model_pitch_contour_mbwb10ms[] = {
    256,  91, 137, 176, 195, 209, 221, 229, 236, 242, 247, 252, 256
};

static const uint16_t silk_model_pitch_contour_mbwb20ms[] = {
    256,  33,  55,  73,  89, 104, 118, 132, 145, 158, 168, 177, 186, 194, 200, 206,
    212, 217, 221, 225, 229, 232, 235, 238, 240, 242, 244, 246, 248, 250, 252, 253,
    254, 255, 256
};

static const uint16_t silk_model_ltp_filter[] = { 256, 77, 157, 256 };

static const uint16_t silk_model_ltp_filter0_sel[] = {
    256, 185, 200, 213, 226, 235, 244, 250, 256
};

static const uint16_t silk_model_ltp_filter1_sel[] = {
    256,  57,  91, 112, 132, 147, 160, 172, 185, 195, 205, 214, 224, 233, 241, 248, 256
};

static const uint16_t silk_model_ltp_filter2_sel[] = {
    256,  15,  31,  45,  57,  69,  81,  92, 103, 114, 124, 133, 142, 151, 160, 168,
    176, 184, 192, 199, 206, 212, 218, 223, 227, 232, 236, 240, 244, 247, 251, 254, 256
};

static const uint16_t silk_model_ltp_scale_index[] = { 256, 128, 192, 256 };

static const uint16_t silk_model_lcg_seed[] = { 256, 64, 128, 192, 256 };

static const uint16_t silk_model_exc_rate[2][10] = {
    { 256,  15,  66,  78, 124, 169, 182, 215, 242, 256 }, // unvoiced
    { 256,  33,  63,  99, 116, 150, 199, 217, 238, 256 }  // voiced
};

static const uint16_t silk_model_pulse_count[11][19] = {
    { 256, 131, 205, 230, 238, 241, 244, 245, 246,
      247, 248, 249, 250, 251, 252, 253, 254, 255, 256 },
    { 256,  58, 151, 211, 234, 241, 244, 245, 246,
      247, 248, 249, 250, 251, 252, 253, 254, 255, 256 },
    { 256,  43,  94, 140, 173, 197, 213, 224, 232,
      238, 241, 244, 247, 249, 250, 251, 253, 254, 256 },
    { 256,  17,  69, 140, 197, 228, 240, 245, 246,
      247, 248, 249, 250, 251, 252, 253, 254, 255, 256 },
    { 256,   6,  27,  68, 121, 170, 205, 226, 237,
      243, 246, 248, 250, 251, 252, 253, 254, 255, 256 },
    { 256,   7,  21,  43,  71, 100, 128, 153, 173,
      190, 203, 214, 223, 230, 235, 239, 243, 246, 256 },
    { 256,   2,   7,  21,  50,  92, 138, 179, 210,
      229, 240, 246, 249, 251, 252, 253, 254, 255, 256 },
    { 256,   1,   3,   7,  17,  36,  65, 100, 137,
      171, 199, 219, 233, 241, 246, 250, 252, 254, 256 },
    { 256,   1,   3,   5,  10,  19,  33,  53,  77,
      104, 132, 158, 181, 201, 216, 227, 235, 241, 256 },
    { 256,   1,   2,   3,   9,  36,  94, 150, 189,
      214, 228, 238, 244, 247, 250, 252, 253, 254, 256 },
    { 256,   2,   3,   9,  36,  94, 150, 189, 214,
      228, 238, 244, 247, 250, 252, 253, 254, 256, 256 }
};

static const uint16_t silk_model_pulse_location[4][168] = {
    {
        256, 126, 256,
        256, 56, 198, 256,
        256, 25, 126, 230, 256,
        256, 12, 72, 180, 244, 256,
        256, 7, 42, 126, 213, 250, 256,
        256, 4, 24, 83, 169, 232, 253, 256,
        256, 3, 15, 53, 125, 200, 242, 254, 256,
        256, 2, 10, 35, 89, 162, 221, 248, 255, 256,
        256, 2, 7, 24, 63, 126, 191, 233, 251, 255, 256,
        256, 1, 5, 17, 45, 94, 157, 211, 241, 252, 255, 256,
        256, 1, 5, 13, 33, 70, 125, 182, 223, 245, 253, 255, 256,
        256, 1, 4, 11, 26, 54, 98, 151, 199, 232, 248, 254, 255, 256,
        256, 1, 3, 9, 21, 42, 77, 124, 172, 212, 237, 249, 254, 255, 256,
        256, 1, 2, 6, 16, 33, 60, 97, 144, 187, 220, 241, 250, 254, 255, 256,
        256, 1, 2, 3, 11, 25, 47, 80, 120, 163, 201, 229, 245, 253, 254, 255, 256,
        256, 1, 2, 3, 4, 17, 35, 62, 98, 139, 180, 214, 238, 252, 253, 254, 255, 256
    },{
        256, 127, 256,
        256, 53, 202, 256,
        256, 22, 127, 233, 256,
        256, 11, 72, 183, 246, 256,
        256, 6, 41, 127, 215, 251, 256,
        256, 4, 24, 83, 170, 232, 253, 256,
        256, 3, 16, 56, 127, 200, 241, 254, 256,
        256, 3, 12, 39, 92, 162, 218, 246, 255, 256,
        256, 3, 11, 30, 67, 124, 185, 229, 249, 255, 256,
        256, 3, 10, 25, 53, 97, 151, 200, 233, 250, 255, 256,
        256, 1, 8, 21, 43, 77, 123, 171, 209, 237, 251, 255, 256,
        256, 1, 2, 13, 35, 62, 97, 139, 186, 219, 244, 254, 255, 256,
        256, 1, 2, 8, 22, 48, 85, 128, 171, 208, 234, 248, 254, 255, 256,
        256, 1, 2, 6, 16, 36, 67, 107, 149, 189, 220, 240, 250, 254, 255, 256,
        256, 1, 2, 5, 13, 29, 55, 90, 128, 166, 201, 227, 243, 251, 254, 255, 256,
        256, 1, 2, 4, 10, 22, 43, 73, 109, 147, 183, 213, 234, 246, 252, 254, 255, 256
    },{
        256, 127, 256,
        256, 49, 206, 256,
        256, 20, 127, 236, 256,
        256, 11, 71, 184, 246, 256,
        256, 7, 43, 127, 214, 250, 256,
        256, 6, 30, 87, 169, 229, 252, 256,
        256, 5, 23, 62, 126, 194, 236, 252, 256,
        256, 6, 20, 49, 96, 157, 209, 239, 253, 256,
        256, 1, 16, 39, 74, 125, 175, 215, 245, 255, 256,
        256, 1, 2, 23, 55, 97, 149, 195, 236, 254, 255, 256,
        256, 1, 7, 23, 50, 86, 128, 170, 206, 233, 249, 255, 256,
        256, 1, 6, 18, 39, 70, 108, 148, 186, 217, 238, 250, 255, 256,
        256, 1, 4, 13, 30, 56, 90, 128, 166, 200, 226, 243, 252, 255, 256,
        256, 1, 4, 11, 25, 47, 76, 110, 146, 180, 209, 231, 245, 252, 255, 256,
        256, 1, 3, 8, 19, 37, 62, 93, 128, 163, 194, 219, 237, 248, 253, 255, 256,
        256, 1, 2, 6, 15, 30, 51, 79, 111, 145, 177, 205, 226, 241, 250, 254, 255, 256
    },{
        256, 128, 256,
        256, 42, 214, 256,
        256, 21, 128, 235, 256,
        256, 12, 72, 184, 245, 256,
        256, 8, 42, 128, 214, 249, 256,
        256, 8, 31, 86, 176, 231, 251, 256,
        256, 5, 20, 58, 130, 202, 238, 253, 256,
        256, 6, 18, 45, 97, 174, 221, 241, 251, 256,
        256, 6, 25, 53, 88, 128, 168, 203, 231, 250, 256,
        256, 4, 18, 40, 71, 108, 148, 185, 216, 238, 252, 256,
        256, 3, 13, 31, 57, 90, 128, 166, 199, 225, 243, 253, 256,
        256, 2, 10, 23, 44, 73, 109, 147, 183, 212, 233, 246, 254, 256,
        256, 1, 6, 16, 33, 58, 90, 128, 166, 198, 223, 240, 250, 255, 256,
        256, 1, 5, 12, 25, 46, 75, 110, 146, 181, 210, 231, 244, 251, 255, 256,
        256, 1, 3, 8, 18, 35, 60, 92, 128, 164, 196, 221, 238, 248, 253, 255, 256,
        256, 1, 3, 7, 14, 27, 48, 76, 110, 146, 180, 208, 229, 242, 249, 253, 255, 256
    }
};

static const uint16_t silk_model_excitation_lsb[] = {256, 136, 256};

static const uint16_t silk_model_excitation_sign[3][2][7][3] = {
    {    // Inactive
        {    // Low offset
            {256,   2, 256},
            {256, 207, 256},
            {256, 189, 256},
            {256, 179, 256},
            {256, 174, 256},
            {256, 163, 256},
            {256, 157, 256}
        }, { // High offset
            {256,  58, 256},
            {256, 245, 256},
            {256, 238, 256},
            {256, 232, 256},
            {256, 225, 256},
            {256, 220, 256},
            {256, 211, 256}
        }
    }, { // Unvoiced
        {    // Low offset
            {256,   1, 256},
            {256, 210, 256},
            {256, 190, 256},
            {256, 178, 256},
            {256, 169, 256},
            {256, 162, 256},
            {256, 152, 256}
        }, { // High offset
            {256,  48, 256},
            {256, 242, 256},
            {256, 235, 256},
            {256, 224, 256},
            {256, 214, 256},
            {256, 205, 256},
            {256, 190, 256}
        }
    }, { // Voiced
        {    // Low offset
            {256,   1, 256},
            {256, 162, 256},
            {256, 152, 256},
            {256, 147, 256},
            {256, 144, 256},
            {256, 141, 256},
            {256, 138, 256}
        }, { // High offset
            {256,   8, 256},
            {256, 203, 256},
            {256, 187, 256},
            {256, 176, 256},
            {256, 168, 256},
            {256, 161, 256},
            {256, 154, 256}
        }
    }
};

static const int16_t silk_stereo_weights[] = {
    -13732, -10050,  -8266,  -7526,  -6500,  -5000,  -2950,   -820,
       820,   2950,   5000,   6500,   7526,   8266,  10050,  13732
};

static const uint8_t silk_lsf_s2_model_sel_nbmb[32][10] = {
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 3, 1, 2, 2, 1, 2, 1, 1, 1 },
    { 2, 1, 1, 1, 1, 1, 1, 1, 1, 1 },
    { 1, 2, 2, 2, 2, 1, 2, 1, 1, 1 },
    { 2, 3, 3, 3, 3, 2, 2, 2, 2, 2 },
    { 0, 5, 3, 3, 2, 2, 2, 2, 1, 1 },
    { 0, 2, 2, 2, 2, 2, 2, 2, 2, 1 },
    { 2, 3, 6, 4, 4, 4, 5, 4, 5, 5 },
    { 2, 4, 5, 5, 4, 5, 4, 6, 4, 4 },
    { 2, 4, 4, 7, 4, 5, 4, 5, 5, 4 },
    { 4, 3, 3, 3, 2, 3, 2, 2, 2, 2 },
    { 1, 5, 5, 6, 4, 5, 4, 5, 5, 5 },
    { 2, 7, 4, 6, 5, 5, 5, 5, 5, 5 },
    { 2, 7, 5, 5, 5, 5, 5, 6, 5, 4 },
    { 3, 3, 5, 4, 4, 5, 4, 5, 4, 4 },
    { 2, 3, 3, 5, 5, 4, 4, 4, 4, 4 },
    { 2, 4, 4, 6, 4, 5, 4, 5, 5, 5 },
    { 2, 5, 4, 6, 5, 5, 5, 4, 5, 4 },
    { 2, 7, 4, 5, 4, 5, 4, 5, 5, 5 },
    { 2, 5, 4, 6, 7, 6, 5, 6, 5, 4 },
    { 3, 6, 7, 4, 6, 5, 5, 6, 4, 5 },
    { 2, 7, 6, 4, 4, 4, 5, 4, 5, 5 },
    { 4, 5, 5, 4, 6, 6, 5, 6, 5, 4 },
    { 2, 5, 5, 6, 5, 6, 4, 6, 4, 4 },
    { 4, 5, 5, 5, 3, 7, 4, 5, 5, 4 },
    { 2, 3, 4, 5, 5, 6, 4, 5, 5, 4 },
    { 2, 3, 2, 3, 3, 4, 2, 3, 3, 3 },
    { 1, 1, 2, 2, 2, 2, 2, 3, 2, 2 },
    { 4, 5, 5, 6, 6, 6, 5, 6, 4, 5 },
    { 3, 5, 5, 4, 4, 4, 4, 3, 3, 2 },
    { 2, 5, 3, 7, 5, 5, 4, 4, 5, 4 },
    { 4, 4, 5, 4, 5, 6, 5, 6, 5, 4 }
};

static const uint8_t silk_lsf_s2_model_sel_wb[32][16] = {
    {  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8 },
    { 10, 11, 11, 11, 11, 11, 10, 10, 10, 10, 10,  9,  9,  9,  8, 11 },
    { 10, 13, 13, 11, 15, 12, 12, 13, 10, 13, 12, 13, 13, 12, 11, 11 },
    {  8, 10,  9, 10, 10,  9,  9,  9,  9,  9,  8,  8,  8,  8,  8,  9 },
    {  8, 14, 13, 12, 14, 12, 15, 13, 12, 12, 12, 13, 13, 12, 12, 11 },
    {  8, 11, 13, 13, 12, 11, 11, 13, 11, 11, 11, 11, 11, 11, 10, 12 },
    {  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8 },
    {  8, 10, 14, 11, 15, 10, 13, 11, 12, 13, 13, 12, 11, 11, 10, 11 },
    {  8, 14, 10, 14, 14, 12, 13, 12, 14, 13, 12, 12, 13, 11, 11, 11 },
    { 10,  9,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8 },
    {  8,  9,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  9 },
    { 10, 10, 11, 12, 13, 11, 11, 11, 11, 11, 11, 11, 10, 10,  9, 11 },
    { 10, 10, 11, 11, 12, 11, 11, 11, 11, 11, 11, 11, 11, 10,  9, 11 },
    { 11, 12, 12, 12, 14, 12, 12, 13, 11, 13, 12, 12, 13, 12, 11, 12 },
    {  8, 14, 12, 13, 12, 15, 13, 10, 14, 13, 15, 12, 12, 11, 13, 11 },
    {  8,  9,  8,  9,  9,  9,  9,  9,  9,  9,  8,  8,  8,  8,  9,  8 },
    {  9, 14, 13, 15, 13, 12, 13, 11, 12, 13, 12, 12, 12, 11, 11, 12 },
    {  9, 11, 11, 12, 12, 11, 11, 13, 10, 11, 11, 13, 13, 13, 11, 12 },
    { 10, 11, 11, 10, 10, 10, 11, 10,  9, 10,  9, 10,  9,  9,  9, 12 },
    {  8, 10, 11, 13, 11, 11, 10, 10, 10,  9,  9,  8,  8,  8,  8,  8 },
    { 11, 12, 11, 13, 11, 11, 10, 10,  9,  9,  9,  9,  9, 10, 10, 12 },
    { 10, 14, 11, 15, 15, 12, 13, 12, 13, 11, 13, 11, 11, 10, 11, 11 },
    { 10, 11, 13, 14, 14, 11, 13, 11, 12, 12, 11, 11, 11, 11, 10, 12 },
    {  9, 11, 11, 12, 12, 12, 12, 11, 13, 13, 13, 11,  9,  9,  9,  9 },
    { 10, 13, 11, 14, 14, 12, 15, 12, 12, 13, 11, 12, 12, 11, 11, 11 },
    {  8, 14,  9,  9,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8,  8 },
    {  8, 14, 14, 11, 13, 10, 13, 13, 11, 12, 12, 15, 15, 12, 12, 12 },
    { 11, 11, 15, 11, 13, 12, 11, 11, 11, 10, 10, 11, 11, 11, 10, 11 },
    {  8,  8,  9,  8,  8,  8, 10,  9, 10,  9,  9, 10, 10, 10,  9,  9 },
    {  8, 11, 10, 13, 11, 11, 10, 11, 10,  9,  8,  8,  9,  8,  8,  9 },
    { 11, 13, 13, 12, 15, 13, 11, 11, 10, 11, 10, 10,  9,  8,  9,  8 },
    { 10, 11, 13, 11, 12, 11, 11, 11, 10,  9, 10, 14, 12,  8,  8,  8 }
};

static const uint8_t silk_lsf_pred_weights_nbmb[2][9] = {
    {179, 138, 140, 148, 151, 149, 153, 151, 163},
    {116,  67,  82,  59,  92,  72, 100,  89,  92}
};

static const uint8_t silk_lsf_pred_weights_wb[2][15] = {
    {175, 148, 160, 176, 178, 173, 174, 164, 177, 174, 196, 182, 198, 192, 182},
    { 68,  62,  66,  60,  72, 117,  85,  90, 118, 136, 151, 142, 160, 142, 155}
};

static const uint8_t silk_lsf_weight_sel_nbmb[32][9] = {
    { 0, 1, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 1, 1, 0, 0, 0, 0, 1, 0 },
    { 0, 1, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 1, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 0, 1, 1, 0, 0, 0, 1, 0 },
    { 0, 1, 1, 0, 0, 1, 1, 0, 0 },
    { 0, 0, 1, 1, 0, 1, 0, 1, 1 },
    { 0, 0, 1, 1, 0, 0, 1, 1, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 1, 0, 1, 1, 1, 1, 1, 0 },
    { 0, 1, 0, 1, 1, 1, 1, 1, 0 },
    { 0, 1, 1, 1, 1, 1, 1, 1, 0 },
    { 1, 0, 1, 1, 0, 1, 1, 1, 1 },
    { 0, 1, 1, 1, 1, 1, 0, 1, 0 },
    { 0, 0, 1, 1, 0, 1, 0, 1, 0 },
    { 0, 0, 1, 1, 1, 0, 1, 1, 1 },
    { 0, 1, 1, 0, 0, 1, 1, 1, 0 },
    { 0, 0, 0, 1, 1, 1, 0, 1, 0 },
    { 0, 1, 1, 0, 0, 1, 0, 1, 0 },
    { 0, 1, 1, 0, 0, 0, 1, 1, 0 },
    { 0, 0, 0, 0, 0, 1, 1, 1, 1 },
    { 0, 0, 1, 1, 0, 0, 0, 1, 1 },
    { 0, 0, 0, 1, 0, 1, 1, 1, 1 },
    { 0, 1, 1, 1, 1, 1, 1, 1, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 1, 1, 0, 1, 0 },
    { 1, 0, 0, 1, 0, 0, 0, 0, 0 },
    { 0, 0, 0, 1, 1, 0, 1, 0, 1 },
    { 1, 0, 1, 1, 0, 1, 1, 1, 1 }
};

static const uint8_t silk_lsf_weight_sel_wb[32][15] = {
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
    { 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 0 },
    { 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0 },
    { 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1 },
    { 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0 },
    { 0, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0 },
    { 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0 },
    { 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 },
    { 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0 },
    { 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0 },
    { 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 },
    { 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 0 },
    { 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 0 },
    { 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0 },
    { 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 1 },
    { 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1 },
    { 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 },
    { 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0 }
};

static const uint8_t silk_lsf_codebook_nbmb[32][10] = {
    { 12,  35,  60,  83, 108, 132, 157, 180, 206, 228 },
    { 15,  32,  55,  77, 101, 125, 151, 175, 201, 225 },
    { 19,  42,  66,  89, 114, 137, 162, 184, 209, 230 },
    { 12,  25,  50,  72,  97, 120, 147, 172, 200, 223 },
    { 26,  44,  69,  90, 114, 135, 159, 180, 205, 225 },
    { 13,  22,  53,  80, 106, 130, 156, 180, 205, 228 },
    { 15,  25,  44,  64,  90, 115, 142, 168, 196, 222 },
    { 19,  24,  62,  82, 100, 120, 145, 168, 190, 214 },
    { 22,  31,  50,  79, 103, 120, 151, 170, 203, 227 },
    { 21,  29,  45,  65, 106, 124, 150, 171, 196, 224 },
    { 30,  49,  75,  97, 121, 142, 165, 186, 209, 229 },
    { 19,  25,  52,  70,  93, 116, 143, 166, 192, 219 },
    { 26,  34,  62,  75,  97, 118, 145, 167, 194, 217 },
    { 25,  33,  56,  70,  91, 113, 143, 165, 196, 223 },
    { 21,  34,  51,  72,  97, 117, 145, 171, 196, 222 },
    { 20,  29,  50,  67,  90, 117, 144, 168, 197, 221 },
    { 22,  31,  48,  66,  95, 117, 146, 168, 196, 222 },
    { 24,  33,  51,  77, 116, 134, 158, 180, 200, 224 },
    { 21,  28,  70,  87, 106, 124, 149, 170, 194, 217 },
    { 26,  33,  53,  64,  83, 117, 152, 173, 204, 225 },
    { 27,  34,  65,  95, 108, 129, 155, 174, 210, 225 },
    { 20,  26,  72,  99, 113, 131, 154, 176, 200, 219 },
    { 34,  43,  61,  78,  93, 114, 155, 177, 205, 229 },
    { 23,  29,  54,  97, 124, 138, 163, 179, 209, 229 },
    { 30,  38,  56,  89, 118, 129, 158, 178, 200, 231 },
    { 21,  29,  49,  63,  85, 111, 142, 163, 193, 222 },
    { 27,  48,  77, 103, 133, 158, 179, 196, 215, 232 },
    { 29,  47,  74,  99, 124, 151, 176, 198, 220, 237 },
    { 33,  42,  61,  76,  93, 121, 155, 174, 207, 225 },
    { 29,  53,  87, 112, 136, 154, 170, 188, 208, 227 },
    { 24,  30,  52,  84, 131, 150, 166, 186, 203, 229 },
    { 37,  48,  64,  84, 104, 118, 156, 177, 201, 230 }
};

static const uint8_t silk_lsf_codebook_wb[32][16] = {
    {  7,  23,  38,  54,  69,  85, 100, 116, 131, 147, 162, 178, 193, 208, 223, 239 },
    { 13,  25,  41,  55,  69,  83,  98, 112, 127, 142, 157, 171, 187, 203, 220, 236 },
    { 15,  21,  34,  51,  61,  78,  92, 106, 126, 136, 152, 167, 185, 205, 225, 240 },
    { 10,  21,  36,  50,  63,  79,  95, 110, 126, 141, 157, 173, 189, 205, 221, 237 },
    { 17,  20,  37,  51,  59,  78,  89, 107, 123, 134, 150, 164, 184, 205, 224, 240 },
    { 10,  15,  32,  51,  67,  81,  96, 112, 129, 142, 158, 173, 189, 204, 220, 236 },
    {  8,  21,  37,  51,  65,  79,  98, 113, 126, 138, 155, 168, 179, 192, 209, 218 },
    { 12,  15,  34,  55,  63,  78,  87, 108, 118, 131, 148, 167, 185, 203, 219, 236 },
    { 16,  19,  32,  36,  56,  79,  91, 108, 118, 136, 154, 171, 186, 204, 220, 237 },
    { 11,  28,  43,  58,  74,  89, 105, 120, 135, 150, 165, 180, 196, 211, 226, 241 },
    {  6,  16,  33,  46,  60,  75,  92, 107, 123, 137, 156, 169, 185, 199, 214, 225 },
    { 11,  19,  30,  44,  57,  74,  89, 105, 121, 135, 152, 169, 186, 202, 218, 234 },
    { 12,  19,  29,  46,  57,  71,  88, 100, 120, 132, 148, 165, 182, 199, 216, 233 },
    { 17,  23,  35,  46,  56,  77,  92, 106, 123, 134, 152, 167, 185, 204, 222, 237 },
    { 14,  17,  45,  53,  63,  75,  89, 107, 115, 132, 151, 171, 188, 206, 221, 240 },
    {  9,  16,  29,  40,  56,  71,  88, 103, 119, 137, 154, 171, 189, 205, 222, 237 },
    { 16,  19,  36,  48,  57,  76,  87, 105, 118, 132, 150, 167, 185, 202, 218, 236 },
    { 12,  17,  29,  54,  71,  81,  94, 104, 126, 136, 149, 164, 182, 201, 221, 237 },
    { 15,  28,  47,  62,  79,  97, 115, 129, 142, 155, 168, 180, 194, 208, 223, 238 },
    {  8,  14,  30,  45,  62,  78,  94, 111, 127, 143, 159, 175, 192, 207, 223, 239 },
    { 17,  30,  49,  62,  79,  92, 107, 119, 132, 145, 160, 174, 190, 204, 220, 235 },
    { 14,  19,  36,  45,  61,  76,  91, 108, 121, 138, 154, 172, 189, 205, 222, 238 },
    { 12,  18,  31,  45,  60,  76,  91, 107, 123, 138, 154, 171, 187, 204, 221, 236 },
    { 13,  17,  31,  43,  53,  70,  83, 103, 114, 131, 149, 167, 185, 203, 220, 237 },
    { 17,  22,  35,  42,  58,  78,  93, 110, 125, 139, 155, 170, 188, 206, 224, 240 },
    {  8,  15,  34,  50,  67,  83,  99, 115, 131, 146, 162, 178, 193, 209, 224, 239 },
    { 13,  16,  41,  66,  73,  86,  95, 111, 128, 137, 150, 163, 183, 206, 225, 241 },
    { 17,  25,  37,  52,  63,  75,  92, 102, 119, 132, 144, 160, 175, 191, 212, 231 },
    { 19,  31,  49,  65,  83, 100, 117, 133, 147, 161, 174, 187, 200, 213, 227, 242 },
    { 18,  31,  52,  68,  88, 103, 117, 126, 138, 149, 163, 177, 192, 207, 223, 239 },
    { 16,  29,  47,  61,  76,  90, 106, 119, 133, 147, 161, 176, 193, 209, 224, 240 },
    { 15,  21,  35,  50,  61,  73,  86,  97, 110, 119, 129, 141, 175, 198, 218, 237 }
};

static const uint16_t silk_lsf_min_spacing_nbmb[] = {
    250, 3, 6, 3, 3, 3, 4, 3, 3, 3, 461
};

static const uint16_t silk_lsf_min_spacing_wb[] = {
    100, 3, 40, 3, 3, 3, 5, 14, 14, 10, 11, 3, 8, 9, 7, 3, 347
};

static const uint8_t silk_lsf_ordering_nbmb[] = {
    0, 9, 6, 3, 4, 5, 8, 1, 2, 7
};

static const uint8_t silk_lsf_ordering_wb[] = {
    0, 15, 8, 7, 4, 11, 12, 3, 2, 13, 10, 5, 6, 9, 14, 1
};

static const int16_t silk_cosine[] = { /* (0.12) */
     4096,  4095,  4091,  4085,
     4076,  4065,  4052,  4036,
     4017,  3997,  3973,  3948,
     3920,  3889,  3857,  3822,
     3784,  3745,  3703,  3659,
     3613,  3564,  3513,  3461,
     3406,  3349,  3290,  3229,
     3166,  3102,  3035,  2967,
     2896,  2824,  2751,  2676,
     2599,  2520,  2440,  2359,
     2276,  2191,  2106,  2019,
     1931,  1842,  1751,  1660,
     1568,  1474,  1380,  1285,
     1189,  1093,   995,   897,
      799,   700,   601,   501,
      401,   301,   201,   101,
        0,  -101,  -201,  -301,
     -401,  -501,  -601,  -700,
     -799,  -897,  -995, -1093,
    -1189, -1285, -1380, -1474,
    -1568, -1660, -1751, -1842,
    -1931, -2019, -2106, -2191,
    -2276, -2359, -2440, -2520,
    -2599, -2676, -2751, -2824,
    -2896, -2967, -3035, -3102,
    -3166, -3229, -3290, -3349,
    -3406, -3461, -3513, -3564,
    -3613, -3659, -3703, -3745,
    -3784, -3822, -3857, -3889,
    -3920, -3948, -3973, -3997,
    -4017, -4036, -4052, -4065,
    -4076, -4085, -4091, -4095,
    -4096
};

static const uint16_t silk_pitch_scale[]   = {  4,   6,   8};

static const uint16_t silk_pitch_min_lag[] = { 16,  24,  32};

static const uint16_t silk_pitch_max_lag[] = {144, 216, 288};

static const int8_t silk_pitch_offset_nb10ms[3][2] = {
    { 0,  0},
    { 1,  0},
    { 0,  1}
};

static const int8_t silk_pitch_offset_nb20ms[11][4] = {
    { 0,  0,  0,  0},
    { 2,  1,  0, -1},
    {-1,  0,  1,  2},
    {-1,  0,  0,  1},
    {-1,  0,  0,  0},
    { 0,  0,  0,  1},
    { 0,  0,  1,  1},
    { 1,  1,  0,  0},
    { 1,  0,  0,  0},
    { 0,  0,  0, -1},
    { 1,  0,  0, -1}
};

static const int8_t silk_pitch_offset_mbwb10ms[12][2] = {
    { 0,  0},
    { 0,  1},
    { 1,  0},
    {-1,  1},
    { 1, -1},
    {-1,  2},
    { 2, -1},
    {-2,  2},
    { 2, -2},
    {-2,  3},
    { 3, -2},
    {-3,  3}
};

static const int8_t silk_pitch_offset_mbwb20ms[34][4] = {
    { 0,  0,  0,  0},
    { 0,  0,  1,  1},
    { 1,  1,  0,  0},
    {-1,  0,  0,  0},
    { 0,  0,  0,  1},
    { 1,  0,  0,  0},
    {-1,  0,  0,  1},
    { 0,  0,  0, -1},
    {-1,  0,  1,  2},
    { 1,  0,  0, -1},
    {-2, -1,  1,  2},
    { 2,  1,  0, -1},
    {-2,  0,  0,  2},
    {-2,  0,  1,  3},
    { 2,  1, -1, -2},
    {-3, -1,  1,  3},
    { 2,  0,  0, -2},
    { 3,  1,  0, -2},
    {-3, -1,  2,  4},
    {-4, -1,  1,  4},
    { 3,  1, -1, -3},
    {-4, -1,  2,  5},
    { 4,  2, -1, -3},
    { 4,  1, -1, -4},
    {-5, -1,  2,  6},
    { 5,  2, -1, -4},
    {-6, -2,  2,  6},
    {-5, -2,  2,  5},
    { 6,  2, -1, -5},
    {-7, -2,  3,  8},
    { 6,  2, -2, -6},
    { 5,  2, -2, -5},
    { 8,  3, -2, -7},
    {-9, -3,  3,  9}
};

static const int8_t silk_ltp_filter0_taps[8][5] = {
    {  4,   6,  24,   7,   5},
    {  0,   0,   2,   0,   0},
    { 12,  28,  41,  13,  -4},
    { -9,  15,  42,  25,  14},
    {  1,  -2,  62,  41,  -9},
    {-10,  37,  65,  -4,   3},
    { -6,   4,  66,   7,  -8},
    { 16,  14,  38,  -3,  33}
};

static const int8_t silk_ltp_filter1_taps[16][5] = {
    { 13,  22,  39,  23,  12},
    { -1,  36,  64,  27,  -6},
    { -7,  10,  55,  43,  17},
    {  1,   1,   8,   1,   1},
    {  6, -11,  74,  53,  -9},
    {-12,  55,  76, -12,   8},
    { -3,   3,  93,  27,  -4},
    { 26,  39,  59,   3,  -8},
    {  2,   0,  77,  11,   9},
    { -8,  22,  44,  -6,   7},
    { 40,   9,  26,   3,   9},
    { -7,  20, 101,  -7,   4},
    {  3,  -8,  42,  26,   0},
    {-15,  33,  68,   2,  23},
    { -2,  55,  46,  -2,  15},
    {  3,  -1,  21,  16,  41}
};

static const int8_t silk_ltp_filter2_taps[32][5] = {
    { -6,  27,  61,  39,   5},
    {-11,  42,  88,   4,   1},
    { -2,  60,  65,   6,  -4},
    { -1,  -5,  73,  56,   1},
    { -9,  19,  94,  29,  -9},
    {  0,  12,  99,   6,   4},
    {  8, -19, 102,  46, -13},
    {  3,   2,  13,   3,   2},
    {  9, -21,  84,  72, -18},
    {-11,  46, 104, -22,   8},
    { 18,  38,  48,  23,   0},
    {-16,  70,  83, -21,  11},
    {  5, -11, 117,  22,  -8},
    { -6,  23, 117, -12,   3},
    {  3,  -8,  95,  28,   4},
    {-10,  15,  77,  60, -15},
    { -1,   4, 124,   2,  -4},
    {  3,  38,  84,  24, -25},
    {  2,  13,  42,  13,  31},
    { 21,  -4,  56,  46,  -1},
    { -1,  35,  79, -13,  19},
    { -7,  65,  88,  -9, -14},
    { 20,   4,  81,  49, -29},
    { 20,   0,  75,   3, -17},
    {  5,  -9,  44,  92,  -8},
    {  1,  -3,  22,  69,  31},
    { -6,  95,  41, -12,   5},
    { 39,  67,  16,  -4,   1},
    {  0,  -6, 120,  55, -36},
    {-13,  44, 122,   4, -24},
    { 81,   5,  11,   3,   7},
    {  2,   0,   9,  10,  88}
};

static const uint16_t silk_ltp_scale_factor[] = {15565, 12288, 8192};

static const uint8_t silk_shell_blocks[3][2] = {
    { 5, 10}, // NB
    { 8, 15}, // MB
    {10, 20}  // WB
};

static const uint8_t silk_quant_offset[2][2] = { /* (0.23) */
    {25, 60}, // Inactive or Unvoiced
    { 8, 25}  // Voiced
};

static const int silk_stereo_interp_len[3] = {
    64, 96, 128
};

static inline void silk_stabilize_lsf(int16_t nlsf[16], int order, const uint16_t min_delta[17])
{
    int pass, i;
    for (pass = 0; pass < 20; pass++) {
        int k, min_diff = 0;
        for (i = 0; i < order+1; i++) {
            int low  = i != 0     ? nlsf[i-1] : 0;
            int high = i != order ? nlsf[i]   : 32768;
            int diff = (high - low) - (min_delta[i]);

            if (diff < min_diff) {
                min_diff = diff;
                k = i;

                if (pass == 20)
                    break;
            }
        }
        if (min_diff == 0) /* no issues; stabilized */
            return;

        /* wiggle one or two LSFs */
        if (k == 0) {
            /* repel away from lower bound */
            nlsf[0] = min_delta[0];
        } else if (k == order) {
            /* repel away from higher bound */
            nlsf[order-1] = 32768 - min_delta[order];
        } else {
            /* repel away from current position */
            int min_center = 0, max_center = 32768, center_val;

            /* lower extent */
            for (i = 0; i < k; i++)
                min_center += min_delta[i];
            min_center += min_delta[k] >> 1;

            /* upper extent */
            for (i = order; i > k; i--)
                max_center -= min_delta[i];
            max_center -= min_delta[k] >> 1;

            /* move apart */
            center_val = nlsf[k - 1] + nlsf[k];
            center_val = (center_val >> 1) + (center_val & 1); // rounded divide by 2
            center_val = FFMIN(max_center, FFMAX(min_center, center_val));

            nlsf[k - 1] = center_val - (min_delta[k] >> 1);
            nlsf[k]     = nlsf[k - 1] + min_delta[k];
        }
    }

    /* resort to the fall-back method, the standard method for LSF stabilization */

    /* sort; as the LSFs should be nearly sorted, use insertion sort */
    for (i = 1; i < order; i++) {
        int j, value = nlsf[i];
        for (j = i - 1; j >= 0 && nlsf[j] > value; j--)
            nlsf[j + 1] = nlsf[j];
        nlsf[j + 1] = value;
    }

    /* push forwards to increase distance */
    if (nlsf[0] < min_delta[0])
        nlsf[0] = min_delta[0];
    for (i = 1; i < order; i++)
        if (nlsf[i] < nlsf[i - 1] + min_delta[i])
            nlsf[i] = nlsf[i - 1] + min_delta[i];

    /* push backwards to increase distance */
    if (nlsf[order-1] > 32768 - min_delta[order])
        nlsf[order-1] = 32768 - min_delta[order];
    for (i = order-2; i >= 0; i--)
        if (nlsf[i] > nlsf[i + 1] - min_delta[i+1])
            nlsf[i] = nlsf[i + 1] - min_delta[i+1];

    return;
}

static inline int silk_is_lpc_stable(const int16_t lpc[16], int order)
{
    int k, j, DC_resp = 0;
    int32_t lpc32[2][16];       // Q24
    int totalinvgain = 1 << 30; // 1.0 in Q30
    int32_t *row = lpc32[0], *prevrow;

    /* initialize the first row for the Levinson recursion */
    for (k = 0; k < order; k++) {
        DC_resp += lpc[k];
        row[k] = lpc[k] * 4096;
    }

    if (DC_resp >= 4096)
        return 0;

    /* check if prediction gain pushes any coefficients too far */
    for (k = order - 1; 1; k--) {
        int rc;      // Q31; reflection coefficient
        int gaindiv; // Q30; inverse of the gain (the divisor)
        int gain;    // gain for this reflection coefficient
        int fbits;   // fractional bits used for the gain
        int error;   // Q29; estimate of the error of our partial estimate of 1/gaindiv

        if (FFABS(row[k]) > 16773022)
            return 0;

        rc      = -(row[k] * 128);
        gaindiv = (1 << 30) - MULH(rc, rc);

        totalinvgain = MULH(totalinvgain, gaindiv) << 2;
        if (k == 0)
            return (totalinvgain >= 107374);

        /* approximate 1.0/gaindiv */
        fbits = opus_ilog(gaindiv);
        gain  = ((1 << 29) - 1) / (gaindiv >> (fbits + 1 - 16)); // Q<fbits-16>
        error = (1 << 29) - MULL(gaindiv << (15 + 16 - fbits), gain, 16);
        gain  = ((gain << 16) + (error * gain >> 13));

        /* switch to the next row of the LPC coefficients */
        prevrow = row;
        row = lpc32[k & 1];

        for (j = 0; j < k; j++) {
            int x = prevrow[j] - ROUND_MULL(prevrow[k - j - 1], rc, 31);
            row[j] = ROUND_MULL(x, gain, fbits);
        }
    }
}

static void silk_lsp2poly(const int32_t lsp[16], int32_t pol[16], int half_order)
{
    int i, j;

    pol[0] = 65536; // 1.0 in Q16
    pol[1] = -lsp[0];

    for (i = 1; i < half_order; i++) {
        pol[i + 1] = pol[i - 1] * 2 - ROUND_MULL(lsp[2 * i], pol[i], 16);
        for (j = i; j > 1; j--)
            pol[j] += pol[j - 2] - ROUND_MULL(lsp[2 * i], pol[j - 1], 16);

        pol[1] -= lsp[2 * i];
    }
}

static void silk_lsf2lpc(const int16_t nlsf[16], float lpcf[16], int order)
{
    int i, k;
    int32_t lsp[16];     // Q17; 2*cos(LSF)
    int32_t p[9], q[9];  // Q16
    int32_t lpc32[16];   // Q17
    int16_t lpc[16];     // Q12

    /* convert the LSFs to LSPs, i.e. 2*cos(LSF) */
    for (k = 0; k < order; k++) {
        int index = nlsf[k] >> 8;
        int offset = nlsf[k] & 255;
        int k2 = (order == 10) ? silk_lsf_ordering_nbmb[k] : silk_lsf_ordering_wb[k];

        /* interpolate and round */
        lsp[k2]  = silk_cosine[index] * 256;
        lsp[k2] += (silk_cosine[index + 1] - silk_cosine[index]) * offset;
        lsp[k2]  = (lsp[k2] + 4) >> 3;
    }

    silk_lsp2poly(lsp    , p, order >> 1);
    silk_lsp2poly(lsp + 1, q, order >> 1);

    /* reconstruct A(z) */
    for (k = 0; k < order>>1; k++) {
        lpc32[k]         = -p[k + 1] - p[k] - q[k + 1] + q[k];
        lpc32[order-k-1] = -p[k + 1] - p[k] + q[k + 1] - q[k];
    }

    /* limit the range of the LPC coefficients to each fit within an int16_t */
    for (i = 0; i < 10; i++) {
        int j;
        unsigned int maxabs = 0;
        for (j = 0, k = 0; j < order; j++) {
            unsigned int x = FFABS(lpc32[k]);
            if (x > maxabs) {
                maxabs = x; // Q17
                k      = j;
            }
        }

        maxabs = (maxabs + 16) >> 5; // convert to Q12

        if (maxabs > 32767) {
            /* perform bandwidth expansion */
            unsigned int chirp, chirp_base; // Q16
            maxabs = FFMIN(maxabs, 163838); // anything above this overflows chirp's numerator
            chirp_base = chirp = 65470 - ((maxabs - 32767) << 14) / ((maxabs * (k+1)) >> 2);

            for (k = 0; k < order; k++) {
                lpc32[k] = ROUND_MULL(lpc32[k], chirp, 16);
                chirp    = (chirp_base * chirp + 32768) >> 16;
            }
        } else break;
    }

    if (i == 10) {
        /* time's up: just clamp */
        for (k = 0; k < order; k++) {
            int x = (lpc32[k] + 16) >> 5;
            lpc[k] = av_clip_int16(x);
            lpc32[k] = lpc[k] << 5; // shortcut mandated by the spec; drops lower 5 bits
        }
    } else {
        for (k = 0; k < order; k++)
            lpc[k] = (lpc32[k] + 16) >> 5;
    }

    /* if the prediction gain causes the LPC filter to become unstable,
       apply further bandwidth expansion on the Q17 coefficients */
    for (i = 1; i <= 16 && !silk_is_lpc_stable(lpc, order); i++) {
        unsigned int chirp, chirp_base;
        chirp_base = chirp = 65536 - (1 << i);

        for (k = 0; k < order; k++) {
            lpc32[k] = ROUND_MULL(lpc32[k], chirp, 16);
            lpc[k]   = (lpc32[k] + 16) >> 5;
            chirp    = (chirp_base * chirp + 32768) >> 16;
        }
    }

    for (i = 0; i < order; i++)
        lpcf[i] = lpc[i] / 4096.0f;
}

static inline void silk_decode_lpc(SilkContext *s, SilkFrame *frame,
                                   OpusRangeCoder *rc,
                                   float lpc_leadin[16], float lpc[16],
                                   int *lpc_order, int *has_lpc_leadin, int voiced)
{
    int i;
    int order;                   // order of the LP polynomial; 10 for NB/MB and 16 for WB
    int8_t  lsf_i1, lsf_i2[16];  // stage-1 and stage-2 codebook indices
    int16_t lsf_res[16];         // residual as a Q10 value
    int16_t nlsf[16];            // Q15

    *lpc_order = order = s->wb ? 16 : 10;

    /* obtain LSF stage-1 and stage-2 indices */
    lsf_i1 = opus_rc_getsymbol(rc, silk_model_lsf_s1[s->wb][voiced]);
    for (i = 0; i < order; i++) {
        int index = s->wb ? silk_lsf_s2_model_sel_wb  [lsf_i1][i] :
                            silk_lsf_s2_model_sel_nbmb[lsf_i1][i];
        lsf_i2[i] = opus_rc_getsymbol(rc, silk_model_lsf_s2[index]) - 4;
        if (lsf_i2[i] == -4)
            lsf_i2[i] -= opus_rc_getsymbol(rc, silk_model_lsf_s2_ext);
        else if (lsf_i2[i] == 4)
            lsf_i2[i] += opus_rc_getsymbol(rc, silk_model_lsf_s2_ext);
    }

    /* reverse the backwards-prediction step */
    for (i = order - 1; i >= 0; i--) {
        int qstep = s->wb ? 9830 : 11796;

        lsf_res[i] = lsf_i2[i] * 1024;
        if (lsf_i2[i] < 0)      lsf_res[i] += 102;
        else if (lsf_i2[i] > 0) lsf_res[i] -= 102;
        lsf_res[i] = (lsf_res[i] * qstep) >> 16;

        if (i + 1 < order) {
            int weight = s->wb ? silk_lsf_pred_weights_wb  [silk_lsf_weight_sel_wb  [lsf_i1][i]][i] :
                                 silk_lsf_pred_weights_nbmb[silk_lsf_weight_sel_nbmb[lsf_i1][i]][i];
            lsf_res[i] += (lsf_res[i+1] * weight) >> 8;
        }
    }

    /* reconstruct the NLSF coefficients from the supplied indices */
    for (i = 0; i < order; i++) {
        const uint8_t * codebook = s->wb ? silk_lsf_codebook_wb  [lsf_i1] :
                                           silk_lsf_codebook_nbmb[lsf_i1];
        int cur, prev, next, weight_sq, weight, ipart, fpart, y, value;

        /* find the weight of the residual */
        /* TODO: precompute */
        cur = codebook[i];
        prev = i ? codebook[i - 1] : 0;
        next = i + 1 < order ? codebook[i + 1] : 256;
        weight_sq = (1024 / (cur - prev) + 1024 / (next - cur)) << 16;

        /* approximate square-root with mandated fixed-point arithmetic */
        ipart = opus_ilog(weight_sq);
        fpart = (weight_sq >> (ipart-8)) & 127;
        y = ((ipart & 1) ? 32768 : 46214) >> ((32 - ipart)>>1);
        weight = y + ((213 * fpart * y) >> 16);

        value = cur * 128 + (lsf_res[i] * 16384) / weight;
        nlsf[i] = av_clip_uintp2(value, 15);
    }

    /* stabilize the NLSF coefficients */
    silk_stabilize_lsf(nlsf, order, s->wb ? silk_lsf_min_spacing_wb :
                                            silk_lsf_min_spacing_nbmb);

    /* produce an interpolation for the first 2 subframes, */
    /* and then convert both sets of NLSFs to LPC coefficients */
    *has_lpc_leadin = 0;
    if (s->subframes == 4) {
        int offset = opus_rc_getsymbol(rc, silk_model_lsf_interpolation_offset);
        if (offset != 4 && frame->coded) {
            *has_lpc_leadin = 1;
            if (offset != 0) {
                int16_t nlsf_leadin[16];
                for (i = 0; i < order; i++)
                    nlsf_leadin[i] = frame->nlsf[i] +
                        ((nlsf[i] - frame->nlsf[i]) * offset >> 2);
                silk_lsf2lpc(nlsf_leadin, lpc_leadin, order);
            } else  /* avoid re-computation for a (roughly) 1-in-4 occurrence */
                memcpy(lpc_leadin, frame->lpc, 16 * sizeof(float));
        } else
            offset = 4;
        s->nlsf_interp_factor = offset;

        silk_lsf2lpc(nlsf, lpc, order);
    } else {
        s->nlsf_interp_factor = 4;
        silk_lsf2lpc(nlsf, lpc, order);
    }

    memcpy(frame->nlsf, nlsf, order * sizeof(nlsf[0]));
    memcpy(frame->lpc,  lpc,  order * sizeof(lpc[0]));
}

static inline void silk_count_children(OpusRangeCoder *rc, int model, int32_t total,
                                       int32_t child[2])
{
    if (total != 0) {
        child[0] = opus_rc_getsymbol(rc,
                       silk_model_pulse_location[model] + (((total - 1 + 5) * (total - 1)) >> 1));
        child[1] = total - child[0];
    } else {
        child[0] = 0;
        child[1] = 0;
    }
}

static inline void silk_decode_excitation(SilkContext *s, OpusRangeCoder *rc,
                                          float* excitationf,
                                          int qoffset_high, int active, int voiced)
{
    int i;
    uint32_t seed;
    int shellblocks;
    int ratelevel;
    uint8_t pulsecount[20];     // total pulses in each shell block
    uint8_t lsbcount[20] = {0}; // raw lsbits defined for each pulse in each shell block
    int32_t excitation[320];    // Q23

    /* excitation parameters */
    seed = opus_rc_getsymbol(rc, silk_model_lcg_seed);
    shellblocks = silk_shell_blocks[s->bandwidth][s->subframes >> 2];
    ratelevel = opus_rc_getsymbol(rc, silk_model_exc_rate[voiced]);

    for (i = 0; i < shellblocks; i++) {
        pulsecount[i] = opus_rc_getsymbol(rc, silk_model_pulse_count[ratelevel]);
        if (pulsecount[i] == 17) {
            while (pulsecount[i] == 17 && ++lsbcount[i] != 10)
                pulsecount[i] = opus_rc_getsymbol(rc, silk_model_pulse_count[9]);
            if (lsbcount[i] == 10)
                pulsecount[i] = opus_rc_getsymbol(rc, silk_model_pulse_count[10]);
        }
    }

    /* decode pulse locations using PVQ */
    for (i = 0; i < shellblocks; i++) {
        if (pulsecount[i] != 0) {
            int a, b, c, d;
            int32_t * location = excitation + 16*i;
            int32_t branch[4][2];
            branch[0][0] = pulsecount[i];

            /* unrolled tail recursion */
            for (a = 0; a < 1; a++) {
                silk_count_children(rc, 0, branch[0][a], branch[1]);
                for (b = 0; b < 2; b++) {
                    silk_count_children(rc, 1, branch[1][b], branch[2]);
                    for (c = 0; c < 2; c++) {
                        silk_count_children(rc, 2, branch[2][c], branch[3]);
                        for (d = 0; d < 2; d++) {
                            silk_count_children(rc, 3, branch[3][d], location);
                            location += 2;
                        }
                    }
                }
            }
        } else
            memset(excitation + 16*i, 0, 16*sizeof(int32_t));
    }

    /* decode least significant bits */
    for (i = 0; i < shellblocks << 4; i++) {
        int bit;
        for (bit = 0; bit < lsbcount[i >> 4]; bit++)
            excitation[i] = (excitation[i] << 1) |
                            opus_rc_getsymbol(rc, silk_model_excitation_lsb);
    }

    /* decode signs */
    for (i = 0; i < shellblocks << 4; i++) {
        if (excitation[i] != 0) {
            int sign = opus_rc_getsymbol(rc, silk_model_excitation_sign[active +
                                         voiced][qoffset_high][FFMIN(pulsecount[i >> 4], 6)]);
            if (sign == 0)
                excitation[i] *= -1;
        }
    }

    /* assemble the excitation */
    for (i = 0; i < shellblocks << 4; i++) {
        int value = excitation[i];
        excitation[i] = value * 256 | silk_quant_offset[voiced][qoffset_high];
        if (value < 0)      excitation[i] += 20;
        else if (value > 0) excitation[i] -= 20;

        /* invert samples pseudorandomly */
        seed = 196314165 * seed + 907633515;
        if (seed & 0x80000000)
            excitation[i] *= -1;
        seed += value;

        excitationf[i] = excitation[i] / 8388608.0f;
    }
}

/** Maximum residual history according to 4.2.7.6.1 */
#define SILK_MAX_LAG  (288 + LTP_ORDER / 2)

/** Order of the LTP filter */
#define LTP_ORDER 5

static void silk_decode_frame(SilkContext *s, OpusRangeCoder *rc,
                              int frame_num, int channel, int coded_channels, int active, int active1)
{
    /* per frame */
    int voiced;       // combines with active to indicate inactive, active, or active+voiced
    int qoffset_high;
    int order;                             // order of the LPC coefficients
    float lpc_leadin[16], lpc_body[16], residual[SILK_MAX_LAG + SILK_HISTORY];
    int has_lpc_leadin;
    float ltpscale;

    /* per subframe */
    struct {
        float gain;
        int pitchlag;
        float ltptaps[5];
    } sf[4];

    SilkFrame * const frame = s->frame + channel;

    int i;

    /* obtain stereo weights */
    if (coded_channels == 2 && channel == 0) {
        int n, wi[2], ws[2], w[2];
        n     = opus_rc_getsymbol(rc, silk_model_stereo_s1);
        wi[0] = opus_rc_getsymbol(rc, silk_model_stereo_s2) + 3 * (n / 5);
        ws[0] = opus_rc_getsymbol(rc, silk_model_stereo_s3);
        wi[1] = opus_rc_getsymbol(rc, silk_model_stereo_s2) + 3 * (n % 5);
        ws[1] = opus_rc_getsymbol(rc, silk_model_stereo_s3);

        for (i = 0; i < 2; i++)
            w[i] = silk_stereo_weights[wi[i]] +
                   (((silk_stereo_weights[wi[i] + 1] - silk_stereo_weights[wi[i]]) * 6554) >> 16)
                    * (ws[i]*2 + 1);

        s->stereo_weights[0] = (w[0] - w[1]) / 8192.0;
        s->stereo_weights[1] = w[1]          / 8192.0;

        /* and read the mid-only flag */
        s->midonly = active1 ? 0 : opus_rc_getsymbol(rc, silk_model_mid_only);
    }

    /* obtain frame type */
    if (!active) {
        qoffset_high = opus_rc_getsymbol(rc, silk_model_frame_type_inactive);
        voiced = 0;
    } else {
        int type = opus_rc_getsymbol(rc, silk_model_frame_type_active);
        qoffset_high = type & 1;
        voiced = type >> 1;
    }

    /* obtain subframe quantization gains */
    for (i = 0; i < s->subframes; i++) {
        int log_gain;     //Q7
        int ipart, fpart, lingain;

        if (i == 0 && (frame_num == 0 || !frame->coded)) {
            /* gain is coded absolute */
            int x = opus_rc_getsymbol(rc, silk_model_gain_highbits[active + voiced]);
            log_gain = (x<<3) | opus_rc_getsymbol(rc, silk_model_gain_lowbits);

            if (frame->coded)
                log_gain = FFMAX(log_gain, frame->log_gain - 16);
        } else {
            /* gain is coded relative */
            int delta_gain = opus_rc_getsymbol(rc, silk_model_gain_delta);
            log_gain = av_clip_uintp2(FFMAX((delta_gain<<1) - 16,
                                     frame->log_gain + delta_gain - 4), 6);
        }

        frame->log_gain = log_gain;

        /* approximate 2**(x/128) with a Q7 (i.e. non-integer) input */
        log_gain = (log_gain * 0x1D1C71 >> 16) + 2090;
        ipart = log_gain >> 7;
        fpart = log_gain & 127;
        lingain = (1 << ipart) + ((-174 * fpart * (128-fpart) >>16) + fpart) * ((1<<ipart) >> 7);
        sf[i].gain = lingain / 65536.0f;
    }

    /* obtain LPC filter coefficients */
    silk_decode_lpc(s, frame, rc, lpc_leadin, lpc_body, &order, &has_lpc_leadin, voiced);

    /* obtain pitch lags, if this is a voiced frame */
    if (voiced) {
        int lag_absolute = (!frame_num || !frame->prev_voiced);
        int primarylag;         // primary pitch lag for the entire SILK frame
        int ltpfilter;
        const int8_t * offsets;

        if (!lag_absolute) {
            int delta = opus_rc_getsymbol(rc, silk_model_pitch_delta);
            if (delta)
                primarylag = frame->primarylag + delta - 9;
            else
                lag_absolute = 1;
        }

        if (lag_absolute) {
            /* primary lag is coded absolute */
            int highbits, lowbits;
            const uint16_t *model[] = {
                silk_model_pitch_lowbits_nb, silk_model_pitch_lowbits_mb,
                silk_model_pitch_lowbits_wb
            };
            highbits = opus_rc_getsymbol(rc, silk_model_pitch_highbits);
            lowbits  = opus_rc_getsymbol(rc, model[s->bandwidth]);

            primarylag = silk_pitch_min_lag[s->bandwidth] +
                         highbits*silk_pitch_scale[s->bandwidth] + lowbits;
        }
        frame->primarylag = primarylag;

        if (s->subframes == 2)
            offsets = (s->bandwidth == OPUS_BANDWIDTH_NARROWBAND)
                     ? silk_pitch_offset_nb10ms[opus_rc_getsymbol(rc,
                                                silk_model_pitch_contour_nb10ms)]
                     : silk_pitch_offset_mbwb10ms[opus_rc_getsymbol(rc,
                                                silk_model_pitch_contour_mbwb10ms)];
        else
            offsets = (s->bandwidth == OPUS_BANDWIDTH_NARROWBAND)
                     ? silk_pitch_offset_nb20ms[opus_rc_getsymbol(rc,
                                                silk_model_pitch_contour_nb20ms)]
                     : silk_pitch_offset_mbwb20ms[opus_rc_getsymbol(rc,
                                                silk_model_pitch_contour_mbwb20ms)];

        for (i = 0; i < s->subframes; i++)
            sf[i].pitchlag = av_clip(primarylag + offsets[i],
                                     silk_pitch_min_lag[s->bandwidth],
                                     silk_pitch_max_lag[s->bandwidth]);

        /* obtain LTP filter coefficients */
        ltpfilter = opus_rc_getsymbol(rc, silk_model_ltp_filter);
        for (i = 0; i < s->subframes; i++) {
            int index, j;
            const uint16_t *filter_sel[] = {
                silk_model_ltp_filter0_sel, silk_model_ltp_filter1_sel,
                silk_model_ltp_filter2_sel
            };
            const int8_t (*filter_taps[])[5] = {
                silk_ltp_filter0_taps, silk_ltp_filter1_taps, silk_ltp_filter2_taps
            };
            index = opus_rc_getsymbol(rc, filter_sel[ltpfilter]);
            for (j = 0; j < 5; j++)
                sf[i].ltptaps[j] = filter_taps[ltpfilter][index][j] / 128.0f;
        }
    }

    /* obtain LTP scale factor */
    if (voiced && frame_num == 0)
        ltpscale = silk_ltp_scale_factor[opus_rc_getsymbol(rc,
                                         silk_model_ltp_scale_index)] / 16384.0f;
    else ltpscale = 15565.0f/16384.0f;

    /* generate the excitation signal for the entire frame */
    silk_decode_excitation(s, rc, residual + SILK_MAX_LAG, qoffset_high,
                           active, voiced);

    /* skip synthesising the side channel if we want mono-only */
    if (s->output_channels == channel)
        return;

    /* generate the output signal */
    for (i = 0; i < s->subframes; i++) {
        const float * lpc_coeff = (i < 2 && has_lpc_leadin) ? lpc_leadin : lpc_body;
        float *dst    = frame->output      + SILK_HISTORY + i * s->sflength;
        float *resptr = residual           + SILK_MAX_LAG + i * s->sflength;
        float *lpc    = frame->lpc_history + SILK_HISTORY + i * s->sflength;
        float sum;
        int j, k;

        if (voiced) {
            int out_end;
            float scale;

            if (i < 2 || s->nlsf_interp_factor == 4) {
                out_end = -i * s->sflength;
                scale   = ltpscale;
            } else {
                out_end = -(i - 2) * s->sflength;
                scale   = 1.0f;
            }

            /* when the LPC coefficients change, a re-whitening filter is used */
            /* to produce a residual that accounts for the change */
            for (j = - sf[i].pitchlag - LTP_ORDER/2; j < out_end; j++) {
                sum = dst[j];
                for (k = 0; k < order; k++)
                    sum -= lpc_coeff[k] * dst[j - k - 1];
                resptr[j] = av_clipf(sum, -1.0f, 1.0f) * scale / sf[i].gain;
            }

            if (out_end) {
                float rescale = sf[i-1].gain / sf[i].gain;
                for (j = out_end; j < 0; j++)
                    resptr[j] *= rescale;
            }

            /* LTP synthesis */
            for (j = 0; j < s->sflength; j++) {
                sum = resptr[j];
                for (k = 0; k < LTP_ORDER; k++)
                    sum += sf[i].ltptaps[k] * resptr[j - sf[i].pitchlag + LTP_ORDER/2 - k];
                resptr[j] = sum;
            }
        }

        /* LPC synthesis */
        for (j = 0; j < s->sflength; j++) {
            sum = resptr[j] * sf[i].gain;
            for (k = 1; k <= order; k++)
                sum += lpc_coeff[k - 1] * lpc[j - k];

            lpc[j] = sum;
            dst[j] = av_clipf(sum, -1.0f, 1.0f);
        }
    }

    frame->prev_voiced = voiced;
    memmove(frame->lpc_history, frame->lpc_history + s->flength, SILK_HISTORY * sizeof(float));
    memmove(frame->output,      frame->output      + s->flength, SILK_HISTORY * sizeof(float));

    frame->coded = 1;
}

static void silk_unmix_ms(SilkContext *s, float *l, float *r)
{
    float *mid    = s->frame[0].output + SILK_HISTORY - s->flength;
    float *side   = s->frame[1].output + SILK_HISTORY - s->flength;
    float w0_prev = s->prev_stereo_weights[0];
    float w1_prev = s->prev_stereo_weights[1];
    float w0      = s->stereo_weights[0];
    float w1      = s->stereo_weights[1];
    int n1        = silk_stereo_interp_len[s->bandwidth];
    int i;

    for (i = 0; i < n1; i++) {
        float interp0 = w0_prev + i * (w0 - w0_prev) / n1;
        float interp1 = w1_prev + i * (w1 - w1_prev) / n1;
        float p0      = 0.25 * (mid[i - 2] + 2 * mid[i - 1] + mid[i]);

        l[i] = av_clipf((1 + interp1) * mid[i - 1] + side[i - 1] + interp0 * p0, -1.0, 1.0);
        r[i] = av_clipf((1 - interp1) * mid[i - 1] - side[i - 1] - interp0 * p0, -1.0, 1.0);
    }

    for (; i < s->flength; i++) {
        float p0 = 0.25 * (mid[i - 2] + 2 * mid[i - 1] + mid[i]);

        l[i] = av_clipf((1 + w1) * mid[i - 1] + side[i - 1] + w0 * p0, -1.0, 1.0);
        r[i] = av_clipf((1 - w1) * mid[i - 1] - side[i - 1] - w0 * p0, -1.0, 1.0);
    }

    memcpy(s->prev_stereo_weights, s->stereo_weights, sizeof(s->stereo_weights));
}

static void silk_flush_frame(SilkFrame *frame)
{
    if (!frame->coded)
        return;

    memset(frame->output,      0, sizeof(frame->output));
    memset(frame->lpc_history, 0, sizeof(frame->lpc_history));

    memset(frame->lpc,  0, sizeof(frame->lpc));
    memset(frame->nlsf, 0, sizeof(frame->nlsf));

    frame->log_gain = 0;

    frame->primarylag  = 0;
    frame->prev_voiced = 0;
    frame->coded       = 0;
}

int ff_silk_decode_superframe(SilkContext *s, OpusRangeCoder *rc,
                              float *output[2],
                              enum OpusBandwidth bandwidth,
                              int coded_channels,
                              int duration_ms)
{
    int active[2][6], redundancy[2];
    int nb_frames, i, j;

    if (bandwidth > OPUS_BANDWIDTH_WIDEBAND ||
        coded_channels > 2 || duration_ms > 60) {
        av_log(s->avctx, AV_LOG_ERROR, "Invalid parameters passed "
               "to the SILK decoder.\n");
        return AVERROR(EINVAL);
    }

    nb_frames = 1 + (duration_ms > 20) + (duration_ms > 40);
    s->subframes = duration_ms / nb_frames / 5;         // 5ms subframes
    s->sflength  = 20 * (bandwidth + 2);
    s->flength   = s->sflength * s->subframes;
    s->bandwidth = bandwidth;
    s->wb        = bandwidth == OPUS_BANDWIDTH_WIDEBAND;

    /* make sure to flush the side channel when switching from mono to stereo */
    if (coded_channels > s->prev_coded_channels)
        silk_flush_frame(&s->frame[1]);
    s->prev_coded_channels = coded_channels;

    /* read the LP-layer header bits */
    for (i = 0; i < coded_channels; i++) {
        for (j = 0; j < nb_frames; j++)
            active[i][j] = opus_rc_p2model(rc, 1);

        redundancy[i] = opus_rc_p2model(rc, 1);
        if (redundancy[i]) {
            av_log(s->avctx, AV_LOG_ERROR, "LBRR frames present; this is unsupported\n");
            return AVERROR_PATCHWELCOME;
        }
    }

    for (i = 0; i < nb_frames; i++) {
        for (j = 0; j < coded_channels && !s->midonly; j++)
            silk_decode_frame(s, rc, i, j, coded_channels, active[j][i], active[1][i]);

        /* reset the side channel if it is not coded */
        if (s->midonly && s->frame[1].coded)
            silk_flush_frame(&s->frame[1]);

        if (coded_channels == 1 || s->output_channels == 1) {
            for (j = 0; j < s->output_channels; j++) {
                memcpy(output[j] + i * s->flength,
                       s->frame[0].output + SILK_HISTORY - s->flength - 2,
                       s->flength * sizeof(float));
            }
        } else {
            silk_unmix_ms(s, output[0] + i * s->flength, output[1] + i * s->flength);
        }

        s->midonly        = 0;
    }

    return nb_frames * s->flength;
}

void ff_silk_free(SilkContext **ps)
{
    av_freep(ps);
}

void ff_silk_flush(SilkContext *s)
{
    silk_flush_frame(&s->frame[0]);
    silk_flush_frame(&s->frame[1]);

    memset(s->prev_stereo_weights, 0, sizeof(s->prev_stereo_weights));
}

int ff_silk_init(AVCodecContext *avctx, SilkContext **ps, int output_channels)
{
    SilkContext *s;

    if (output_channels != 1 && output_channels != 2) {
        av_log(avctx, AV_LOG_ERROR, "Invalid number of output channels: %d\n",
               output_channels);
        return AVERROR(EINVAL);
    }

    s = av_mallocz(sizeof(*s));
    if (!s)
        return AVERROR(ENOMEM);

    s->avctx           = avctx;
    s->output_channels = output_channels;

    ff_silk_flush(s);

    *ps = s;

    return 0;
}
