Exemple #1
0
        internal static void celt_fir(
            int[] x,
            int x_ptr,
            int[] num,
            int num_ptr,
            int[] y,
            int y_ptr,
            int N,
            int ord,
            int[] mem
            )
        {
            int i, j;

            int[] rnum    = new int[ord];
            int[] local_x = new int[N + ord];

            for (i = 0; i < ord; i++)
            {
                rnum[i] = num[num_ptr + ord - i - 1];
            }

            for (i = 0; i < ord; i++)
            {
                local_x[i] = mem[ord - i - 1];
            }

            for (i = 0; i < N; i++)
            {
                local_x[i + ord] = x[x_ptr + i];
            }

            for (i = 0; i < ord; i++)
            {
                mem[i] = x[x_ptr + N - i - 1];
            }

            for (i = 0; i < N - 3; i += 4)
            {
                int sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
                xcorr_kernel(rnum, local_x, i, ref sum0, ref sum1, ref sum2, ref sum3, ord);
                y[y_ptr + i]     = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
                y[y_ptr + i + 1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
                y[y_ptr + i + 2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
                y[y_ptr + i + 3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
            }

            for (; i < N; i++)
            {
                int sum = 0;

                for (j = 0; j < ord; j++)
                {
                    sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]);
                }

                y[y_ptr + i] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[x_ptr + i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
            }
        }
Exemple #2
0
        internal static void find_best_pitch(int[] xcorr, int[] y, int len,
                                             int max_pitch, int[] best_pitch,
                                             int yshift, int maxcorr
                                             )
        {
            int i, j;
            int Syy = 1;
            int best_num_0;
            int best_num_1;
            int best_den_0;
            int best_den_1;
            int xshift = Inlines.celt_ilog2(maxcorr) - 14;

            best_num_0    = -1;
            best_num_1    = -1;
            best_den_0    = 0;
            best_den_1    = 0;
            best_pitch[0] = 0;
            best_pitch[1] = 1;
            for (j = 0; j < len; j++)
            {
                Syy = Inlines.ADD32(Syy, Inlines.SHR32(Inlines.MULT16_16(y[j], y[j]), yshift));
            }
            for (i = 0; i < max_pitch; i++)
            {
                if (xcorr[i] > 0)
                {
                    int num;
                    int xcorr16;
                    xcorr16 = Inlines.EXTRACT16(Inlines.VSHR32(xcorr[i], xshift));
                    num     = Inlines.MULT16_16_Q15((xcorr16), (xcorr16));
                    if (Inlines.MULT16_32_Q15(num, best_den_1) > Inlines.MULT16_32_Q15(best_num_1, Syy))
                    {
                        if (Inlines.MULT16_32_Q15(num, best_den_0) > Inlines.MULT16_32_Q15(best_num_0, Syy))
                        {
                            best_num_1    = best_num_0;
                            best_den_1    = best_den_0;
                            best_pitch[1] = best_pitch[0];
                            best_num_0    = num;
                            best_den_0    = Syy;
                            best_pitch[0] = i;
                        }
                        else
                        {
                            best_num_1    = num;
                            best_den_1    = Syy;
                            best_pitch[1] = i;
                        }
                    }
                }

                Syy += Inlines.SHR32(Inlines.MULT16_16(y[i + len], y[i + len]), yshift) - Inlines.SHR32(Inlines.MULT16_16(y[i], y[i]), yshift);
                Syy  = Inlines.MAX32(1, Syy);
            }
        }
Exemple #3
0
        internal static void quant_coarse_energy(CeltMode m, int start, int end, int effEnd,
                                                 int[][] eBands, int[][] oldEBands, uint budget,
                                                 int[][] error, EntropyCoder enc, int C, int LM, int nbAvailableBytes,
                                                 int force_intra, ref int delayedIntra, int two_pass, int loss_rate, int lfe)
        {
            int intra;
            int max_decay;

            int[][]      oldEBands_intra;
            int[][]      error_intra;
            EntropyCoder enc_start_state = new EntropyCoder(); // [porting note] stack variable
            uint         tell;
            int          badness1 = 0;
            int          intra_bias;
            int          new_distortion;


            intra          = (force_intra != 0 || (two_pass == 0 && delayedIntra > 2 * C * (end - start) && nbAvailableBytes > (end - start) * C)) ? 1 : 0;
            intra_bias     = (int)((budget * delayedIntra * loss_rate) / (C * 512));
            new_distortion = loss_distortion(eBands, oldEBands, start, effEnd, m.nbEBands, C);

            tell = (uint)enc.tell();
            if (tell + 3 > budget)
            {
                two_pass = intra = 0;
            }

            max_decay = ((short)(0.5 + (16.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(16.0f, CeltConstants.DB_SHIFT)*/;
            if (end - start > 10)
            {
                max_decay = (Inlines.MIN32(max_decay, Inlines.SHL32(nbAvailableBytes, CeltConstants.DB_SHIFT - 3))); // opus bug: useless extend32
            }
            if (lfe != 0)
            {
                max_decay = ((short)(0.5 + (3.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(3.0f, CeltConstants.DB_SHIFT)*/;
            }
            enc_start_state.Assign(enc);

            oldEBands_intra = Arrays.InitTwoDimensionalArray <int>(C, m.nbEBands);
            error_intra     = Arrays.InitTwoDimensionalArray <int>(C, m.nbEBands);
            Array.Copy(oldEBands[0], 0, oldEBands_intra[0], 0, m.nbEBands);
            if (C == 2)
            {
                Array.Copy(oldEBands[1], 0, oldEBands_intra[1], 0, m.nbEBands);
            }

            if (two_pass != 0 || intra != 0)
            {
                badness1 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands_intra, (int)budget,
                                                    (int)tell, Tables.e_prob_model[LM][1], error_intra, enc, C, LM, 1, max_decay, lfe);
            }

            if (intra == 0)
            {
                int          intra_buf;
                EntropyCoder enc_intra_state = new EntropyCoder(); // [porting note] stack variable
                int          tell_intra;
                uint         nstart_bytes;
                uint         nintra_bytes;
                uint         save_bytes;
                int          badness2;
                byte[]       intra_bits = null;

                tell_intra = (int)enc.tell_frac();

                enc_intra_state.Assign(enc);

                nstart_bytes = enc_start_state.range_bytes();
                nintra_bytes = enc_intra_state.range_bytes();
                intra_buf    = enc_intra_state.buf_ptr + (int)nstart_bytes;
                save_bytes   = nintra_bytes - nstart_bytes;

                if (save_bytes != 0)
                {
                    intra_bits = new byte[(int)save_bytes];
                    /* Copy bits from intra bit-stream */
                    Array.Copy(enc_intra_state.buf, intra_buf, intra_bits, 0, (int)save_bytes);
                }

                enc.Assign(enc_start_state);

                badness2 = quant_coarse_energy_impl(m, start, end, eBands, oldEBands, (int)budget,
                                                    (int)tell, Tables.e_prob_model[LM][intra], error, enc, C, LM, 0, max_decay, lfe);

                if (two_pass != 0 && (badness1 < badness2 || (badness1 == badness2 && ((int)enc.tell_frac()) + intra_bias > tell_intra)))
                {
                    enc.Assign(enc_intra_state);
                    /* Copy intra bits to bit-stream */
                    if (intra_bits != null)
                    {
                        Array.Copy(intra_bits, 0, enc_intra_state.buf, intra_buf, (int)(nintra_bytes - nstart_bytes));
                    }
                    Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands);
                    Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands);
                    if (C == 2)
                    {
                        Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands);
                        Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands);
                    }
                    intra = 1;
                }
            }
            else
            {
                Array.Copy(oldEBands_intra[0], 0, oldEBands[0], 0, m.nbEBands);
                Array.Copy(error_intra[0], 0, error[0], 0, m.nbEBands);
                if (C == 2)
                {
                    Array.Copy(oldEBands_intra[1], 0, oldEBands[1], 0, m.nbEBands);
                    Array.Copy(error_intra[1], 0, error[1], 0, m.nbEBands);
                }
            }

            if (intra != 0)
            {
                delayedIntra = new_distortion;
            }
            else
            {
                delayedIntra = Inlines.ADD32(Inlines.MULT16_32_Q15(Inlines.MULT16_16_Q15(pred_coef[LM], pred_coef[LM]), delayedIntra),
                                             new_distortion);
            }
        }
Exemple #4
0
        internal static uint alg_quant(int[] X, int X_ptr, int N, int K, int spread, int B, EntropyCoder enc
                                       )
        {
            int[] y = new int[N];
            int[] iy = new int[N];
            int[] signx = new int[N];
            int   i, j;
            int   s;
            int   pulsesLeft;
            int   sum;
            int   xy;
            int   yy;
            uint  collapse_mask;

            Inlines.OpusAssert(K > 0, "alg_quant() needs at least one pulse");
            Inlines.OpusAssert(N > 1, "alg_quant() needs at least two dimensions");

            exp_rotation(X, X_ptr, N, 1, B, K, spread);

            /* Get rid of the sign */
            sum = 0;
            j   = 0;
            do
            {
                int xpj = X_ptr + j;

                /* OPT: Make sure the following two lines result in conditional moves
                 * rather than branches. */
                signx[j] = X[xpj] > 0 ? 1 : -1;
                X[xpj]   = Inlines.ABS16(X[xpj]);

                iy[j] = 0;
                y[j]  = 0;
            } while (++j < N);

            xy = yy = 0;

            pulsesLeft = K;

            /* Do a pre-search by projecting on the pyramid */
            if (K > (N >> 1))
            {
                int rcp;
                j = 0; do
                {
                    sum += X[X_ptr + j];
                } while (++j < N);

                /* If X is too small, just replace it with a pulse at 0 */

                /* Prevents infinities and NaNs from causing too many pulses
                *  to be allocated. 64 is an approximation of infinity here. */
                if (sum <= K)
                {
                    X[X_ptr] = ((short)(0.5 + (1.0f) * (((int)1) << (14)))) /*Inlines.QCONST16(1.0f, 14)*/;
                    j        = X_ptr + 1;
                    do
                    {
                        X[j] = 0;
                    } while (++j < N + X_ptr);

                    sum = ((short)(0.5 + (1.0f) * (((int)1) << (14)))) /*Inlines.QCONST16(1.0f, 14)*/;
                }

                rcp = Inlines.EXTRACT16(Inlines.MULT16_32_Q16((K - 1), Inlines.celt_rcp(sum)));
                j   = 0;

                do
                {
                    /* It's really important to round *towards zero* here */
                    iy[j]       = Inlines.MULT16_16_Q15(X[X_ptr + j], rcp);
                    y[j]        = (int)iy[j];
                    yy          = (Inlines.MAC16_16(yy, y[j], y[j]));
                    xy          = Inlines.MAC16_16(xy, X[X_ptr + j], y[j]);
                    y[j]       *= 2;
                    pulsesLeft -= iy[j];
                } while (++j < N);
            }

            Inlines.OpusAssert(pulsesLeft >= 1, "Allocated too many pulses in the quick pass");

            /* This should never happen, but just in case it does (e.g. on silence)
             * we fill the first bin with pulses. */
            if (pulsesLeft > N + 3)
            {
                int tmp = (int)pulsesLeft;
                yy         = (Inlines.MAC16_16(yy, tmp, tmp));
                yy         = (Inlines.MAC16_16(yy, tmp, y[0]));
                iy[0]     += pulsesLeft;
                pulsesLeft = 0;
            }

            s = 1;
            for (i = 0; i < pulsesLeft; i++)
            {
                int best_id;
                int best_num = 0 - CeltConstants.VERY_LARGE16;
                int best_den = 0;
                int rshift   = 1 + Inlines.celt_ilog2(K - pulsesLeft + i + 1);
                best_id = 0;

                /* The squared magnitude term gets added anyway, so we might as well
                 * add it outside the loop */
                yy = Inlines.ADD16(yy, 1); // opus bug - was add32
                j  = 0;
                do
                {
                    int Rxy, Ryy;
                    /* Temporary sums of the new pulse(s) */
                    Rxy = Inlines.EXTRACT16(Inlines.SHR32(Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + j])), rshift));
                    /* We're multiplying y[j] by two so we don't have to do it here */
                    Ryy = Inlines.ADD16(yy, y[j]);

                    /* Approximate score: we maximise Rxy/sqrt(Ryy) (we're guaranteed that
                     * Rxy is positive because the sign is pre-computed) */
                    Rxy = Inlines.MULT16_16_Q15(Rxy, Rxy);

                    /* The idea is to check for num/den >= best_num/best_den, but that way
                     * we can do it without any division */
                    /* OPT: Make sure to use conditional moves here */
                    if (Inlines.MULT16_16(best_den, Rxy) > Inlines.MULT16_16(Ryy, best_num))
                    {
                        best_den = Ryy;
                        best_num = Rxy;
                        best_id  = j;
                    }
                } while (++j < N);

                /* Updating the sums of the new pulse(s) */
                xy = Inlines.ADD32(xy, Inlines.EXTEND32(X[X_ptr + best_id]));
                /* We're multiplying y[j] by two so we don't have to do it here */
                yy = Inlines.ADD16(yy, y[best_id]);

                /* Only now that we've made the final choice, update y/iy */
                /* Multiplying y[j] by 2 so we don't have to do it everywhere else */
                y[best_id] = (y[best_id] + (2 * s));
                iy[best_id]++;
            }

            /* Put the original sign back */
            j = 0;
            do
            {
                X[X_ptr + j] = (Inlines.MULT16_16(signx[j], X[X_ptr + j]));

                /* OPT: Make sure your compiler uses a conditional move here rather than
                 *   a branch. */
                iy[j] = signx[j] < 0 ? -iy[j] : iy[j];
            } while (++j < N);

            CWRS.encode_pulses(iy, N, K, enc);

            collapse_mask = extract_collapse_mask(iy, N, B);

            return(collapse_mask);
        }
