Beispiel #1
0
        internal int opus_multistream_encode_native <T>
        (
            opus_copy_channel_in_func <T> copy_channel_in,
            T[] pcm,
            int pcm_ptr,
            int analysis_frame_size,
            byte[] data,
            int data_ptr,
            int max_data_bytes,
            int lsb_depth,
            Downmix.downmix_func <T> downmix,
            int float_api
        )
        {
            int Fs;
            int s;
            int encoder_ptr;
            int tot_size;

            short[]          buf;
            int[]            bandSMR;
            byte[]           tmp_data = new byte[MS_FRAME_TMP];
            OpusRepacketizer rp       = new OpusRepacketizer();
            int      vbr;
            CeltMode celt_mode;

            int[] bitrates    = new int[256];
            int[] bandLogE    = new int[42];
            int[] mem         = null;
            int[] preemph_mem = null;
            int   frame_size;
            int   rate_sum;
            int   smallest_packet;

            if (this.surround != 0)
            {
                preemph_mem = this.preemph_mem;
                mem         = this.window_mem;
            }

            encoder_ptr = 0;
            Fs          = this.encoders[encoder_ptr].SampleRate;
            vbr         = this.encoders[encoder_ptr].UseVBR ? 1 : 0;
            celt_mode   = this.encoders[encoder_ptr].GetCeltMode();

            {
                int delay_compensation;
                int channels;

                channels            = this.layout.nb_streams + this.layout.nb_coupled_streams;
                delay_compensation  = this.encoders[encoder_ptr].Lookahead;
                delay_compensation -= Fs / 400;
                frame_size          = CodecHelpers.compute_frame_size(pcm, pcm_ptr, analysis_frame_size,
                                                                      this.variable_duration, channels, Fs, this.bitrate_bps,
                                                                      delay_compensation, downmix, this.subframe_mem, this.encoders[encoder_ptr].analysis.enabled);
            }

            if (400 * frame_size < Fs)
            {
                return(OpusError.OPUS_BAD_ARG);
            }

            /* Validate frame_size before using it to allocate stack space.
             * This mirrors the checks in opus_encode[_float](). */
            if (400 * frame_size != Fs && 200 * frame_size != Fs &&
                100 * frame_size != Fs && 50 * frame_size != Fs &&
                25 * frame_size != Fs && 50 * frame_size != 3 * Fs)
            {
                return(OpusError.OPUS_BAD_ARG);
            }

            /* Smallest packet the encoder can produce. */
            smallest_packet = this.layout.nb_streams * 2 - 1;
            if (max_data_bytes < smallest_packet)
            {
                return(OpusError.OPUS_BUFFER_TOO_SMALL);
            }
            buf = new short[2 * frame_size];

            bandSMR = new int[21 * this.layout.nb_channels];
            if (this.surround != 0)
            {
                surround_analysis(celt_mode, pcm, pcm_ptr, bandSMR, mem, preemph_mem, frame_size, 120, this.layout.nb_channels, Fs, copy_channel_in);
            }

            /* Compute bitrate allocation between streams (this could be a lot better) */
            rate_sum = surround_rate_allocation(bitrates, frame_size);

            if (vbr == 0)
            {
                if (this.bitrate_bps == OpusConstants.OPUS_AUTO)
                {
                    max_data_bytes = Inlines.IMIN(max_data_bytes, 3 * rate_sum / (3 * 8 * Fs / frame_size));
                }
                else if (this.bitrate_bps != OpusConstants.OPUS_BITRATE_MAX)
                {
                    max_data_bytes = Inlines.IMIN(max_data_bytes, Inlines.IMAX(smallest_packet,
                                                                               3 * this.bitrate_bps / (3 * 8 * Fs / frame_size)));
                }
            }

            for (s = 0; s < this.layout.nb_streams; s++)
            {
                OpusEncoder enc = this.encoders[encoder_ptr];
                encoder_ptr += 1;
                enc.Bitrate  = (bitrates[s]);
                if (this.surround != 0)
                {
                    int equiv_rate;
                    equiv_rate = this.bitrate_bps;
                    if (frame_size * 50 < Fs)
                    {
                        equiv_rate -= 60 * (Fs / frame_size - 50) * this.layout.nb_channels;
                    }
                    if (equiv_rate > 10000 * this.layout.nb_channels)
                    {
                        enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_FULLBAND);
                    }
                    else if (equiv_rate > 7000 * this.layout.nb_channels)
                    {
                        enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_SUPERWIDEBAND);
                    }
                    else if (equiv_rate > 5000 * this.layout.nb_channels)
                    {
                        enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_WIDEBAND);
                    }
                    else
                    {
                        enc.Bandwidth = (OpusBandwidth.OPUS_BANDWIDTH_NARROWBAND);
                    }
                    if (s < this.layout.nb_coupled_streams)
                    {
                        /* To preserve the spatial image, force stereo CELT on coupled streams */
                        enc.ForceMode     = (OpusMode.MODE_CELT_ONLY);
                        enc.ForceChannels = (2);
                    }
                }
            }

            encoder_ptr = 0;
            /* Counting ToC */
            tot_size = 0;
            for (s = 0; s < this.layout.nb_streams; s++)
            {
                OpusEncoder enc;
                int         len;
                int         curr_max;
                int         c1, c2;

                rp.Reset();
                enc = this.encoders[encoder_ptr];
                if (s < this.layout.nb_coupled_streams)
                {
                    int i;
                    int left, right;
                    left  = OpusMultistream.get_left_channel(this.layout, s, -1);
                    right = OpusMultistream.get_right_channel(this.layout, s, -1);
                    copy_channel_in(buf, 0, 2,
                                    pcm, pcm_ptr, this.layout.nb_channels, left, frame_size);
                    copy_channel_in(buf, 1, 2,
                                    pcm, pcm_ptr, this.layout.nb_channels, right, frame_size);
                    encoder_ptr += 1;
                    if (this.surround != 0)
                    {
                        for (i = 0; i < 21; i++)
                        {
                            bandLogE[i]      = bandSMR[21 * left + i];
                            bandLogE[21 + i] = bandSMR[21 * right + i];
                        }
                    }
                    c1 = left;
                    c2 = right;
                }
                else
                {
                    int i;
                    int chan = OpusMultistream.get_mono_channel(this.layout, s, -1);
                    copy_channel_in(buf, 0, 1,
                                    pcm, pcm_ptr, this.layout.nb_channels, chan, frame_size);
                    encoder_ptr += 1;
                    if (this.surround != 0)
                    {
                        for (i = 0; i < 21; i++)
                        {
                            bandLogE[i] = bandSMR[21 * chan + i];
                        }
                    }
                    c1 = chan;
                    c2 = -1;
                }
                if (this.surround != 0)
                {
                    enc.SetEnergyMask(bandLogE);
                }

                /* number of bytes left (+Toc) */
                curr_max = max_data_bytes - tot_size;
                /* Reserve one byte for the last stream and two for the others */
                curr_max -= Inlines.IMAX(0, 2 * (this.layout.nb_streams - s - 1) - 1);
                curr_max  = Inlines.IMIN(curr_max, MS_FRAME_TMP);
                /* Repacketizer will add one or two bytes for self-delimited frames */
                if (s != this.layout.nb_streams - 1)
                {
                    curr_max -= curr_max > 253 ? 2 : 1;
                }
                if (vbr == 0 && s == this.layout.nb_streams - 1)
                {
                    enc.Bitrate = (curr_max * (8 * Fs / frame_size));
                }
                len = enc.opus_encode_native(buf, 0, frame_size, tmp_data, 0, curr_max, lsb_depth,
                                             pcm, pcm_ptr, analysis_frame_size, c1, c2, this.layout.nb_channels, downmix, float_api);
                if (len < 0)
                {
                    return(len);
                }

                /* We need to use the repacketizer to add the self-delimiting lengths
                 * while taking into account the fact that the encoder can now return
                 * more than one frame at a time (e.g. 60 ms CELT-only) */
                rp.AddPacket(tmp_data, 0, len);
                len = rp.opus_repacketizer_out_range_impl(0, rp.GetNumFrames(),
                                                          data, data_ptr, max_data_bytes - tot_size, (s != this.layout.nb_streams - 1) ? 1 : 0, (vbr == 0 && s == this.layout.nb_streams - 1) ? 1 : 0);
                data_ptr += len;
                tot_size += len;
            }

            return(tot_size);
        }
Beispiel #2
0
        // 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;
                    }
                }
            }
        }