Ejemplo n.º 1
0
        internal int celt_decode_with_ec(byte[] data, int data_ptr,
                                         int len, short[] pcm, int pcm_ptr, int frame_size, EntropyCoder dec, int accum)
        {
            int c, i, N;
            int spread_decision;
            int bits;

            int[][] X;
            int[]   fine_quant;
            int[]   pulses;
            int[]   cap;
            int[]   offsets;
            int[]   fine_priority;
            int[]   tf_res;
            byte[]  collapse_masks;
            int[][] out_syn      = new int[2][];
            int[]   out_syn_ptrs = new int[2];
            int[]   oldBandE, oldLogE, oldLogE2, backgroundLogE;

            int      shortBlocks;
            int      isTransient;
            int      intra_ener;
            int      CC = this.channels;
            int      LM, M;
            int      start;
            int      end;
            int      effEnd;
            int      codedBands;
            int      alloc_trim;
            int      postfilter_pitch;
            int      postfilter_gain;
            int      intensity   = 0;
            int      dual_stereo = 0;
            int      total_bits;
            int      balance;
            int      tell;
            int      dynalloc_logp;
            int      postfilter_tapset;
            int      anti_collapse_rsv;
            int      anti_collapse_on = 0;
            int      silence;
            int      C = this.stream_channels;
            CeltMode mode; // porting note: pointer
            int      nbEBands;
            int      overlap;

            short[] eBands;

            mode        = this.mode;
            nbEBands    = mode.nbEBands;
            overlap     = mode.overlap;
            eBands      = mode.eBands;
            start       = this.start;
            end         = this.end;
            frame_size *= this.downsample;

            oldBandE       = this.oldEBands;
            oldLogE        = this.oldLogE;
            oldLogE2       = this.oldLogE2;
            backgroundLogE = this.backgroundLogE;

            {
                for (LM = 0; LM <= mode.maxLM; LM++)
                {
                    if (mode.shortMdctSize << LM == frame_size)
                    {
                        break;
                    }
                }
                if (LM > mode.maxLM)
                {
                    return(OpusError.OPUS_BAD_ARG);
                }
            }
            M = 1 << LM;

            if (len < 0 || len > 1275 || pcm == null)
            {
                return(OpusError.OPUS_BAD_ARG);
            }

            N = M * mode.shortMdctSize;
            c = 0; do
            {
                out_syn[c]      = this.decode_mem[c];
                out_syn_ptrs[c] = CeltConstants.DECODE_BUFFER_SIZE - N;
            } while (++c < CC);

            effEnd = end;
            if (effEnd > mode.effEBands)
            {
                effEnd = mode.effEBands;
            }

            if (data == null || len <= 1)
            {
                this.celt_decode_lost(N, LM);
                CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum);

                return(frame_size / this.downsample);
            }

            if (dec == null)
            {
                // If no entropy decoder was passed into this function, we need to create
                // a new one here for local use only. It only exists in this function scope.
                dec = new EntropyCoder();
                dec.dec_init(data, data_ptr, (uint)len);
            }

            if (C == 1)
            {
                for (i = 0; i < nbEBands; i++)
                {
                    oldBandE[i] = Inlines.MAX16(oldBandE[i], oldBandE[nbEBands + i]);
                }
            }

            total_bits = len * 8;
            tell       = dec.tell();

            if (tell >= total_bits)
            {
                silence = 1;
            }
            else if (tell == 1)
            {
                silence = dec.dec_bit_logp(15);
            }
            else
            {
                silence = 0;
            }

            if (silence != 0)
            {
                /* Pretend we've read all the remaining bits */
                tell             = len * 8;
                dec.nbits_total += tell - dec.tell();
            }

            postfilter_gain   = 0;
            postfilter_pitch  = 0;
            postfilter_tapset = 0;
            if (start == 0 && tell + 16 <= total_bits)
            {
                if (dec.dec_bit_logp(1) != 0)
                {
                    int qg, octave;
                    octave           = (int)dec.dec_uint(6);
                    postfilter_pitch = (16 << octave) + (int)dec.dec_bits(4 + (uint)octave) - 1;
                    qg = (int)dec.dec_bits(3);
                    if (dec.tell() + 2 <= total_bits)
                    {
                        postfilter_tapset = dec.dec_icdf(Tables.tapset_icdf, 2);
                    }
                    postfilter_gain = ((short)(0.5 + (.09375f) * (((int)1) << (15)))) /*Inlines.QCONST16(.09375f, 15)*/ * (qg + 1);
                }
                tell = dec.tell();
            }

            if (LM > 0 && tell + 3 <= total_bits)
            {
                isTransient = dec.dec_bit_logp(3);
                tell        = dec.tell();
            }
            else
            {
                isTransient = 0;
            }

            if (isTransient != 0)
            {
                shortBlocks = M;
            }
            else
            {
                shortBlocks = 0;
            }

            /* Decode the global flags (first symbols in the stream) */
            intra_ener = tell + 3 <= total_bits?dec.dec_bit_logp(3) : 0;

            /* Get band energies */
            QuantizeBands.unquant_coarse_energy(mode, start, end, oldBandE,
                                                intra_ener, dec, C, LM);

            tf_res = new int[nbEBands];
            CeltCommon.tf_decode(start, end, isTransient, tf_res, LM, dec);

            tell            = dec.tell();
            spread_decision = Spread.SPREAD_NORMAL;
            if (tell + 4 <= total_bits)
            {
                spread_decision = dec.dec_icdf(Tables.spread_icdf, 5);
            }

            cap = new int[nbEBands];

            CeltCommon.init_caps(mode, cap, LM, C);

            offsets = new int[nbEBands];

            dynalloc_logp = 6;
            total_bits  <<= EntropyCoder.BITRES;
            tell          = (int)dec.tell_frac();
            for (i = start; i < end; i++)
            {
                int width, quanta;
                int dynalloc_loop_logp;
                int boost;
                width = C * (eBands[i + 1] - eBands[i]) << LM;

                /* quanta is 6 bits, but no more than 1 bit/sample
                 * and no less than 1/8 bit/sample */
                quanta             = Inlines.IMIN(width << EntropyCoder.BITRES, Inlines.IMAX(6 << EntropyCoder.BITRES, width));
                dynalloc_loop_logp = dynalloc_logp;
                boost = 0;
                while (tell + (dynalloc_loop_logp << EntropyCoder.BITRES) < total_bits && boost < cap[i])
                {
                    int flag;
                    flag = dec.dec_bit_logp((uint)dynalloc_loop_logp);
                    tell = (int)dec.tell_frac();
                    if (flag == 0)
                    {
                        break;
                    }
                    boost             += quanta;
                    total_bits        -= quanta;
                    dynalloc_loop_logp = 1;
                }
                offsets[i] = boost;
                /* Making dynalloc more likely */
                if (boost > 0)
                {
                    dynalloc_logp = Inlines.IMAX(2, dynalloc_logp - 1);
                }
            }

            fine_quant = new int[nbEBands];
            alloc_trim = tell + (6 << EntropyCoder.BITRES) <= total_bits?
                         dec.dec_icdf(Tables.trim_icdf, 7) : 5;

            bits = (((int)len * 8) << EntropyCoder.BITRES) - (int)dec.tell_frac() - 1;
            anti_collapse_rsv = isTransient != 0 && LM >= 2 && bits >= ((LM + 2) << EntropyCoder.BITRES) ? (1 << EntropyCoder.BITRES) : 0;
            bits -= anti_collapse_rsv;

            pulses        = new int[nbEBands];
            fine_priority = new int[nbEBands];

            codedBands = Rate.compute_allocation(mode, start, end, offsets, cap,
                                                 alloc_trim, ref intensity, ref dual_stereo, bits, out balance, pulses,
                                                 fine_quant, fine_priority, C, LM, dec, 0, 0, 0);

            QuantizeBands.unquant_fine_energy(mode, start, end, oldBandE, fine_quant, dec, C);

            c = 0;
            do
            {
                Arrays.MemMoveInt(decode_mem[c], N, 0, CeltConstants.DECODE_BUFFER_SIZE - N + overlap / 2);
            } while (++c < CC);

            /* Decode fixed codebook */
            collapse_masks = new byte[C * nbEBands];

            X = Arrays.InitTwoDimensionalArray <int>(C, N);   /**< Interleaved normalised MDCTs */

            Bands.quant_all_bands(0, mode, start, end, X[0], C == 2 ? X[1] : null, collapse_masks,
                                  null, pulses, shortBlocks, spread_decision, dual_stereo, intensity, tf_res,
                                  len * (8 << EntropyCoder.BITRES) - anti_collapse_rsv, balance, dec, LM, codedBands, ref this.rng);

            if (anti_collapse_rsv > 0)
            {
                anti_collapse_on = (int)dec.dec_bits(1);
            }

            QuantizeBands.unquant_energy_finalise(mode, start, end, oldBandE,
                                                  fine_quant, fine_priority, len * 8 - dec.tell(), dec, C);

            if (anti_collapse_on != 0)
            {
                Bands.anti_collapse(mode, X, collapse_masks, LM, C, N,
                                    start, end, oldBandE, oldLogE, oldLogE2, pulses, this.rng);
            }

            if (silence != 0)
            {
                for (i = 0; i < C * nbEBands; i++)
                {
                    oldBandE[i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
                }
            }

            CeltCommon.celt_synthesis(mode, X, out_syn, out_syn_ptrs, oldBandE, start, effEnd,
                                      C, CC, isTransient, LM, this.downsample, silence);

            c = 0; do
            {
                this.postfilter_period     = Inlines.IMAX(this.postfilter_period, CeltConstants.COMBFILTER_MINPERIOD);
                this.postfilter_period_old = Inlines.IMAX(this.postfilter_period_old, CeltConstants.COMBFILTER_MINPERIOD);
                CeltCommon.comb_filter(out_syn[c], out_syn_ptrs[c], out_syn[c], out_syn_ptrs[c], this.postfilter_period_old, this.postfilter_period, mode.shortMdctSize,
                                       this.postfilter_gain_old, this.postfilter_gain, this.postfilter_tapset_old, this.postfilter_tapset,
                                       mode.window, overlap);
                if (LM != 0)
                {
                    CeltCommon.comb_filter(
                        out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize),
                        out_syn[c], out_syn_ptrs[c] + (mode.shortMdctSize),
                        this.postfilter_period, postfilter_pitch, N - mode.shortMdctSize,
                        this.postfilter_gain, postfilter_gain, this.postfilter_tapset, postfilter_tapset,
                        mode.window, overlap);
                }
            } while (++c < CC);
            this.postfilter_period_old = this.postfilter_period;
            this.postfilter_gain_old   = this.postfilter_gain;
            this.postfilter_tapset_old = this.postfilter_tapset;
            this.postfilter_period     = postfilter_pitch;
            this.postfilter_gain       = postfilter_gain;
            this.postfilter_tapset     = postfilter_tapset;
            if (LM != 0)
            {
                this.postfilter_period_old = this.postfilter_period;
                this.postfilter_gain_old   = this.postfilter_gain;
                this.postfilter_tapset_old = this.postfilter_tapset;
            }

            if (C == 1)
            {
                Array.Copy(oldBandE, 0, oldBandE, nbEBands, nbEBands);
            }

            /* In case start or end were to change */
            if (isTransient == 0)
            {
                int max_background_increase;
                Array.Copy(oldLogE, oldLogE2, 2 * nbEBands);
                Array.Copy(oldBandE, oldLogE, 2 * nbEBands);

                /* In normal circumstances, we only allow the noise floor to increase by
                 * up to 2.4 dB/second, but when we're in DTX, we allow up to 6 dB
                 * increase for each update.*/
                if (this.loss_count < 10)
                {
                    max_background_increase = M * ((short)(0.5 + (0.001f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(0.001f, CeltConstants.DB_SHIFT)*/;
                }
                else
                {
                    max_background_increase = ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/;
                }
                for (i = 0; i < 2 * nbEBands; i++)
                {
                    backgroundLogE[i] = Inlines.MIN16(backgroundLogE[i] + max_background_increase, oldBandE[i]);
                }
            }
            else
            {
                for (i = 0; i < 2 * nbEBands; i++)
                {
                    oldLogE[i] = Inlines.MIN16(oldLogE[i], oldBandE[i]);
                }
            }
            c = 0; do
            {
                for (i = 0; i < start; i++)
                {
                    oldBandE[c * nbEBands + i] = 0;
                    oldLogE[c * nbEBands + i]  = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
                }
                for (i = end; i < nbEBands; i++)
                {
                    oldBandE[c * nbEBands + i] = 0;
                    oldLogE[c * nbEBands + i]  = oldLogE2[c * nbEBands + i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/;
                }
            } while (++c < 2);
            this.rng = dec.rng;

            CeltCommon.deemphasis(out_syn, out_syn_ptrs, pcm, pcm_ptr, N, CC, this.downsample, mode.preemph, this.preemph_memD, accum);
            this.loss_count = 0;

            if (dec.tell() > 8 * len)
            {
                return(OpusError.OPUS_INTERNAL_ERROR);
            }
            if (dec.get_error() != 0)
            {
                this.error = 1;
            }
            return(frame_size / this.downsample);
        }
Ejemplo n.º 2
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);
        }