Exemple #5
0
        internal int opus_decode_frame(byte[] data, int data_ptr,
                                       int len, short[] pcm, int pcm_ptr, int frame_size, int decode_fec)
        {
            SilkDecoder  silk_dec;
            CeltDecoder  celt_dec;
            int          i, silk_ret = 0, celt_ret = 0;
            EntropyCoder dec = new EntropyCoder(); // porting note: stack var
            int          silk_frame_size;
            int          pcm_silk_size;

            short[] pcm_silk;
            int     pcm_transition_silk_size;

            short[] pcm_transition_silk;
            int     pcm_transition_celt_size;

            short[] pcm_transition_celt;
            short[] pcm_transition = null;
            int     redundant_audio_size;

            short[] redundant_audio;

            int      audiosize;
            OpusMode mode;
            int      transition = 0;
            int      start_band;
            int      redundancy       = 0;
            int      redundancy_bytes = 0;
            int      celt_to_silk     = 0;
            int      c;
            int      F2_5, F5, F10, F20;

            int[] window;
            uint  redundant_rng = 0;
            int   celt_accum;

            silk_dec = this.SilkDecoder;
            celt_dec = this.Celt_Decoder;
            F20      = this.Fs / 50;
            F10      = F20 >> 1;
            F5       = F10 >> 1;
            F2_5     = F5 >> 1;
            if (frame_size < F2_5)
            {
                return(OpusError.OPUS_BUFFER_TOO_SMALL);
            }
            /* Limit frame_size to avoid excessive stack allocations. */
            frame_size = Inlines.IMIN(frame_size, this.Fs / 25 * 3);
            /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */
            if (len <= 1)
            {
                data = null;
                /* In that case, don't conceal more than what the ToC says */
                frame_size = Inlines.IMIN(frame_size, this.frame_size);
            }
            if (data != null)
            {
                audiosize = this.frame_size;
                mode      = this.mode;
                dec.dec_init(data, data_ptr, (uint)len);
            }
            else
            {
                audiosize = frame_size;
                mode      = this.prev_mode;

                if (mode == 0)
                {
                    /* If we haven't got any packet yet, all we can do is return zeros */
                    for (i = pcm_ptr; i < pcm_ptr + (audiosize * this.channels); i++)
                    {
                        pcm[i] = 0;
                    }

                    return(audiosize);
                }

                /* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT),
                 * 10, or 20 (e.g. 12.5 or 30 ms). */
                if (audiosize > F20)
                {
                    do
                    {
                        int ret = opus_decode_frame(null, 0, 0, pcm, pcm_ptr, Inlines.IMIN(audiosize, F20), 0);
                        if (ret < 0)
                        {
                            return(ret);
                        }
                        pcm_ptr   += ret * this.channels;
                        audiosize -= ret;
                    } while (audiosize > 0);

                    return(frame_size);
                }
                else if (audiosize < F20)
                {
                    if (audiosize > F10)
                    {
                        audiosize = F10;
                    }
                    else if (mode != OpusMode.MODE_SILK_ONLY && audiosize > F5 && audiosize < F10)
                    {
                        audiosize = F5;
                    }
                }
            }

            /* In fixed-point, we can tell CELT to do the accumulation on top of the
             * SILK PCM buffer. This saves some stack space. */
            celt_accum = ((mode != OpusMode.MODE_CELT_ONLY) && (frame_size >= F10)) ? 1 : 0;

            pcm_transition_silk_size = 0;
            pcm_transition_celt_size = 0;
            if (data != null && this.prev_mode > 0 && (
                    (mode == OpusMode.MODE_CELT_ONLY && this.prev_mode != OpusMode.MODE_CELT_ONLY && (this.prev_redundancy == 0)) ||
                    (mode != OpusMode.MODE_CELT_ONLY && this.prev_mode == OpusMode.MODE_CELT_ONLY))
                )
            {
                transition = 1;
                /* Decide where to allocate the stack memory for pcm_transition */
                if (mode == OpusMode.MODE_CELT_ONLY)
                {
                    pcm_transition_celt_size = F5 * this.channels;
                }
                else
                {
                    pcm_transition_silk_size = F5 * this.channels;
                }
            }
            pcm_transition_celt = new short[pcm_transition_celt_size];
            if (transition != 0 && mode == OpusMode.MODE_CELT_ONLY)
            {
                pcm_transition = pcm_transition_celt;
                opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0);
            }
            if (audiosize > frame_size)
            {
                /*fprintf(stderr, "PCM buffer too small: %d vs %d (mode = %d)\n", audiosize, frame_size, mode);*/

                return(OpusError.OPUS_BAD_ARG);
            }
            else
            {
                frame_size = audiosize;
            }

            /* Don't allocate any memory when in CELT-only mode */
            pcm_silk_size = (mode != OpusMode.MODE_CELT_ONLY && (celt_accum == 0)) ? Inlines.IMAX(F10, frame_size) * this.channels : 0;
            pcm_silk      = new short[pcm_silk_size];

            /* SILK processing */
            if (mode != OpusMode.MODE_CELT_ONLY)
            {
                int     lost_flag, decoded_samples;
                short[] pcm_ptr2;
                int     pcm_ptr2_ptr = 0;

                if (celt_accum != 0)
                {
                    pcm_ptr2     = pcm;
                    pcm_ptr2_ptr = pcm_ptr;
                }
                else
                {
                    pcm_ptr2     = pcm_silk;
                    pcm_ptr2_ptr = 0;
                }

                if (this.prev_mode == OpusMode.MODE_CELT_ONLY)
                {
                    DecodeAPI.silk_InitDecoder(silk_dec);
                }

                /* The SILK PLC cannot produce frames of less than 10 ms */
                this.DecControl.payloadSize_ms = Inlines.IMAX(10, 1000 * audiosize / this.Fs);

                if (data != null)
                {
                    this.DecControl.nChannelsInternal = this.stream_channels;
                    if (mode == OpusMode.MODE_SILK_ONLY)
                    {
                        if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND)
                        {
                            this.DecControl.internalSampleRate = 8000;
                        }
                        else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND)
                        {
                            this.DecControl.internalSampleRate = 12000;
                        }
                        else if (this.bandwidth == OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND)
                        {
                            this.DecControl.internalSampleRate = 16000;
                        }
                        else
                        {
                            this.DecControl.internalSampleRate = 16000;
                            Inlines.OpusAssert(false);
                        }
                    }
                    else
                    {
                        /* Hybrid mode */
                        this.DecControl.internalSampleRate = 16000;
                    }
                }

                lost_flag       = data == null ? 1 : 2 * decode_fec;
                decoded_samples = 0;
                do
                {
                    /* Call SILK decoder */
                    int first_frame = (decoded_samples == 0) ? 1 : 0;
                    silk_ret = DecodeAPI.silk_Decode(silk_dec, this.DecControl,
                                                     lost_flag, first_frame, dec, pcm_ptr2, pcm_ptr2_ptr, out silk_frame_size);
                    if (silk_ret != 0)
                    {
                        if (lost_flag != 0)
                        {
                            /* PLC failure should not be fatal */
                            silk_frame_size = frame_size;
                            Arrays.MemSetWithOffset <short>(pcm_ptr2, 0, pcm_ptr2_ptr, frame_size * this.channels);
                        }
                        else
                        {
                            return(OpusError.OPUS_INTERNAL_ERROR);
                        }
                    }
                    pcm_ptr2_ptr    += (silk_frame_size * this.channels);
                    decoded_samples += silk_frame_size;
                } while (decoded_samples < frame_size);
            }

            start_band = 0;
            if (decode_fec == 0 && mode != OpusMode.MODE_CELT_ONLY && data != null &&
                dec.tell() + 17 + 20 * (this.mode == OpusMode.MODE_HYBRID ? 1 : 0) <= 8 * len)
            {
                /* Check if we have a redundant 0-8 kHz band */
                if (mode == OpusMode.MODE_HYBRID)
                {
                    redundancy = dec.dec_bit_logp(12);
                }
                else
                {
                    redundancy = 1;
                }
                if (redundancy != 0)
                {
                    celt_to_silk = dec.dec_bit_logp(1);

                    /* redundancy_bytes will be at least two, in the non-hybrid
                     * case due to the ec_tell() check above */
                    redundancy_bytes = mode == OpusMode.MODE_HYBRID ?
                                       (int)dec.dec_uint(256) + 2 :
                                       len - ((dec.tell() + 7) >> 3);
                    len -= redundancy_bytes;

                    /* This is a sanity check. It should never happen for a valid
                     * packet, so the exact behaviour is not normative. */
                    if (len * 8 < dec.tell())
                    {
                        len = 0;
                        redundancy_bytes = 0;
                        redundancy       = 0;
                    }
                    /* Shrink decoder because of raw bits */
                    dec.storage = (uint)(dec.storage - redundancy_bytes);
                }
            }
            if (mode != OpusMode.MODE_CELT_ONLY)
            {
                start_band = 17;
            }

            {
                int endband = 21;

                switch (this.bandwidth)
                {
                case OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND:
                    endband = 13;
                    break;

                case OpusBandwidth.OPUS_BANDWIDTH_MEDIUMBAND:
                case OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND:
                    endband = 17;
                    break;

                case OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND:
                    endband = 19;
                    break;

                case OpusBandwidth.OPUS_BANDWIDTH_FULLBAND:
                    endband = 21;
                    break;
                }
                celt_dec.SetEndBand(endband);
                celt_dec.SetChannels(this.stream_channels);
            }

            if (redundancy != 0)
            {
                transition = 0;
                pcm_transition_silk_size = 0;
            }

            pcm_transition_silk = new short[pcm_transition_silk_size];

            if (transition != 0 && mode != OpusMode.MODE_CELT_ONLY)
            {
                pcm_transition = pcm_transition_silk;
                opus_decode_frame(null, 0, 0, pcm_transition, 0, Inlines.IMIN(F5, audiosize), 0);
            }

            /* Only allocation memory for redundancy if/when needed */
            redundant_audio_size = redundancy != 0 ? F5 * this.channels : 0;
            redundant_audio      = new short[redundant_audio_size];

            /* 5 ms redundant frame for CELT->SILK*/
            if (redundancy != 0 && celt_to_silk != 0)
            {
                celt_dec.SetStartBand(0);
                celt_dec.celt_decode_with_ec(data, (data_ptr + len), redundancy_bytes,
                                             redundant_audio, 0, F5, null, 0);
                redundant_rng = celt_dec.GetFinalRange();
            }

            /* MUST be after PLC */
            celt_dec.SetStartBand(start_band);

            if (mode != OpusMode.MODE_SILK_ONLY)
            {
                int celt_frame_size = Inlines.IMIN(F20, frame_size);
                /* Make sure to discard any previous CELT state */
                if (mode != this.prev_mode && this.prev_mode > 0 && this.prev_redundancy == 0)
                {
                    celt_dec.ResetState();
                }
                /* Decode CELT */
                celt_ret = celt_dec.celt_decode_with_ec(decode_fec != 0 ? null : data, data_ptr,
                                                        len, pcm, pcm_ptr, celt_frame_size, dec, celt_accum);
            }
            else
            {
                if (celt_accum == 0)
                {
                    for (i = pcm_ptr; i < (frame_size * this.channels) + pcm_ptr; i++)
                    {
                        pcm[i] = 0;
                    }
                }

                /* For hybrid -> SILK transitions, we let the CELT MDCT
                 * do a fade-out by decoding a silence frame */
                if (this.prev_mode == OpusMode.MODE_HYBRID && !(redundancy != 0 && celt_to_silk != 0 && this.prev_redundancy != 0))
                {
                    celt_dec.SetStartBand(0);
                    celt_dec.celt_decode_with_ec(SILENCE, 0, 2, pcm, pcm_ptr, F2_5, null, celt_accum);
                }
            }

            if (mode != OpusMode.MODE_CELT_ONLY && celt_accum == 0)
            {
                for (i = 0; i < frame_size * this.channels; i++)
                {
                    pcm[pcm_ptr + i] = Inlines.SAT16(Inlines.ADD32(pcm[pcm_ptr + i], pcm_silk[i]));
                }
            }

            window = celt_dec.GetMode().window;

            /* 5 ms redundant frame for SILK->CELT */
            if (redundancy != 0 && celt_to_silk == 0)
            {
                celt_dec.ResetState();
                celt_dec.SetStartBand(0);

                celt_dec.celt_decode_with_ec(data, data_ptr + len, redundancy_bytes, redundant_audio, 0, F5, null, 0);
                redundant_rng = celt_dec.GetFinalRange();
                CodecHelpers.smooth_fade(pcm, pcm_ptr + this.channels * (frame_size - F2_5), redundant_audio, this.channels * F2_5,
                                         pcm, (pcm_ptr + this.channels * (frame_size - F2_5)), F2_5, this.channels, window, this.Fs);
            }
            if (redundancy != 0 && celt_to_silk != 0)
            {
                for (c = 0; c < this.channels; c++)
                {
                    for (i = 0; i < F2_5; i++)
                    {
                        pcm[this.channels * i + c + pcm_ptr] = redundant_audio[this.channels * i + c];
                    }
                }
                CodecHelpers.smooth_fade(redundant_audio, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)),
                                         pcm, (pcm_ptr + (this.channels * F2_5)), F2_5, this.channels, window, this.Fs);
            }
            if (transition != 0)
            {
                if (audiosize >= F5)
                {
                    for (i = 0; i < this.channels * F2_5; i++)
                    {
                        pcm[i] = pcm_transition[i];
                    }
                    CodecHelpers.smooth_fade(pcm_transition, (this.channels * F2_5), pcm, (pcm_ptr + (this.channels * F2_5)),
                                             pcm, (pcm_ptr + (this.channels * F2_5)), F2_5,
                                             this.channels, window, this.Fs);
                }
                else
                {
                    /* Not enough time to do a clean transition, but we do it anyway
                     * This will not preserve amplitude perfectly and may introduce
                     * a bit of temporal aliasing, but it shouldn't be too bad and
                     * that's pretty much the best we can do. In any case, generating this
                     * transition is pretty silly in the first place */
                    CodecHelpers.smooth_fade(pcm_transition, 0, pcm, pcm_ptr,
                                             pcm, pcm_ptr, F2_5,
                                             this.channels, window, this.Fs);
                }
            }

            if (this.decode_gain != 0)
            {
                int gain;
                gain = Inlines.celt_exp2(Inlines.MULT16_16_P15(((short)(0.5 + (6.48814081e-4f) * (((int)1) << (25)))) /*Inlines.QCONST16(6.48814081e-4f, 25)*/, this.decode_gain));
                for (i = pcm_ptr; i < pcm_ptr + (frame_size * this.channels); i++)
                {
                    int x;
                    x      = Inlines.MULT16_32_P16(pcm[i], gain);
                    pcm[i] = (short)Inlines.SATURATE(x, 32767);
                }
            }

            if (len <= 1)
            {
                this.rangeFinal = 0;
            }
            else
            {
                this.rangeFinal = dec.rng ^ redundant_rng;
            }

            this.prev_mode       = mode;
            this.prev_redundancy = (redundancy != 0 && celt_to_silk == 0) ? 1 : 0;

            return(celt_ret < 0 ? celt_ret : audiosize);
        }
        internal static unsafe void celt_fir(
            short *x,
            short[] num,
            short *y,
            int N,
            int ord,
            short[] mem
            )
        {
            int i, j;

            short[] rnum    = new short[ord];
            short[] local_x = new short[N + ord];
            fixed(short *prnum = rnum, plocal_x = local_x)
            {
                for (i = 0; i < ord; i++)
                {
                    prnum[i] = num[ord - i - 1];
                }

                for (i = 0; i < ord; i++)
                {
                    plocal_x[i] = mem[ord - i - 1];
                }

                for (i = 0; i < N; i++)
                {
                    plocal_x[i + ord] = x[i];
                }

                for (i = 0; i < ord; i++)
                {
                    mem[i] = x[N - i - 1];
                }

                short *py = y;

                for (i = 0; i < N - 3; i += 4)
                {
                    int    sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
                    short *local_x2 = plocal_x + i;
                    xcorr_kernel(prnum, local_x2, ref sum0, ref sum1, ref sum2, ref sum3, ord);
                    py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
                    py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
                    py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
                    py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i + 3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
                    py   += 4;
                }

                for (; i < N; i++)
                {
                    int sum = 0;

                    for (j = 0; j < ord; j++)
                    {
                        sum = Inlines.MAC16_16(sum, prnum[j], plocal_x[i + j]);
                    }

                    *py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(x[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
                    py++;
                }
            }
        }
        internal static unsafe void celt_fir(
            int *px,
            int *pnum,
            int *py,
            int N,
            int ord,
            int[] mem
            )
        {
            int i, j;

            int[] rnum    = new int[ord];
            int[] local_x = new int[N + ord];
            fixed(int *prnum = rnum, plocal_x_base = local_x)
            {
                for (i = 0; i < ord; i++)
                {
                    rnum[i] = pnum[ord - i - 1];
                }

                for (i = 0; i < ord; i++)
                {
                    local_x[i] = mem[ord - i - 1];
                }

                for (i = 0; i < N; i++)
                {
                    local_x[i + ord] = px[i];
                }

                for (i = 0; i < ord; i++)
                {
                    mem[i] = px[N - i - 1];
                }

                int *px2 = px;

                for (i = 0; i < N - 3; i += 4)
                {
                    int  sum0 = 0, sum1 = 0, sum2 = 0, sum3 = 0;
                    int *plocal_x = plocal_x_base + i;
                    xcorr_kernel(prnum, plocal_x, ref sum0, ref sum1, ref sum2, ref sum3, ord);
                    py[0] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[0]), Inlines.PSHR32(sum0, CeltConstants.SIG_SHIFT))));
                    py[1] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[1]), Inlines.PSHR32(sum1, CeltConstants.SIG_SHIFT))));
                    py[2] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[2]), Inlines.PSHR32(sum2, CeltConstants.SIG_SHIFT))));
                    py[3] = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px2[3]), Inlines.PSHR32(sum3, CeltConstants.SIG_SHIFT))));
                    py   += 4;
                    px2  += 4;
                }

                for (; i < N; i++)
                {
                    int sum = 0;

                    for (j = 0; j < ord; j++)
                    {
                        sum = Inlines.MAC16_16(sum, rnum[j], local_x[i + j]);
                    }

                    *py = Inlines.SATURATE16((Inlines.ADD32(Inlines.EXTEND32(px[i]), Inlines.PSHR32(sum, CeltConstants.SIG_SHIFT))));
                    py++;
                }
            }
        }