/* returns GAIN_ANALYSIS_OK if successful, GAIN_ANALYSIS_ERROR if not */
gain_calc_status
ReplayGain_analyze_samples(replaygain_ReplayGain* self,
                           const double* left_samples,
                           const double* right_samples,
                           size_t num_samples,
                           int num_channels)
{
    const double*  curleft;
    const double*  curright;
    long            batchsamples;
    long            cursamples;
    long            cursamplepos;
    long            i;

    if ( num_samples == 0 )
        return GAIN_ANALYSIS_OK;

    cursamplepos = 0;
    batchsamples = num_samples;

    switch ( num_channels) {
    case  1: right_samples = left_samples;
    case  2: break;
    default: return GAIN_ANALYSIS_ERROR;
    }

    if ( num_samples < MAX_ORDER ) {
        memcpy (self->linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(double) );
        memcpy ( self->rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(double) );
    }
    else {
        memcpy ( self->linprebuf + MAX_ORDER, left_samples,  MAX_ORDER   * sizeof(double) );
        memcpy ( self->rinprebuf + MAX_ORDER, right_samples, MAX_ORDER   * sizeof(double) );
    }

    while ( batchsamples > 0 ) {
        cursamples = batchsamples > self->sampleWindow - self->totsamp  ?  self->sampleWindow - self->totsamp  :  batchsamples;
        if ( cursamplepos < MAX_ORDER ) {
            curleft  = self->linpre + cursamplepos;
            curright = self->rinpre + cursamplepos;
            if (cursamples > MAX_ORDER - cursamplepos )
                cursamples = MAX_ORDER - cursamplepos;
        }
        else {
            curleft  = left_samples  + cursamplepos;
            curright = right_samples + cursamplepos;
        }

        YULE_FILTER ( curleft , self->lstep + self->totsamp, cursamples, ABYule[self->freqindex]);
        YULE_FILTER ( curright, self->rstep + self->totsamp, cursamples, ABYule[self->freqindex]);

        BUTTER_FILTER ( self->lstep + self->totsamp, self->lout + self->totsamp, cursamples, ABButter[self->freqindex]);
        BUTTER_FILTER ( self->rstep + self->totsamp, self->rout + self->totsamp, cursamples, ABButter[self->freqindex]);

        curleft = self->lout + self->totsamp; /* Get the squared values */
        curright = self->rout + self->totsamp;

        i = cursamples % 16;
        while (i--)
            {   self->lsum += fsqr(*curleft++);
                self->rsum += fsqr(*curright++);
            }
        i = cursamples / 16;
        while (i--)
            {   self->lsum += fsqr(curleft[0])
                    + fsqr(curleft[1])
                    + fsqr(curleft[2])
                    + fsqr(curleft[3])
                    + fsqr(curleft[4])
                    + fsqr(curleft[5])
                    + fsqr(curleft[6])
                    + fsqr(curleft[7])
                    + fsqr(curleft[8])
                    + fsqr(curleft[9])
                    + fsqr(curleft[10])
                    + fsqr(curleft[11])
                    + fsqr(curleft[12])
                    + fsqr(curleft[13])
                    + fsqr(curleft[14])
                    + fsqr(curleft[15]);
                curleft += 16;
                self->rsum += fsqr(curright[0])
                    + fsqr(curright[1])
                    + fsqr(curright[2])
                    + fsqr(curright[3])
                    + fsqr(curright[4])
                    + fsqr(curright[5])
                    + fsqr(curright[6])
                    + fsqr(curright[7])
                    + fsqr(curright[8])
                    + fsqr(curright[9])
                    + fsqr(curright[10])
                    + fsqr(curright[11])
                    + fsqr(curright[12])
                    + fsqr(curright[13])
                    + fsqr(curright[14])
                    + fsqr(curright[15]);
                curright += 16;
            }

        batchsamples -= cursamples;
        cursamplepos += cursamples;
        self->totsamp      += cursamples;
        if ( self->totsamp == self->sampleWindow ) {  /* Get the Root Mean Square (RMS) for this set of samples */
            double  val  = STEPS_per_dB * 10. * log10 ( (self->lsum + self->rsum) / self->totsamp * 0.5 + 1.e-37 );
            int     ival = (int) val;
            if ( ival <                     0 ) ival = 0;
            if ( ival >= (int)(sizeof(self->A)/sizeof(*(self->A))) ) ival = sizeof(self->A)/sizeof(*(self->A)) - 1;
            self->A [ival]++;
            self->lsum = self->rsum = 0.;
            memmove ( self->loutbuf , self->loutbuf  + self->totsamp, MAX_ORDER * sizeof(double) );
            memmove ( self->routbuf , self->routbuf  + self->totsamp, MAX_ORDER * sizeof(double) );
            memmove ( self->lstepbuf, self->lstepbuf + self->totsamp, MAX_ORDER * sizeof(double) );
            memmove ( self->rstepbuf, self->rstepbuf + self->totsamp, MAX_ORDER * sizeof(double) );
            self->totsamp = 0;
        }
        if ( self->totsamp > self->sampleWindow )   /* somehow I really screwed up: Error in programming! Contact author about self->totsamp > self->sampleWindow */
            return GAIN_ANALYSIS_ERROR;
    }
    if ( num_samples < MAX_ORDER ) {
        memmove ( self->linprebuf,                           self->linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(double) );
        memmove ( self->rinprebuf,                           self->rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(double) );
        memcpy  ( self->linprebuf + MAX_ORDER - num_samples, left_samples,          num_samples             * sizeof(double) );
        memcpy  ( self->rinprebuf + MAX_ORDER - num_samples, right_samples,         num_samples             * sizeof(double) );
    }
    else {
        memcpy  ( self->linprebuf, left_samples  + num_samples - MAX_ORDER, MAX_ORDER * sizeof(double) );
        memcpy  ( self->rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(double) );
    }

    return GAIN_ANALYSIS_OK;
}
int
AnalyzeSamples(replaygain_t * rgData, const Float_t * left_samples, const Float_t * right_samples,
               size_t num_samples, int num_channels)
{
    const Float_t *curleft;
    const Float_t *curright;
    long    batchsamples;
    long    cursamples;
    long    cursamplepos;
    int     i;

    if (num_samples == 0)
        return GAIN_ANALYSIS_OK;

    cursamplepos = 0;
    batchsamples = (long) num_samples;

    switch (num_channels) {
    case 1:
        right_samples = left_samples;
        break;
    case 2:
        break;
    default:
        return GAIN_ANALYSIS_ERROR;
    }

    if (num_samples < MAX_ORDER) {
        memcpy(rgData->linprebuf + MAX_ORDER, left_samples, num_samples * sizeof(Float_t));
        memcpy(rgData->rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t));
    }
    else {
        memcpy(rgData->linprebuf + MAX_ORDER, left_samples, MAX_ORDER * sizeof(Float_t));
        memcpy(rgData->rinprebuf + MAX_ORDER, right_samples, MAX_ORDER * sizeof(Float_t));
    }

    while (batchsamples > 0) {
        cursamples = batchsamples > rgData->sampleWindow - rgData->totsamp ?
            rgData->sampleWindow - rgData->totsamp : batchsamples;
        if (cursamplepos < MAX_ORDER) {
            curleft = rgData->linpre + cursamplepos;
            curright = rgData->rinpre + cursamplepos;
            if (cursamples > MAX_ORDER - cursamplepos)
                cursamples = MAX_ORDER - cursamplepos;
        }
        else {
            curleft = left_samples + cursamplepos;
            curright = right_samples + cursamplepos;
        }

        YULE_FILTER(curleft, rgData->lstep + rgData->totsamp, cursamples,
                    ABYule[rgData->freqindex]);
        YULE_FILTER(curright, rgData->rstep + rgData->totsamp, cursamples,
                    ABYule[rgData->freqindex]);

        BUTTER_FILTER(rgData->lstep + rgData->totsamp, rgData->lout + rgData->totsamp, cursamples,
                      ABButter[rgData->freqindex]);
        BUTTER_FILTER(rgData->rstep + rgData->totsamp, rgData->rout + rgData->totsamp, cursamples,
                      ABButter[rgData->freqindex]);

        curleft = rgData->lout + rgData->totsamp; /* Get the squared values */
        curright = rgData->rout + rgData->totsamp;

        i = cursamples % 8;
        while (i--) {
            rgData->lsum += fsqr(*curleft++);
            rgData->rsum += fsqr(*curright++);
        }
        i = cursamples / 8;
        while (i--) {
            rgData->lsum += fsqr(curleft[0])
                + fsqr(curleft[1])
                + fsqr(curleft[2])
                + fsqr(curleft[3])
                + fsqr(curleft[4])
                + fsqr(curleft[5])
                + fsqr(curleft[6])
                + fsqr(curleft[7]);
            curleft += 8;
            rgData->rsum += fsqr(curright[0])
                + fsqr(curright[1])
                + fsqr(curright[2])
                + fsqr(curright[3])
                + fsqr(curright[4])
                + fsqr(curright[5])
                + fsqr(curright[6])
                + fsqr(curright[7]);
            curright += 8;
        }

        batchsamples -= cursamples;
        cursamplepos += cursamples;
        rgData->totsamp += cursamples;
        if (rgData->totsamp == rgData->sampleWindow) { /* Get the Root Mean Square (RMS) for this set of samples */
            double const val =
                STEPS_per_dB * 10. * log10((rgData->lsum + rgData->rsum) / rgData->totsamp * 0.5 +
                                           1.e-37);
            size_t  ival = (val <= 0) ? 0 : (size_t) val;
            if (ival >= sizeof(rgData->A) / sizeof(*(rgData->A)))
                ival = sizeof(rgData->A) / sizeof(*(rgData->A)) - 1;
            rgData->A[ival]++;
            rgData->lsum = rgData->rsum = 0.;
            memmove(rgData->loutbuf, rgData->loutbuf + rgData->totsamp,
                    MAX_ORDER * sizeof(Float_t));
            memmove(rgData->routbuf, rgData->routbuf + rgData->totsamp,
                    MAX_ORDER * sizeof(Float_t));
            memmove(rgData->lstepbuf, rgData->lstepbuf + rgData->totsamp,
                    MAX_ORDER * sizeof(Float_t));
            memmove(rgData->rstepbuf, rgData->rstepbuf + rgData->totsamp,
                    MAX_ORDER * sizeof(Float_t));
            rgData->totsamp = 0;
        }
        if (rgData->totsamp > rgData->sampleWindow) /* somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow */
            return GAIN_ANALYSIS_ERROR;
    }
    if (num_samples < MAX_ORDER) {
        memmove(rgData->linprebuf, rgData->linprebuf + num_samples,
                (MAX_ORDER - num_samples) * sizeof(Float_t));
        memmove(rgData->rinprebuf, rgData->rinprebuf + num_samples,
                (MAX_ORDER - num_samples) * sizeof(Float_t));
        memcpy(rgData->linprebuf + MAX_ORDER - num_samples, left_samples,
               num_samples * sizeof(Float_t));
        memcpy(rgData->rinprebuf + MAX_ORDER - num_samples, right_samples,
               num_samples * sizeof(Float_t));
    }
    else {
        memcpy(rgData->linprebuf, left_samples + num_samples - MAX_ORDER,
               MAX_ORDER * sizeof(Float_t));
        memcpy(rgData->rinprebuf, right_samples + num_samples - MAX_ORDER,
               MAX_ORDER * sizeof(Float_t));
    }

    return GAIN_ANALYSIS_OK;
}
int
AnalyzeSamples ( const Float_t* left_samples, const Float_t* right_samples, size_t num_samples, int num_channels )
{
    const Float_t*  curleft;
    const Float_t*  curright;
    long            batchsamples;
    long            cursamples;
    long            cursamplepos;
    int             i;

    if ( num_samples == 0 )
        return GAIN_ANALYSIS_OK;

    cursamplepos = 0;
    batchsamples = num_samples;

    switch ( num_channels) {
    case  1: right_samples = left_samples;
    case  2: break;
    default: return GAIN_ANALYSIS_ERROR;
    }

    if ( num_samples < MAX_ORDER ) {
        memcpy ( linprebuf + MAX_ORDER, left_samples , num_samples * sizeof(Float_t) );
        memcpy ( rinprebuf + MAX_ORDER, right_samples, num_samples * sizeof(Float_t) );
    }
    else {
        memcpy ( linprebuf + MAX_ORDER, left_samples,  MAX_ORDER   * sizeof(Float_t) );
        memcpy ( rinprebuf + MAX_ORDER, right_samples, MAX_ORDER   * sizeof(Float_t) );
    }

    while ( batchsamples > 0 ) {
        cursamples = batchsamples > sampleWindow-totsamp  ?  sampleWindow - totsamp  :  batchsamples;
        if ( cursamplepos < MAX_ORDER ) {
            curleft  = linpre+cursamplepos;
            curright = rinpre+cursamplepos;
            if (cursamples > MAX_ORDER - cursamplepos )
                cursamples = MAX_ORDER - cursamplepos;
        }
        else {
            curleft  = left_samples  + cursamplepos;
            curright = right_samples + cursamplepos;
        }

        YULE_FILTER ( curleft , lstep + totsamp, cursamples, ABYule[freqindex]);
        YULE_FILTER ( curright, rstep + totsamp, cursamples, ABYule[freqindex]);

        BUTTER_FILTER ( lstep + totsamp, lout + totsamp, cursamples, ABButter[freqindex]);
        BUTTER_FILTER ( rstep + totsamp, rout + totsamp, cursamples, ABButter[freqindex]);

        curleft = lout + totsamp;                   // Get the squared values
        curright = rout + totsamp;

        i = cursamples % 16;
        while (i--)
        {   lsum += fsqr(*curleft++);
            rsum += fsqr(*curright++);
        }
        i = cursamples / 16;
        while (i--)
        {   lsum += fsqr(curleft[0])
                  + fsqr(curleft[1])
                  + fsqr(curleft[2])
                  + fsqr(curleft[3])
                  + fsqr(curleft[4])
                  + fsqr(curleft[5])
                  + fsqr(curleft[6])
                  + fsqr(curleft[7])
                  + fsqr(curleft[8])
                  + fsqr(curleft[9])
                  + fsqr(curleft[10])
                  + fsqr(curleft[11])
                  + fsqr(curleft[12])
                  + fsqr(curleft[13])
                  + fsqr(curleft[14])
                  + fsqr(curleft[15]);
            curleft += 16;
            rsum += fsqr(curright[0])
                  + fsqr(curright[1])
                  + fsqr(curright[2])
                  + fsqr(curright[3])
                  + fsqr(curright[4])
                  + fsqr(curright[5])
                  + fsqr(curright[6])
                  + fsqr(curright[7])
                  + fsqr(curright[8])
                  + fsqr(curright[9])
                  + fsqr(curright[10])
                  + fsqr(curright[11])
                  + fsqr(curright[12])
                  + fsqr(curright[13])
                  + fsqr(curright[14])
                  + fsqr(curright[15]);
            curright += 16;
        }

        batchsamples -= cursamples;
        cursamplepos += cursamples;
        totsamp      += cursamples;
        if ( totsamp == sampleWindow ) {  // Get the Root Mean Square (RMS) for this set of samples
            double  val  = STEPS_per_dB * 10. * log10 ( (lsum+rsum) / totsamp * 0.5 + 1.e-37 );
            int     ival = (int) val;
            if ( ival <                     0 ) ival = 0;
            if ( ival >= (int)(sizeof(A)/sizeof(*A)) ) ival = sizeof(A)/sizeof(*A) - 1;
            A [ival]++;
            lsum = rsum = 0.;
            memmove ( loutbuf , loutbuf  + totsamp, MAX_ORDER * sizeof(Float_t) );
            memmove ( routbuf , routbuf  + totsamp, MAX_ORDER * sizeof(Float_t) );
            memmove ( lstepbuf, lstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
            memmove ( rstepbuf, rstepbuf + totsamp, MAX_ORDER * sizeof(Float_t) );
            totsamp = 0;
        }
        if ( totsamp > sampleWindow )   // somehow I really screwed up: Error in programming! Contact author about totsamp > sampleWindow
            return GAIN_ANALYSIS_ERROR;
    }
    if ( num_samples < MAX_ORDER ) {
        memmove ( linprebuf,                           linprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
        memmove ( rinprebuf,                           rinprebuf + num_samples, (MAX_ORDER-num_samples) * sizeof(Float_t) );
        memcpy  ( linprebuf + MAX_ORDER - num_samples, left_samples,          num_samples             * sizeof(Float_t) );
        memcpy  ( rinprebuf + MAX_ORDER - num_samples, right_samples,         num_samples             * sizeof(Float_t) );
    }
    else {
        memcpy  ( linprebuf, left_samples  + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
        memcpy  ( rinprebuf, right_samples + num_samples - MAX_ORDER, MAX_ORDER * sizeof(Float_t) );
    }

    return GAIN_ANALYSIS_OK;
}