private void set_pinfo(LameGlobalFlags gfp, GrInfo cod_info, III_psy_ratio ratio, int gr, int ch)
        {
            var   gfc = gfp.internal_flags;
            int   sfb, sfb2;
            int   l;
            float en0, en1;
            var   ifqstep  = cod_info.scalefac_scale == 0 ? .5f : 1.0f;
            var   scalefac = cod_info.scalefac;
            var   l3_xmin  = new float[L3Side.SFBMAX];
            var   xfsf     = new float[L3Side.SFBMAX];
            var   noise    = new CalcNoiseResult();

            calc_xmin(gfp, ratio, cod_info, l3_xmin);
            calc_noise(cod_info, l3_xmin, xfsf, noise, null);
            var j = 0;

            sfb2 = cod_info.sfb_lmax;
            if (cod_info.block_type != Encoder.SHORT_TYPE && 0 == cod_info.mixed_block_flag)
            {
                sfb2 = 22;
            }

            for (sfb = 0; sfb < sfb2; sfb++)
            {
                var start = gfc.scalefac_band.l[sfb];
                var end   = gfc.scalefac_band.l[sfb + 1];
                var bw    = end - start;
                for (en0 = 0.0f; j < end; j++)
                {
                    en0 += cod_info.xr[j] * cod_info.xr[j];
                }

                en0 /= bw;
                en1  = 1e15f;
                gfc.pinfo.en[gr][ch][sfb]   = en1 * en0;
                gfc.pinfo.xfsf[gr][ch][sfb] = en1 * l3_xmin[sfb] * xfsf[sfb] / bw;
                if (ratio.en.l[sfb] > 0 && !gfp.ATHonly)
                {
                    en0 = en0 / ratio.en.l[sfb];
                }
                else
                {
                    en0 = 0.0f;
                }

                gfc.pinfo.thr[gr][ch][sfb]     = en1 * Math.Max(en0 * ratio.thm.l[sfb], gfc.ATH.l[sfb]);
                gfc.pinfo.LAMEsfb[gr][ch][sfb] = 0;
                if (cod_info.preflag != 0 && sfb >= 11)
                {
                    gfc.pinfo.LAMEsfb[gr][ch][sfb] = -ifqstep * pretab[sfb];
                }

                if (sfb < Encoder.SBPSY_l)
                {
                    Debug.Assert(scalefac[sfb] >= 0);
                    gfc.pinfo.LAMEsfb[gr][ch][sfb] -= ifqstep * scalefac[sfb];
                }
            }

            if (cod_info.block_type == Encoder.SHORT_TYPE)
            {
                sfb2 = sfb;
                for (sfb = cod_info.sfb_smin; sfb < Encoder.SBMAX_s; sfb++)
                {
                    var start = gfc.scalefac_band.s[sfb];
                    var end   = gfc.scalefac_band.s[sfb + 1];
                    var bw    = end - start;
                    for (var i = 0; i < 3; i++)
                    {
                        for (en0 = 0.0f, l = start; l < end; l++)
                        {
                            en0 += cod_info.xr[j] * cod_info.xr[j];
                            j++;
                        }

                        en0 = (float)Math.Max(en0 / bw, 1e-20);
                        en1 = 1e15f;
                        gfc.pinfo.en_s[gr][ch][3 * sfb + i]   = en1 * en0;
                        gfc.pinfo.xfsf_s[gr][ch][3 * sfb + i] = en1 * l3_xmin[sfb2] * xfsf[sfb2] / bw;
                        if (ratio.en.s[sfb][i] > 0)
                        {
                            en0 = en0 / ratio.en.s[sfb][i];
                        }
                        else
                        {
                            en0 = 0.0f;
                        }

                        if (gfp.ATHonly || gfp.ATHshort)
                        {
                            en0 = 0;
                        }

                        gfc.pinfo.thr_s[gr][ch][3 * sfb + i] =
                            en1 * Math.Max(en0 * ratio.thm.s[sfb][i], gfc.ATH.s[sfb]);
                        gfc.pinfo.LAMEsfb_s[gr][ch][3 * sfb + i] = -2.0 * cod_info.subblock_gain[i];
                        if (sfb < Encoder.SBPSY_s)
                        {
                            gfc.pinfo.LAMEsfb_s[gr][ch][3 * sfb + i] -= ifqstep * scalefac[sfb2];
                        }

                        sfb2++;
                    }
                }
            }

            gfc.pinfo.LAMEqss[gr][ch]      = cod_info.global_gain;
            gfc.pinfo.LAMEmainbits[gr][ch] = cod_info.part2_3_length + cod_info.part2_length;
            gfc.pinfo.LAMEsfbits[gr][ch]   = cod_info.part2_length;
            gfc.pinfo.over[gr][ch]         = noise.over_count;
            gfc.pinfo.max_noise[gr][ch]    = noise.max_noise * 10.0;
            gfc.pinfo.over_noise[gr][ch]   = noise.over_noise * 10.0;
            gfc.pinfo.tot_noise[gr][ch]    = noise.tot_noise * 10.0;
            gfc.pinfo.over_SSD[gr][ch]     = noise.over_SSD;
        }
