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); }
// fixme: test the perf of this alternate implementation //int logSum(int a, int b) //{ // return log2(pow(4, a) + pow(4, b)) / 2; //} internal static void surround_analysis <T>(CeltMode celt_mode, T[] pcm, int pcm_ptr, int[] bandLogE, int[] mem, int[] preemph_mem, int len, int overlap, int channels, int rate, opus_copy_channel_in_func <T> copy_channel_in ) { int c; int i; int LM; int[] pos = { 0, 0, 0, 0, 0, 0, 0, 0 }; int upsample; int frame_size; int channel_offset; int[][] bandE = Arrays.InitTwoDimensionalArray <int>(1, 21); int[][] maskLogE = Arrays.InitTwoDimensionalArray <int>(3, 21); int[] input; short[] x; int[][] freq; upsample = CeltCommon.resampling_factor(rate); frame_size = len * upsample; for (LM = 0; LM < celt_mode.maxLM; LM++) { if (celt_mode.shortMdctSize << LM == frame_size) { break; } } input = new int[frame_size + overlap]; x = new short[len]; freq = Arrays.InitTwoDimensionalArray <int>(1, frame_size); channel_pos(channels, pos); for (c = 0; c < 3; c++) { for (i = 0; i < 21; i++) { maskLogE[c][i] = -((short)(0.5 + (28.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(28.0f, CeltConstants.DB_SHIFT)*/; } } for (c = 0; c < channels; c++) { Array.Copy(mem, c * overlap, input, 0, overlap); copy_channel_in(x, 0, 1, pcm, pcm_ptr, channels, c, len); BoxedValueInt boxed_preemph = new BoxedValueInt(preemph_mem[c]); CeltCommon.celt_preemphasis(x, input, overlap, frame_size, 1, upsample, celt_mode.preemph, boxed_preemph, 0); preemph_mem[c] = boxed_preemph.Val; MDCT.clt_mdct_forward( celt_mode.mdct, input, 0, freq[0], 0, celt_mode.window, overlap, celt_mode.maxLM - LM, 1); if (upsample != 1) { int bound = len; for (i = 0; i < bound; i++) { freq[0][i] *= upsample; } for (; i < frame_size; i++) { freq[0][i] = 0; } } Bands.compute_band_energies(celt_mode, freq, bandE, 21, 1, LM); QuantizeBands.amp2Log2(celt_mode, 21, 21, bandE[0], bandLogE, 21 * c, 1); /* Apply spreading function with -6 dB/band going up and -12 dB/band going down. */ for (i = 1; i < 21; i++) { bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i - 1] - ((short)(0.5 + (1.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(1.0f, CeltConstants.DB_SHIFT)*/); } for (i = 19; i >= 0; i--) { bandLogE[21 * c + i] = Inlines.MAX16(bandLogE[21 * c + i], bandLogE[21 * c + i + 1] - ((short)(0.5 + (2.0f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(2.0f, CeltConstants.DB_SHIFT)*/); } if (pos[c] == 1) { for (i = 0; i < 21; i++) { maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i]); } } else if (pos[c] == 3) { for (i = 0; i < 21; i++) { maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i]); } } else if (pos[c] == 2) { for (i = 0; i < 21; i++) { maskLogE[0][i] = logSum(maskLogE[0][i], bandLogE[21 * c + i] - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); maskLogE[2][i] = logSum(maskLogE[2][i], bandLogE[21 * c + i] - ((short)(0.5 + (.5f) * (((int)1) << (CeltConstants.DB_SHIFT)))) /*Inlines.QCONST16(.5f, CeltConstants.DB_SHIFT)*/); } } Array.Copy(input, frame_size, mem, c * overlap, overlap); } for (i = 0; i < 21; i++) { maskLogE[1][i] = Inlines.MIN32(maskLogE[0][i], maskLogE[2][i]); } channel_offset = Inlines.HALF16(Inlines.celt_log2(((int)(0.5 + (2.0f) * (((int)1) << (14)))) /*Inlines.QCONST32(2.0f, 14)*/ / (channels - 1))); for (c = 0; c < 3; c++) { for (i = 0; i < 21; i++) { maskLogE[c][i] += channel_offset; } } for (c = 0; c < channels; c++) { int[] mask; if (pos[c] != 0) { mask = maskLogE[pos[c] - 1]; for (i = 0; i < 21; i++) { bandLogE[21 * c + i] = bandLogE[21 * c + i] - mask[i]; } } else { for (i = 0; i < 21; i++) { bandLogE[21 * c + i] = 0; } } } }