static void psy_3gpp_analyze_channel(FFPsyContext *ctx, int channel,

                                     const float *coefs, const FFPsyWindowInfo *wi)

{

    AacPsyContext *pctx = (AacPsyContext*) ctx->model_priv_data;

    AacPsyChannel *pch  = &pctx->ch[channel];

    int i, w, g;

    float desired_bits, desired_pe, delta_pe, reduction= NAN, spread_en[128] = {0};

    float a = 0.0f, active_lines = 0.0f, norm_fac = 0.0f;

    float pe = pctx->chan_bitrate > 32000 ? 0.0f : FFMAX(50.0f, 100.0f - pctx->chan_bitrate * 100.0f / 32000.0f);

    const int      num_bands   = ctx->num_bands[wi->num_windows == 8];

    const uint8_t *band_sizes  = ctx->bands[wi->num_windows == 8];

    AacPsyCoeffs  *coeffs      = pctx->psy_coef[wi->num_windows == 8];

    const float avoid_hole_thr = wi->num_windows == 8 ? PSY_3GPP_AH_THR_SHORT : PSY_3GPP_AH_THR_LONG;



    //calculate energies, initial thresholds and related values - 5.4.2 "Threshold Calculation"

    calc_thr_3gpp(wi, num_bands, pch, band_sizes, coefs);



    //modify thresholds and energies - spread, threshold in quiet, pre-echo control

    for (w = 0; w < wi->num_windows*16; w += 16) {

        AacPsyBand *bands = &pch->band[w];



        /* 5.4.2.3 "Spreading" & 5.4.3 "Spread Energy Calculation" */

        spread_en[0] = bands[0].energy;

        for (g = 1; g < num_bands; g++) {

            bands[g].thr   = FFMAX(bands[g].thr,    bands[g-1].thr * coeffs[g].spread_hi[0]);

            spread_en[w+g] = FFMAX(bands[g].energy, spread_en[w+g-1] * coeffs[g].spread_hi[1]);

        }

        for (g = num_bands - 2; g >= 0; g--) {

            bands[g].thr   = FFMAX(bands[g].thr,   bands[g+1].thr * coeffs[g].spread_low[0]);

            spread_en[w+g] = FFMAX(spread_en[w+g], spread_en[w+g+1] * coeffs[g].spread_low[1]);

        }

        //5.4.2.4 "Threshold in quiet"

        for (g = 0; g < num_bands; g++) {

            AacPsyBand *band = &bands[g];



            band->thr_quiet = band->thr = FFMAX(band->thr, coeffs[g].ath);

            //5.4.2.5 "Pre-echo control"

            if (!(wi->window_type[0] == LONG_STOP_SEQUENCE || (wi->window_type[1] == LONG_START_SEQUENCE && !w)))

                band->thr = FFMAX(PSY_3GPP_RPEMIN*band->thr, FFMIN(band->thr,

                                  PSY_3GPP_RPELEV*pch->prev_band[w+g].thr_quiet));



            /* 5.6.1.3.1 "Preparatory steps of the perceptual entropy calculation" */

            pe += calc_pe_3gpp(band);

            a  += band->pe_const;

            active_lines += band->active_lines;



            /* 5.6.1.3.3 "Selection of the bands for avoidance of holes" */

            if (spread_en[w+g] * avoid_hole_thr > band->energy || coeffs[g].min_snr > 1.0f)

                band->avoid_holes = PSY_3GPP_AH_NONE;

            else

                band->avoid_holes = PSY_3GPP_AH_INACTIVE;

        }

    }



    /* 5.6.1.3.2 "Calculation of the desired perceptual entropy" */

    ctx->ch[channel].entropy = pe;

    desired_bits = calc_bit_demand(pctx, pe, ctx->bitres.bits, ctx->bitres.size, wi->num_windows == 8);

    desired_pe = PSY_3GPP_BITS_TO_PE(desired_bits);

    /* NOTE: PE correction is kept simple. During initial testing it had very

     *       little effect on the final bitrate. Probably a good idea to come

     *       back and do more testing later.

     */

    if (ctx->bitres.bits > 0)

        desired_pe *= av_clipf(pctx->pe.previous / PSY_3GPP_BITS_TO_PE(ctx->bitres.bits),

                               0.85f, 1.15f);

    pctx->pe.previous = PSY_3GPP_BITS_TO_PE(desired_bits);



    if (desired_pe < pe) {

        /* 5.6.1.3.4 "First Estimation of the reduction value" */

        for (w = 0; w < wi->num_windows*16; w += 16) {

            reduction = calc_reduction_3gpp(a, desired_pe, pe, active_lines);

            pe = 0.0f;

            a  = 0.0f;

            active_lines = 0.0f;

            for (g = 0; g < num_bands; g++) {

                AacPsyBand *band = &pch->band[w+g];



                band->thr = calc_reduced_thr_3gpp(band, coeffs[g].min_snr, reduction);

                /* recalculate PE */

                pe += calc_pe_3gpp(band);

                a  += band->pe_const;

                active_lines += band->active_lines;

            }

        }



        /* 5.6.1.3.5 "Second Estimation of the reduction value" */

        for (i = 0; i < 2; i++) {

            float pe_no_ah = 0.0f, desired_pe_no_ah;

            active_lines = a = 0.0f;

            for (w = 0; w < wi->num_windows*16; w += 16) {

                for (g = 0; g < num_bands; g++) {

                    AacPsyBand *band = &pch->band[w+g];



                    if (band->avoid_holes != PSY_3GPP_AH_ACTIVE) {

                        pe_no_ah += band->pe;

                        a        += band->pe_const;

                        active_lines += band->active_lines;

                    }

                }

            }

            desired_pe_no_ah = FFMAX(desired_pe - (pe - pe_no_ah), 0.0f);

            if (active_lines > 0.0f)

                reduction += calc_reduction_3gpp(a, desired_pe_no_ah, pe_no_ah, active_lines);



            pe = 0.0f;

            for (w = 0; w < wi->num_windows*16; w += 16) {

                for (g = 0; g < num_bands; g++) {

                    AacPsyBand *band = &pch->band[w+g];



                    if (active_lines > 0.0f)

                        band->thr = calc_reduced_thr_3gpp(band, coeffs[g].min_snr, reduction);

                    pe += calc_pe_3gpp(band);

                    band->norm_fac = band->active_lines / band->thr;

                    norm_fac += band->norm_fac;

                }

            }

            delta_pe = desired_pe - pe;

            if (fabs(delta_pe) > 0.05f * desired_pe)

                break;

        }



        if (pe < 1.15f * desired_pe) {

            /* 6.6.1.3.6 "Final threshold modification by linearization" */

            norm_fac = 1.0f / norm_fac;

            for (w = 0; w < wi->num_windows*16; w += 16) {

                for (g = 0; g < num_bands; g++) {

                    AacPsyBand *band = &pch->band[w+g];



                    if (band->active_lines > 0.5f) {

                        float delta_sfb_pe = band->norm_fac * norm_fac * delta_pe;

                        float thr = band->thr;



                        thr *= exp2f(delta_sfb_pe / band->active_lines);

                        if (thr > coeffs[g].min_snr * band->energy && band->avoid_holes == PSY_3GPP_AH_INACTIVE)

                            thr = FFMAX(band->thr, coeffs[g].min_snr * band->energy);

                        band->thr = thr;

                    }

                }

            }

        } else {

            /* 5.6.1.3.7 "Further perceptual entropy reduction" */

            g = num_bands;

            while (pe > desired_pe && g--) {

                for (w = 0; w < wi->num_windows*16; w+= 16) {

                    AacPsyBand *band = &pch->band[w+g];

                    if (band->avoid_holes != PSY_3GPP_AH_NONE && coeffs[g].min_snr < PSY_SNR_1DB) {

                        coeffs[g].min_snr = PSY_SNR_1DB;

                        band->thr = band->energy * PSY_SNR_1DB;

                        pe += band->active_lines * 1.5f - band->pe;

                    }

                }

            }

            /* TODO: allow more holes (unused without mid/side) */

        }

    }



    for (w = 0; w < wi->num_windows*16; w += 16) {

        for (g = 0; g < num_bands; g++) {

            AacPsyBand *band     = &pch->band[w+g];

            FFPsyBand  *psy_band = &ctx->ch[channel].psy_bands[w+g];



            psy_band->threshold = band->thr;

            psy_band->energy    = band->energy;

        }

    }



    memcpy(pch->prev_band, pch->band, sizeof(pch->band));

}