Beispiel #2
0
        internal int lame_encode_mp3_frame(
            LameGlobalFlags gfp,
            float[] inbuf_l,
            float[] inbuf_r,
            byte[] mp3buf,
            int mp3bufPos,
            int mp3buf_size)
        {
            int mp3count;

            var masking_LR = Arrays.ReturnRectangularArray <III_psy_ratio>(2, 2); /*
                                                                                   * LR masking &
                                                                                   * energy
                                                                                   */

            masking_LR[0][0] = new III_psy_ratio();
            masking_LR[0][1] = new III_psy_ratio();
            masking_LR[1][0] = new III_psy_ratio();
            masking_LR[1][1] = new III_psy_ratio();

            var masking_MS = Arrays.ReturnRectangularArray <III_psy_ratio>(2, 2);

            /* MS masking & energy */
            masking_MS[0][0] = new III_psy_ratio();
            masking_MS[0][1] = new III_psy_ratio();
            masking_MS[1][0] = new III_psy_ratio();
            masking_MS[1][1] = new III_psy_ratio();
            III_psy_ratio[][] masking;
            /* pointer to selected maskings */
            var inbuf = new float[2][];

            var gfc = gfp.internal_flags;

            var tot_ener      = Arrays.ReturnRectangularArray <float>(2, 4);
            var ms_ener_ratio = new[]
            {
                .5f,
                .5f
            };

            float[][] pe =
            {
                new[]
                {
                    0.0f,
                    0.0f
                },
                new[]
                {
                    0.0f,
                    0.0f
                }
            },
            pe_MS =
            {
                new[]
                {
                    0.0f,
                    0.0f
                },
                new[]
                {
                    0.0f,
                    0.0f
                }
            };
            float[][] pe_use;

            int ch, gr;

            inbuf[0] = inbuf_l;
            inbuf[1] = inbuf_r;

            if (gfc.lame_encode_frame_init == 0)
            {
                lame_encode_frame_init(gfp, inbuf);
            }

            /// <summary>
            ///******************** padding **************************** </summary>
            /// <summary>
            /// <PRE>
            /// padding method as described in
            /// "MPEG-Layer3 / Bitstream Syntax and Decoding"
            /// by Martin Sieler, Ralph Sperschneider
            ///
            /// note: there is no padding for the very first frame
            ///
            /// Robert Hegemann 2000-06-22
            /// </PRE>
            /// </summary>
            gfc.padding = 0;
            if ((gfc.slot_lag -= gfc.frac_SpF) < 0)
            {
                gfc.slot_lag += gfp.out_samplerate;
                gfc.padding   = 1;
            }

            /// <summary>
            ///**************************************
            /// Stage 1: psychoacoustic model *
            /// ***************************************
            /// </summary>

            if (gfc.psymodel != 0)
            {
                /*
                 * psychoacoustic model psy model has a 1 granule (576) delay that
                 * we must compensate for (mt 6/99).
                 */
                int ret;

                var bufp = new float[2][];
                /* address of beginning of left & right granule */
                var bufpPos = 0;
                /* address of beginning of left & right granule */
                var blocktype = new int[2];

                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        bufp[ch] = inbuf[ch];
                        bufpPos  = 576 + gr * 576 - FFTOFFSET;
                    }

                    if (gfp.VBR == VbrMode.vbr_mtrh || gfp.VBR == VbrMode.vbr_mt)
                    {
                        ret = psy.L3psycho_anal_vbr(
                            gfp,
                            bufp,
                            bufpPos,
                            gr,
                            masking_LR,
                            masking_MS,
                            pe[gr],
                            pe_MS[gr],
                            tot_ener[gr],
                            blocktype);
                    }
                    else
                    {
                        ret = psy.L3psycho_anal_ns(
                            gfp,
                            bufp,
                            bufpPos,
                            gr,
                            masking_LR,
                            masking_MS,
                            pe[gr],
                            pe_MS[gr],
                            tot_ener[gr],
                            blocktype);
                    }

                    if (ret != 0)
                    {
                        return(-4);
                    }

                    if (gfp.mode == MPEGMode.JOINT_STEREO)
                    {
                        ms_ener_ratio[gr] = tot_ener[gr][2] + tot_ener[gr][3];
                        if (ms_ener_ratio[gr] > 0)
                        {
                            ms_ener_ratio[gr] = tot_ener[gr][3] / ms_ener_ratio[gr];
                        }
                    }

                    /* block type flags */
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        var cod_info = gfc.l3_side.tt[gr][ch];
                        cod_info.block_type       = blocktype[ch];
                        cod_info.mixed_block_flag = 0;
                    }
                }
            }
            else
            {
                /* no psy model */
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        gfc.l3_side.tt[gr][ch].block_type       = NORM_TYPE;
                        gfc.l3_side.tt[gr][ch].mixed_block_flag = 0;
                        pe_MS[gr][ch] = pe[gr][ch] = 700;
                    }
                }
            }

            /* auto-adjust of ATH, useful for low volume */
            adjust_ATH(gfc);

            /// <summary>
            ///**************************************
            /// Stage 2: MDCT *
            /// ***************************************
            /// </summary>

            /* polyphase filtering / mdct */
            newMDCT.mdct_sub48(gfc, inbuf[0], inbuf[1]);

            /// <summary>
            ///**************************************
            /// Stage 3: MS/LR decision *
            /// ***************************************
            /// </summary>

            /* Here will be selected MS or LR coding of the 2 stereo channels */
            gfc.mode_ext = MPG_MD_LR_LR;

            if (gfp.force_ms)
            {
                gfc.mode_ext = MPG_MD_MS_LR;
            }
            else if (gfp.mode == MPEGMode.JOINT_STEREO)
            {
                /*
                 * ms_ratio = is scaled, for historical reasons, to look like a
                 * ratio of side_channel / total. 0 = signal is 100% mono .5 = L & R
                 * uncorrelated
                 */

                /// <summary>
                /// <PRE>
                /// [0] and [1] are the results for the two granules in MPEG-1,
                /// in MPEG-2 it's only a faked averaging of the same value
                /// _prev is the value of the last granule of the previous frame
                /// _next is the value of the first granule of the next frame
                /// </PRE>
                /// </summary>

                float sum_pe_MS = 0;
                float sum_pe_LR = 0;
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        sum_pe_MS += pe_MS[gr][ch];
                        sum_pe_LR += pe[gr][ch];
                    }
                }

                /* based on PE: M/S coding would not use much more bits than L/R */
                if (sum_pe_MS <= 1.00 * sum_pe_LR)
                {
                    var gi0 = gfc.l3_side.tt[0];

                    var gi1 = gfc.l3_side.tt[gfc.mode_gr - 1];

                    if (gi0[0].block_type == gi0[1].block_type && gi1[0].block_type == gi1[1].block_type)
                    {
                        gfc.mode_ext = MPG_MD_MS_LR;
                    }
                }
            }

            /* bit and noise allocation */
            if (gfc.mode_ext == MPG_MD_MS_LR)
            {
                masking = masking_MS; // use MS masking
                pe_use  = pe_MS;
            }
            else
            {
                masking = masking_LR; // use LR masking
                pe_use  = pe;
            }

            /* copy data for MP3 frame analyzer */
            if (gfp.analysis && gfc.pinfo != null)
            {
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        gfc.pinfo.ms_ratio[gr]      = gfc.ms_ratio[gr];
                        gfc.pinfo.ms_ener_ratio[gr] = ms_ener_ratio[gr];
                        gfc.pinfo.blocktype[gr][ch] = gfc.l3_side.tt[gr][ch].block_type;
                        gfc.pinfo.pe[gr][ch]        = pe_use[gr][ch];
                        Array.Copy(gfc.l3_side.tt[gr][ch].xr, 0, gfc.pinfo.xr[gr][ch], 0, 576);

                        /*
                         * in psymodel, LR and MS data was stored in pinfo. switch
                         * to MS data:
                         */
                        if (gfc.mode_ext == MPG_MD_MS_LR)
                        {
                            gfc.pinfo.ers[gr][ch] = gfc.pinfo.ers[gr][ch + 2];
                            Array.Copy(
                                gfc.pinfo.energy[gr][ch + 2],
                                0,
                                gfc.pinfo.energy[gr][ch],
                                0,
                                gfc.pinfo.energy[gr][ch].Length);
                        }
                    }
                }
            }

            /// <summary>
            ///**************************************
            /// Stage 4: quantization loop *
            /// ***************************************
            /// </summary>

            if (gfp.VBR == VbrMode.vbr_off || gfp.VBR == VbrMode.vbr_abr)
            {
                int   i;
                float f;

                for (i = 0; i < 18; i++)
                {
                    gfc.nsPsy.pefirbuf[i] = gfc.nsPsy.pefirbuf[i + 1];
                }

                f = 0.0f;
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        f += pe_use[gr][ch];
                    }
                }

                gfc.nsPsy.pefirbuf[18] = f;

                f = gfc.nsPsy.pefirbuf[9];
                for (i = 0; i < 9; i++)
                {
                    f += (gfc.nsPsy.pefirbuf[i] + gfc.nsPsy.pefirbuf[18 - i]) * fircoef[i];
                }

                f = 670 * 5 * gfc.mode_gr * gfc.channels_out / f;
                for (gr = 0; gr < gfc.mode_gr; gr++)
                {
                    for (ch = 0; ch < gfc.channels_out; ch++)
                    {
                        pe_use[gr][ch] *= f;
                    }
                }
            }

            gfc.iteration_loop.iteration_loop(gfp, pe_use, ms_ener_ratio, masking);

            /// <summary>
            ///**************************************
            /// Stage 5: bitstream formatting *
            /// ***************************************
            /// </summary>

            /* write the frame to the bitstream */
            bs.format_bitstream(gfp);

            /* copy mp3 bit buffer into array */
            mp3count = bs.copy_buffer(gfc, mp3buf, mp3bufPos, mp3buf_size, 1);

            if (gfp.bWriteVbrTag)
            {
                vbr.addVbrFrame(gfp);
            }

            if (gfp.analysis && gfc.pinfo != null)
            {
                for (ch = 0; ch < gfc.channels_out; ch++)
                {
                    int j;
                    for (j = 0; j < FFTOFFSET; j++)
                    {
                        gfc.pinfo.pcmdata[ch][j] = gfc.pinfo.pcmdata[ch][j + gfp.framesize];
                    }

                    for (j = FFTOFFSET; j < 1600; j++)
                    {
                        gfc.pinfo.pcmdata[ch][j] = inbuf[ch][j - FFTOFFSET];
                    }
                }

                qupvt.set_frame_pinfo(gfp, masking);
            }

            updateStats(gfc);

            return(mp3count);
        }
        internal int calc_xmin(LameGlobalFlags gfp, III_psy_ratio ratio, GrInfo cod_info, float[] pxmin)
        {
            var pxminPos = 0;

            var gfc = gfp.internal_flags;
            int gsfb, j = 0, ath_over = 0;

            var ATH = gfc.ATH;

            var xr = cod_info.xr;

            var enable_athaa_fix = gfp.VBR == VbrMode.vbr_mtrh ? 1 : 0;
            var masking_lower    = gfc.masking_lower;

            if (gfp.VBR == VbrMode.vbr_mtrh || gfp.VBR == VbrMode.vbr_mt)
            {
                masking_lower = 1.0f;
            }

            for (gsfb = 0; gsfb < cod_info.psy_lmax; gsfb++)
            {
                float en0, xmin;
                float rh1, rh2;
                int   width, l;
                if (gfp.VBR == VbrMode.vbr_rh || gfp.VBR == VbrMode.vbr_mtrh)
                {
                    xmin = athAdjust(ATH.adjust, ATH.l[gsfb], ATH.floor);
                }
                else
                {
                    xmin = ATH.adjust * ATH.l[gsfb];
                }

                width = cod_info.width[gsfb];
                rh1   = xmin / width;
                rh2   = DBL_EPSILON;
                l     = width >> 1;
                en0   = 0.0f;
                do
                {
                    float xa, xb;
                    xa   = xr[j] * xr[j];
                    en0 += xa;
                    rh2 += xa < rh1 ? xa : rh1;
                    j++;
                    xb   = xr[j] * xr[j];
                    en0 += xb;
                    rh2 += xb < rh1 ? xb : rh1;
                    j++;
                }while (--l > 0);

                if (en0 > xmin)
                {
                    ath_over++;
                }

                if (gsfb == Encoder.SBPSY_l)
                {
                    var x = xmin * gfc.nsPsy.longfact[gsfb];
                    if (rh2 < x)
                    {
                        rh2 = x;
                    }
                }

                if (enable_athaa_fix != 0)
                {
                    xmin = rh2;
                }

                if (!gfp.ATHonly)
                {
                    var e = ratio.en.l[gsfb];
                    if (e > 0.0f)
                    {
                        float x;
                        x = en0 * ratio.thm.l[gsfb] * masking_lower / e;
                        if (enable_athaa_fix != 0)
                        {
                            x *= gfc.nsPsy.longfact[gsfb];
                        }

                        if (xmin < x)
                        {
                            xmin = x;
                        }
                    }
                }

                if (enable_athaa_fix != 0)
                {
                    pxmin[pxminPos++] = xmin;
                }
                else
                {
                    pxmin[pxminPos++] = xmin * gfc.nsPsy.longfact[gsfb];
                }
            }

            var max_nonzero = 575;

            if (cod_info.block_type != Encoder.SHORT_TYPE)
            {
                var k = 576;
                while (k-- != 0 && BitStream.EQ(xr[k], 0))
                {
                    max_nonzero = k;
                }
            }

            cod_info.max_nonzero_coeff = max_nonzero;
            for (var sfb = cod_info.sfb_smin; gsfb < cod_info.psymax; sfb++, gsfb += 3)
            {
                int   width, b;
                float tmpATH;
                if (gfp.VBR == VbrMode.vbr_rh || gfp.VBR == VbrMode.vbr_mtrh)
                {
                    tmpATH = athAdjust(ATH.adjust, ATH.s[sfb], ATH.floor);
                }
                else
                {
                    tmpATH = ATH.adjust * ATH.s[sfb];
                }

                width = cod_info.width[gsfb];
                for (b = 0; b < 3; b++)
                {
                    float en0 = 0.0f, xmin;
                    float rh1, rh2;
                    var   l = width >> 1;
                    rh1 = tmpATH / width;
                    rh2 = DBL_EPSILON;
                    do
                    {
                        float xa, xb;
                        xa   = xr[j] * xr[j];
                        en0 += xa;
                        rh2 += xa < rh1 ? xa : rh1;
                        j++;
                        xb   = xr[j] * xr[j];
                        en0 += xb;
                        rh2 += xb < rh1 ? xb : rh1;
                        j++;
                    }while (--l > 0);

                    if (en0 > tmpATH)
                    {
                        ath_over++;
                    }

                    if (sfb == Encoder.SBPSY_s)
                    {
                        var x = tmpATH * gfc.nsPsy.shortfact[sfb];
                        if (rh2 < x)
                        {
                            rh2 = x;
                        }
                    }

                    if (enable_athaa_fix != 0)
                    {
                        xmin = rh2;
                    }
                    else
                    {
                        xmin = tmpATH;
                    }

                    if (!gfp.ATHonly && !gfp.ATHshort)
                    {
                        var e = ratio.en.s[sfb][b];
                        if (e > 0.0f)
                        {
                            float x;
                            x = en0 * ratio.thm.s[sfb][b] * masking_lower / e;
                            if (enable_athaa_fix != 0)
                            {
                                x *= gfc.nsPsy.shortfact[sfb];
                            }

                            if (xmin < x)
                            {
                                xmin = x;
                            }
                        }
                    }

                    if (enable_athaa_fix != 0)
                    {
                        pxmin[pxminPos++] = xmin;
                    }
                    else
                    {
                        pxmin[pxminPos++] = xmin * gfc.nsPsy.shortfact[sfb];
                    }
                }

                if (gfp.useTemporal == true)
                {
                    if (pxmin[pxminPos - 3] > pxmin[pxminPos - 3 + 1])
                    {
                        pxmin[pxminPos - 3 + 1] += (pxmin[pxminPos - 3] - pxmin[pxminPos - 3 + 1]) * gfc.decay;
                    }

                    if (pxmin[pxminPos - 3 + 1] > pxmin[pxminPos - 3 + 2])
                    {
                        pxmin[pxminPos - 3 + 2] += (pxmin[pxminPos - 3 + 1] - pxmin[pxminPos - 3 + 2]) * gfc.decay;
                    }
                }
            }

            return(ath_over);
        }