/***************************************************************************
 *   Copyright (C) 2004 by Predrag Viceic                                  *
 *   viceic@net2000.ch                                             *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/
#include "soundmanager.h"
#include <stdio.h>

using namespace soundtouch;


SoundManager::SoundManager():SoundHolder(),FFTHolder()
{
        instant_buffer=0;

        amp_domain_instant_energy_buffersize=pow(2,8);
        amp_domain_average_energy_buffersize=pow(2,11);
        analysisMethod=AMP_DOMAIN;
        derivateSignal=FALSE;
        effects=0;
        windowMode=WINDOW_HANNING;
        postProcessEffects=new CustomEffects();
        startBand=0;
        endBand=512; //hardcoded, so what?!
        inversionBand=FALSE;
        playingBandOnly=FALSE;
        substractSpectralMean=TRUE;
        beatLines.clear();
        lockedBeatLines.clear();
        envelopeData=0;
        envelopeArray=0;
}


SoundManager::~SoundManager()
{
        if(instant_buffer) zaparr(instant_buffer);
        if(hirestemp_subband_energies) zaparr(hirestemp_subband_energies);
        if(hiresfreq_subband_energies) zaparr(hiresfreq_subband_energies);
}

/*!
    \fn SoundManager::analyse()
 */
QMemArray<long> SoundManager::analyse(const double treshold,
                                                                                   const int window){
        return analyse(treshold,window,0,getFrames());
    }

QMemArray<long> SoundManager::analyse(const double treshold,
                                                                                   const int window,long start_sample,long end_sample){
    assert(start_sample>=0 && end_sample<=getFrames());
    if(analysisMethod==AMP_DOMAIN)
        return analyse_time_domain(treshold,window,start_sample,end_sample);
    else if(analysisMethod==FREQ_DOMAIN)
        return analyse_freq_domain(treshold,window,start_sample,end_sample);
    #ifdef HAS_AUBIO
        else if(analysisMethod==COMPLEX_DOMAIN || analysisMethod==HFC
                            || analysisMethod==PHASE_DIFF || analysisMethod==COMPLEX_DOMAIN)
        return analyse_aubio_onsets(treshold,window,start_sample,end_sample);        
    #endif // HAS_AUBIO
    //else return error;
}

QMemArray<long> SoundManager::analyse_freq_domain(const double treshold,
                                                                                   const int window,long start_sample,long end_sample)
{
    assert(start_sample>=0 && end_sample<=getFrames());
    double max_variance=0;
    double min_variance=10;
    QMemArray<beat> tempbeats(0); // array of longs
    tempbeats.resize(0);
    beats.resize(0);
    QProgressDialog progress("Analyzing input signal...","Abort",getFrames(),NULL,"progress",TRUE);
    progress.setMinimumDuration(2000);

    int average_energy_buffersize=44032;
    int subband_number=this->getHiresTempNbBands();

    if(!hirestemp_subband_energies){
                    hirestemp_subband_energies=
                        compute_subband_energies(subband_number,hirestemp_fft_width,
                                            hirestemp_fft_advance,
                                            hirestemp_padding,0,getFrames(),
                                            hirestemp_startfreq,hirestemp_endfreq,
                                            hirestemp_fftMinima,hirestemp_fftMaxima,ORIGINAL_WAVE);
    }

    double* average_energy=new double[subband_number];


    //double manual_treshold=linear_regression(0.0,20.0,2.0,5.0,treshold);
    //double manual_treshold=MyMath::linear_regression(0.0,20.0,0.05,1,treshold);
    double manual_treshold=
                MyMath::linear_regression(1.0,20.0,0,1.01,treshold);
    if(foundBeats.size()==0){
        cout<<"Recomputing beats in frequency domain..\n";
        for (int i=start_sample;i<end_sample-hirestemp_fft_advance;i+=hirestemp_fft_advance){
                //compute average_energy
                int down_limit=0;
                int up_limit=0;
                if(i<average_energy_buffersize){
                            down_limit=start_sample;
                            up_limit=average_energy_buffersize;
                }else{
                        down_limit=i-average_energy_buffersize;
                        up_limit=i;
                }


                for (int k=0;k<subband_number;k++){
                    double temp_avg=0;
                    double count=0;

                    for (int j=down_limit;j<up_limit;j+=hirestemp_fft_advance){
                        temp_avg+=hirestemp_subband_energies[k][j/hirestemp_fft_advance];
                        count++;
                    }
                    average_energy[k]=temp_avg/count;

                    double variance=0;
                    count=0;
                    for (int i2=down_limit;i2<up_limit;i2+=hirestemp_fft_advance){
                        double temp=(hirestemp_subband_energies[k][i2/hirestemp_fft_advance]-average_energy[k]);
                        variance+=temp*temp;
                        count++;
                        /*
                        std::cout<<"var: "<<variance<<" inst: "<<instant_buffer[i2/instant_energy_buffersize]
                                <<" avg: "<<average_energy<<"\n";
                        */
                    }
                    variance=variance/count;
                    if(variance>max_variance) max_variance=variance;
                    if(variance<min_variance) min_variance=variance;
                    //std::cout<<"var: "<<variance<<" min: "<<min_variance<<" max: "<<max_variance<<endl;

                    double computed_treshold=
                        MyMath::linear_regression(min_variance,max_variance,10.0,500.0,variance);
                    if(min_variance==max_variance) computed_treshold=1;

                    computed_treshold=computed_treshold;//*manual_treshold;
                    //std::cout<<"computed_treshold: "<<computed_treshold<<"\n";
                    //std::cout<<"avg["<<k<<"]["<<i/instant_energy_buffersize<<"] computed \n";
                    double temp_instant=hirestemp_subband_energies[k][i/hirestemp_fft_advance];

                    if(temp_instant>average_energy[k] *computed_treshold){
                            tempbeats.resize(tempbeats.size()+1);
                            tempbeats[tempbeats.size()-1].sample_pos=i;
                            tempbeats[tempbeats.size()-1].band=k;
                            tempbeats[tempbeats.size()-1].instant_energy=temp_instant;

                            tempbeats[tempbeats.size()-1].energy_at_pos=getLeftChannel(ORIGINAL_WAVE)[i];

                            /*
                            tempbeats[tempbeats.size()-1].min_amplitude=min_amplitude;
                            tempbeats[tempbeats.size()-1].max_amplitude=max_amplitude;

                            tempbeats[tempbeats.size()-1].min_pos=min_pos;
                            tempbeats[tempbeats.size()-1].max_pos=max_pos;

                            tempbeats[tempbeats.size()-1].left_half_energy=left_half_energy;
                            tempbeats[tempbeats.size()-1].right_half_energy=right_half_energy;
                            */
                            tempbeats[tempbeats.size()-1].computed_treshold=computed_treshold;
                    }
                        //emit(newBeatLineAvailable(i));
                }
                if(i%1000==0){
                    progress.setProgress(i);
                    progress.setLabelText("Analyzing input signal...(frame: "+QString::number(i)+")");
                }
                if(progress.wasCanceled()) break;
        }
        zaparr(average_energy);

        //emit(analysisFinished());

        //remove redundant lines (~same beatline in multiple bands)
        QMemArray<beat> tempbeats1=removeRedundantBeatlines(tempbeats,16);
        tempbeats.resize(0);
        //end remove redundant lines


        //find zero crossings
        int search_distance=256;
        int beatline_distance=getHiresTempFFTAdvance()*2;
        QMemArray<beat> tempbeats2=findPreciseBeat(tempbeats1,search_distance,beatline_distance,1.0,0.8);
        tempbeats1.resize(0);

        search_distance=128;
        beatline_distance=getHiresTempFFTAdvance();
        QMemArray<beat> tempbeats3=findPreciseBeat(tempbeats2,search_distance,beatline_distance,1.0,0.8);
        tempbeats2.resize(0);

        search_distance=64;
        beatline_distance=getHiresTempFFTAdvance()/2;
        QMemArray<beat> tempbeats4=findPreciseBeat(tempbeats3,search_distance,beatline_distance,1.0,0.8);
        tempbeats3.resize(0);

        search_distance=64;
        beatline_distance=getHiresTempFFTAdvance()/4;
        QMemArray<beat> tempbeats7=findPreciseBeat(tempbeats4,search_distance,beatline_distance,1.0,0.8);
        tempbeats4.resize(0);

        search_distance=0;
        beatline_distance=64;
        QMemArray<beat> tempbeats8=findPreciseBeat(tempbeats7,search_distance,beatline_distance,1.0,0.2);
        tempbeats7.resize(0);
        //end find zero crossings

        foundBeats.resize(0);
        for (int i=0;i<tempbeats8.size();i++){
            foundBeats.resize(foundBeats.size()+1);
            foundBeats[foundBeats.size()-1]=tempbeats8[i];
            foundBeats[foundBeats.size()-1].active=TRUE;
        }

        updateMaxAmplitudes(foundBeats,1024);
    }

    QMemArray<beat> tempbeats9(0); // array of beats
    //keep the beatlines exceeding slider amplitude
    for (int i=0;i<foundBeats.size();i++){
        if(foundBeats[i].max_amplitude>=manual_treshold && foundBeats[i].active){
            tempbeats9.resize(tempbeats9.size()+1);
            tempbeats9[tempbeats9.size()-1]=foundBeats[i];
        }
    }
    //keep the first beatline in a packet
    int distance_limit=4096*manual_treshold;
    for (int i=0;i<tempbeats9.size();i++){
        if(i==0 || tempbeats9[i].sample_pos-beats[beats.size()-1] > distance_limit){
            beats.resize(beats.size()+1);
            beats[beats.size()-1]=tempbeats9[i].sample_pos;
        }
        /*
        else if(tempbeats[i].energy>tempbeats[i-1].energy*2.0){
            beats[beats.size()-1]=tempbeats[i].sample_pos;
        }
        */
    }
    return beats;
}

#ifdef HAS_AUBIO
        /*!
            \fn SoundManager::analyse_aubio_onsets()
         */
        QMemArray<long> SoundManager::analyse_aubio_onsets(const double treshold,
                                                                                           const int window,long start_sample,long end_sample)
        {
        assert(start_sample>=0 && end_sample<=getFrames());
         beats.resize(0);
        QProgressDialog progress("Analyzing input signal...","Abort",getFrames(),NULL,"progress",TRUE);
        progress.setMinimumDuration(2000);

        aubio_onsetdetection_type aubio_method;

        switch(analysisMethod) {
            case COMPLEX_DOMAIN:
                aubio_method = complexdomain;
                break;
            case PHASE_DIFF:
                aubio_method = phase;
                    break;
            case SPEC_DIFF:
                aubio_method = specdiff;
                break;
            case HFC:
                aubio_method = hfc;
                    break;
            default:
                break;
        }

        uint_t i            = 0;
        uint_t k            = 0;
        uint_t isonset      = 0;
        uint_t buffer_size  = 1024;
        uint_t overlap_size = 512;
        smpl_t silence_thre = -65.;
        fvec_t * ibuf              = new_fvec(overlap_size, channels);
        fvec_t * onset             = new_fvec(1, channels);
        cvec_t * fftgrain          = new_cvec(buffer_size, channels);
        aubio_pvoc_t * pv          = new_aubio_pvoc(buffer_size, overlap_size, channels);
        aubio_onsetdetection_t * o = new_aubio_onsetdetection(aubio_method,buffer_size,channels);
        aubio_pickpeak_t * parms   = new_aubio_peakpicker(treshold/15.);

        float* lchannel=0;
        float* rchannel=0;
         if(derivateSignal){
             lchannel=exportDerivatedLeftBuffer(0,getFrames(),ORIGINAL_WAVE);
             rchannel=exportDerivatedRightBuffer(0,getFrames(),ORIGINAL_WAVE);
        }else{
            lchannel=getLeftChannel(ORIGINAL_WAVE);
            rchannel=getRightChannel(ORIGINAL_WAVE);
        }

        for (k = 0; k < frames / overlap_size; k++) {
            for (i = 0; i < overlap_size; i++) {
                ibuf->data[0][i] = lchannel[k*overlap_size+i];
                ibuf->data[1][i] = rchannel[k*overlap_size+i];
            }
            aubio_pvoc_do (pv,ibuf, fftgrain);
            aubio_onsetdetection(o,fftgrain, onset);
            isonset = aubio_peakpick_pimrt(onset,parms);
            if (isonset && !aubio_silence_detection(ibuf, silence_thre)) {
                beats.resize(beats.size()+1);
                // missing zero crossing here
                    if (k < 4) k = 4;
                    beats[beats.size()-1]= (k-4) * overlap_size;
            }
            if (! (k % 10)) {
                progress.setProgress(k*overlap_size);
                progress.setLabelText("Analyzing input signal...(frame: "+QString::number(k*overlap_size)+")");
            }
        }

        progress.close();

        del_fvec(ibuf);
        del_fvec(onset);
        del_cvec(fftgrain);
        del_aubio_pvoc(pv);
        if(derivateSignal){
            zaparr(lchannel);
            zaparr(rchannel);
        }

        return beats;
}
#endif // HAS_AUBIO

/*!
    \fn SoundManager::analyse()
 */
QMemArray<long> SoundManager::analyse_time_domain(const double treshold,
                                                                                   const int window,long start_sample,long end_sample)
{
        //double max_variance=30000;
        //double min_variance=30000;
        assert(start_sample>=0 && end_sample<=getFrames());
        double max_variance=0;
        double min_variance=10;

        //static QMemArray<long> beats(0); // array of longs
        QMemArray<beat> tempbeats(0); // array of longs
        tempbeats.resize(1);
        tempbeats[0].sample_pos=0;
        tempbeats[0].band=0;
        tempbeats[0].instant_energy=1;
        tempbeats[0].energy_at_pos=1;
        double manual_treshold=
                MyMath::linear_regression(1.0,20.0,0,1.01,treshold);



        QProgressDialog progress("Analyzing input signal...","Abort",getFrames(),NULL,"progress",TRUE);
        progress.setMinimumDuration(2000);

        float* lchannel=0;
        float* rchannel=0;
        if(derivateSignal){
            lchannel=exportDerivatedLeftBuffer(0,getFrames(),ORIGINAL_WAVE);
            rchannel=exportDerivatedRightBuffer(0,getFrames(),ORIGINAL_WAVE);
        }else{
            //lchannel=exportLeftBuffer(0,getFrames());
            //rchannel=exportRightBuffer(0,getFrames());

            lchannel=getLeftChannel(ORIGINAL_WAVE);
            rchannel=getRightChannel(ORIGINAL_WAVE);
        }

        //std::cout<<"window: "<<window<<" treshold: "<<treshold<<"\n";
        //int average_energy_buffersize=44032;
        int average_energy_buffersize=amp_domain_average_energy_buffersize;

        if(!instant_buffer){
            cout<<"Started computing instant energies\n";
            QProgressDialog progress2("Computing instant energies...",NULL,getFrames(),NULL,"progress",TRUE);
            progress2.setMinimumDuration(2000);
            instant_buffer=new float[(int)((float)getFrames()/(float)amp_domain_instant_energy_buffersize)+1];
            //compute instant energies
            for(int i=0;i<frames-amp_domain_instant_energy_buffersize;
            i+=amp_domain_instant_energy_buffersize){
                double temp_ie=0;
                for(int i2=i;i2<i+amp_domain_instant_energy_buffersize;i2++){
                    temp_ie+=fabs(lchannel[i2]);
                }

                instant_buffer[i/amp_domain_instant_energy_buffersize]=temp_ie;
                //std::cout<<"inst: "<<temp_ie<<"\n";
                if((i/amp_domain_instant_energy_buffersize)%10==0){
                    progress2.setProgress(i);
                    progress2.setLabelText("Computing instant energies...(frame: "+QString::number(i)+")");
                }
            }
            progress2.close();
            cout<<"Ended computing instant energies\n";
        }
        //char a;
        //std::cin>>a;
        //end compute instant energies
        if(foundBeats.size()==0){
            cout<<"Recomputing beats in time domain..\n";
            beat last_beat;
            last_beat.sample_pos=start_sample-1;
            last_beat.instant_energy=FLT_MIN;
            last_beat.band=-1;

            for (int i=start_sample;i<end_sample-amp_domain_instant_energy_buffersize;
            i+=amp_domain_instant_energy_buffersize){
                //compute average_energy
                int down_limit=0;
                int up_limit=0;
                if(i<average_energy_buffersize){
                            down_limit=start_sample;
                            up_limit=average_energy_buffersize;
                }else{
                        down_limit=i-average_energy_buffersize;
                        up_limit=i;
                }

                int count=0;
                double average_energy=0;
                for (int i2=down_limit;i2<up_limit;i2+=amp_domain_instant_energy_buffersize){
                    average_energy=average_energy+
                        (instant_buffer[i2/amp_domain_instant_energy_buffersize]);
                    count++;
    /*
                    std::cout<<"avg: "<<average_energy<<" inst:"<<instant_buffer[i2/amp_domain_instant_energy_buffersize]<<"\n";
    */
                }
                average_energy=average_energy/float(count);
                //end compute average_energy

                //compute variance

                count=0;
                double variance=0;
                for (int i2=down_limit;i2<up_limit;i2+=amp_domain_instant_energy_buffersize){
                    double temp=(instant_buffer[i2/amp_domain_instant_energy_buffersize]-average_energy);
                    variance+=(temp*temp);
                    count++;
                    /*
                    std::cout<<"var: "<<variance<<" inst: "<<instant_buffer[i2/instant_energy_buffersize]
                            <<" avg: "<<average_energy<<"\n";
                    */
                }

                variance=sqrt(variance/float(count));
                if(variance>max_variance) max_variance=variance;
                if(variance<min_variance) min_variance=variance;
                //end compute variance
                double computed_treshold=MyMath::linear_regression(min_variance,max_variance,1.0,1.2,variance);
                if(min_variance==max_variance) computed_treshold=1;

                computed_treshold=computed_treshold;//*manual_treshold;


    /*
                std::cout<<"inst: "<<instant_buffer[i/instant_energy_buffersize]
                    <<" avg: "<<average_energy<<" var: "<<variance<<" c_t: "<<computed_treshold<<"\n";
    */


                //cout<<"instant_energy: "<<instant_buffer[i/amp_domain_instant_energy_buffersize];
                //cout<<" average_energy: "<<average_energy<<"\n";
                long currentInstantEnergy=instant_buffer[i/amp_domain_instant_energy_buffersize];
                if(currentInstantEnergy>average_energy/* * computed_treshold*/){
                            long pos=i;
                            float min_amplitude=FLT_MAX;
                            float max_amplitude=0;
                            long min_pos=0;
                            long max_pos=0;
                            float left_half_energy=0;
                            float right_half_energy=0;
                            //find absolute minima and maxima
                            for (int count=i;count<i+amp_domain_instant_energy_buffersize;count++){
                                if(fabs(getLeftChannel(ORIGINAL_WAVE)[count]) <=min_amplitude){
                                    min_amplitude=fabs(getLeftChannel(ORIGINAL_WAVE)[count]);
                                    min_pos=count;
                                }
                                if(fabs(getLeftChannel(ORIGINAL_WAVE)[count]) >=max_amplitude){
                                    max_amplitude=fabs(getLeftChannel(ORIGINAL_WAVE)[count]);
                                    max_pos=count;
                                }
                                if(count<i+amp_domain_instant_energy_buffersize/2){
                                    left_half_energy+=fabs(getLeftChannel(ORIGINAL_WAVE)[count]);
                                }else{
                                    right_half_energy+=fabs(getLeftChannel(ORIGINAL_WAVE)[count]);
                                }
                            }
                            //end find absolute minima and maxima
                            tempbeats.resize(tempbeats.size()+1);
                            tempbeats[tempbeats.size()-1].sample_pos=pos;
                            tempbeats[tempbeats.size()-1].band=0;
                            tempbeats[tempbeats.size()-1].instant_energy=currentInstantEnergy;
                            tempbeats[tempbeats.size()-1].energy_at_pos=getLeftChannel(ORIGINAL_WAVE)[pos];
                            tempbeats[tempbeats.size()-1].min_amplitude=min_amplitude;
                            tempbeats[tempbeats.size()-1].max_amplitude=max_amplitude;
                            tempbeats[tempbeats.size()-1].min_pos=min_pos;
                            tempbeats[tempbeats.size()-1].max_pos=max_pos;
                            tempbeats[tempbeats.size()-1].left_half_energy=left_half_energy;
                            tempbeats[tempbeats.size()-1].right_half_energy=right_half_energy;
                            tempbeats[tempbeats.size()-1].computed_treshold=computed_treshold;
                }

                if(((i-start_sample)/amp_domain_instant_energy_buffersize)%1000==0){
                    progress.setProgress(i);
                    progress.setLabelText("Analyzing input signal...(frame: "+QString::number(i)+")");
                    QApplication::eventLoop()->processEvents(QEventLoop::AllEvents);
                }
                if(progress.wasCanceled()) break;
            }
            progress.close();
            cout<<"Recomputing beats in time domain ended\n";

            cout<<"Finding Zero-Crossings\n";
            QProgressDialog progress3("Finding Zero-Crossings","Abort",8,NULL,"progress",TRUE);
            progress.setMinimumDuration(2000);



            //end compute energy
            std::cout<<"max variance: "<<max_variance<<" min variance: "<<min_variance<<endl;
            if(derivateSignal){
                zaparr(lchannel);
                zaparr(rchannel);
            }

            int search_distance=256;
            int beatline_distance=amp_domain_instant_energy_buffersize*2;
            QMemArray<beat> tempbeats2=findPreciseBeat(tempbeats,search_distance,beatline_distance,1.0,0.8);

            progress3.setProgress(1);
            progress3.setLabelText("Finding Zero-Crossings...(stage 1)");

            search_distance=128;
            beatline_distance=amp_domain_instant_energy_buffersize;
            QMemArray<beat> tempbeats3=findPreciseBeat(tempbeats2,search_distance,beatline_distance,1.0,0.8);

            progress3.setProgress(2);
            progress3.setLabelText("Finding Zero-Crossings...(stage 2)");

            search_distance=64;
            beatline_distance=amp_domain_instant_energy_buffersize/2;
            QMemArray<beat> tempbeats4=findPreciseBeat(tempbeats3,search_distance,beatline_distance,1.0,0.8);

            progress3.setProgress(3);
            progress3.setLabelText("Finding Zero-Crossings...(stage 3)");
/*
            search_distance=32;
            beatline_distance=amp_domain_instant_energy_buffersize/2;
            QMemArray<beat> tempbeats5=findPreciseBeat(tempbeats4,search_distance,beatline_distance,1.0,0.8);


            search_distance=32;
            beatline_distance=amp_domain_instant_energy_buffersize/4;
            QMemArray<beat> tempbeats6=findPreciseBeat(tempbeats5,search_distance,beatline_distance,1.0,0.8);
*/

            progress3.setProgress(4);
            progress3.setLabelText("Finding Zero-Crossings...(stage 4)");

            search_distance=64;
            beatline_distance=amp_domain_instant_energy_buffersize/4;
            QMemArray<beat> tempbeats7=findPreciseBeat(tempbeats4,search_distance,beatline_distance,1.0,0.8);

            progress3.setProgress(5);
            progress3.setLabelText("Finding Zero-Crossings...(stage 5)");

            search_distance=0;
            beatline_distance=64;
            QMemArray<beat> tempbeats8=findPreciseBeat(tempbeats7,search_distance,beatline_distance,1.0,0.2);

            progress3.setProgress(6);
            progress3.setLabelText("Finding Zero-Crossings...(stage 6)");

            foundBeats.resize(0);
            for (int i=0;i<tempbeats8.size();i++){
                if(foundBeats.size()==0 || foundBeats[foundBeats.size()-1].sample_pos!=tempbeats8[i].sample_pos){
                    foundBeats.resize(foundBeats.size()+1);
                    foundBeats[foundBeats.size()-1]=tempbeats8[i];
                    foundBeats[foundBeats.size()-1].active=TRUE;
                }
            }
            updateMaxAmplitudes(foundBeats, 1024);
            cout<<"Finding Zero-Crossings ended\n";
            progress3.setProgress(8);
            progress3.setLabelText("Finding Zero-Crossings...(stage 8)");
            progress3.close();
        }


        QMemArray<beat> tempbeats3(0); // array of beats

        //keep the beatlines exceeding slider amplitude
        for (int i=0;i<foundBeats.size();i++){
            if(foundBeats[i].max_amplitude>=manual_treshold && foundBeats[i].active){
                tempbeats3.resize(tempbeats3.size()+1);
                tempbeats3[tempbeats3.size()-1]=foundBeats[i];
            }
        }

        //keep the first beatline in a packet
        int distance_limit=4096*manual_treshold;
        QMemArray<beat> tempbeats4(0); // array of beats
        for (int i=0;i<tempbeats3.size();i++){
                //if beatline not to close to the last one add it

                if(tempbeats4.size()==0 ||
                    ((tempbeats3[i].sample_pos-tempbeats4[tempbeats4.size()-1].sample_pos)>=distance_limit)){
                    tempbeats4.resize(tempbeats4.size()+1);
                    tempbeats4[tempbeats4.size()-1]=tempbeats3[i];

                }else{
                    //else take the beatline with stronger amplitude
		    /*
		    if(tempbeats3[i].max_amplitude>tempbeats4[tempbeats4.size()-1].max_amplitude){
		    	tempbeats4[tempbeats4.size()-1]=tempbeats3[i];
		    }
		    */
		    /*
                    if(tempbeats3[i].right_half_energy-tempbeats3[i].left_half_energy>
                    tempbeats4[tempbeats4.size()-1].right_half_energy-tempbeats4[tempbeats4.size()-1].left_half_energy){
                        tempbeats4[tempbeats4.size()-1]=tempbeats3[i];
                    }
		    */
                }

        }



        beats.resize(0);
        for (int i=0;i<tempbeats4.size();i++){
            if(tempbeats4[i].sample_pos>=start_sample && tempbeats4[i].sample_pos<=end_sample){
                beats.resize(beats.size()+1);
                beats[beats.size()-1]=tempbeats4[i].sample_pos;
            }
        }
        return beats;
}

/*!
    \fn SoundManager::resample()
 */
void SoundManager::resample(list<long>sortedPos,double new_to_oldBPM_ratio)
{
        fileManager->deleteAfterCurrent();
        pushSoundBuffer(ORIGINAL_WAVE);
#ifdef FLOAT_SAMPLES
        cout<<"SoundTouch compiled with FLOAT_SAMPLES\n";
#else
        cout<<"SoundTouch compiled with INTEGER_SAMPLES\n";
#endif;
        QProgressDialog progress("Time stretching/expanding...","Abort",getFrames(),NULL,"progress",TRUE);
        progress.setMinimumDuration(2000);
        progress.setProgress(0);
        SoundTouch* st=new SoundTouch();
	st->setSampleRate(getRate());
	st->setChannels(getChannels());
	st->setTempo(new_to_oldBPM_ratio);
	st->setPitch(1);
	st->setRate(1);
	cout<<"new to old bpm: "<<new_to_oldBPM_ratio<<"\n";

	int startpos(0);
	int endpos(0);
	list<long>::iterator it;
	int output_buffer_size=0;
	int input_buffer_size=0;
        long currentPos=0;
#ifdef FLOAT_SAMPLES
        float* outputWave=
            new float[(int)ceil(getFrames()*(1/new_to_oldBPM_ratio))*getChannels()];
#else
        short* outputWave=
            new short[(int)ceil(getFrames()*(1/new_to_oldBPM_ratio))*getChannels()];
#endif

	for (it=sortedPos.begin();it!=sortedPos.end();++it){
		startpos=endpos;
		endpos=(int)*it;
                cout<<"resampling from: "<<startpos<<" to: "<<endpos<<endl;
		input_buffer_size=endpos-startpos;
		output_buffer_size=(int)ceil((endpos-startpos)*(1/new_to_oldBPM_ratio));


#ifdef FLOAT_SAMPLES
                float * tmp_wave = new float[(endpos-startpos)*2];
                exportMultiplexedBuffer(tmp_wave,startpos,endpos,ORIGINAL_WAVE);
                float * tmp_wave2 = new float[output_buffer_size*getChannels()];
#else
                short * tmp_wave = new short[(endpos-startpos)*2];
                exportMultiplexedShortBuffer(tmp_wave,startpos,endpos,ORIGINAL_WAVE);
                short * tmp_wave2 = new short[output_buffer_size*getChannels()];
#endif


		st->putSamples(tmp_wave,endpos-startpos);
		uint returned=0;
		do{
                        returned=st->receiveSamples(tmp_wave2,output_buffer_size);
#ifdef FLOAT_SAMPLES
                        outputWave=SoundHolder::insertArray(outputWave,
                                                                    tmp_wave2,currentPos,returned);
#else
                        outputWave=SoundHolder::insertShortArray(outputWave,
                                                                    tmp_wave2,currentPos,returned);
#endif
                       currentPos+=returned;
                       if(true){
                            progress.setProgress(currentPos);
                            progress.setLabelText("Time stretching/expanding...(frame: "+QString::number(currentPos)+")");
                        }
		}while(returned!=0);
                zaparr(tmp_wave);
                zaparr(tmp_wave2);
	    }
#ifdef FLOAT_SAMPLES
	float * tmp_wave2 = new float[output_buffer_size*getChannels()];
#else
        short * tmp_wave2 = new short[output_buffer_size*getChannels()];
#endif
	st->flush();
	uint returned=0;
	do{
		returned=st->receiveSamples(tmp_wave2,output_buffer_size);
#ifdef FLOAT_SAMPLES
                outputWave=SoundHolder::insertArray(outputWave,
                                                    tmp_wave2,currentPos,returned);
#else
                outputWave=SoundHolder::insertShortArray(outputWave,
                                                    tmp_wave2,currentPos,returned);
#endif

                currentPos+=returned;
	}while(returned!=0);
#ifdef FLOAT_SAMPLES
        importMultiplexedBuffer(outputWave,currentPos,ORIGINAL_WAVE);
#else
        importMultiplexedShortBuffer(outputWave,currentPos,ORIGINAL_WAVE);
#endif

        zaparr(tmp_wave2);
        zaparr(outputWave);
        if(getHasLADSPA()) initBuffer(AFTER_LADSPA,getFrames());
        if(getHasEnvelope()) initBuffer(AFTER_ENVELOPE,getFrames());
        getEffects()->setNbFrames(getFrames());
        cout<<"timestretch end\n";
        emit(reinitLADSPAValues());
        zapInstantEBuffer();
        if(getEffects()->getCurrent()) applyPlugin(getEffects()->getCurrent()); //reapply current plugin
        else if(getHasEnvelope()) applyEnvelope();
        else emit(waveChanged());


}




/*!
    \fn SoundManager::loadFile(QString filename)
 */
int SoundManager::loadFile()
{
        int result=SoundHolder::loadBuffer(fileManager->getOriginalFilename(),
                                           ORIGINAL_WAVE);
        zapInstantEBuffer();
        return result;
}












/*!
    \fn SoundManager::hasSound()
 */
bool SoundManager::hasSound()
{
    return _hasSound;
}


/*!
    \fn SoundManager::getMaxValue()
 */
double SoundManager::getMaxValue()
{
    return maxValue;
}

void SoundManager::crop(long start_sample, long end_sample){
    fileManager->deleteAfterCurrent();
    pushSoundBuffer(ORIGINAL_WAVE);
    assert(start_sample>=0 && end_sample<=getFrames());
    SoundHolder::cropBuffers(start_sample,end_sample);
    setNbFrames(end_sample-start_sample);
    fileManager->deleteAfterCurrent();
    this->zapInstantEBuffer();
    //emit(waveChanged());
    emit(cropped(start_sample,end_sample));
}


/*!
    \fn SoundManager::setFileManager(FileManager*)
 */
void SoundManager::setFileManager(FileManager* _fm)
{
    fileManager=_fm;
}


/*!
    \fn SoundManager::saveSoundBuffer()
 */
void SoundManager::pushSoundBuffer(bufferType type){
    QString temp=fileManager->getNextFilename();
    temp.prepend(QString::number(type)+"-");
    saveSoundBufferTo(temp,type);
}

/*!
    \fn SoundManager::saveSoundBuffer()
 */
void SoundManager::saveSoundBuffer(long start_sample,long end_sample){
    assert(start_sample>=0 && end_sample<=getFrames());
    QString temp=fileManager->getNextFilename();
    saveChunkTo(temp,start_sample,end_sample,start_sample);
}



/*!
    \fn SoundManager::saveSoundBuffer()
 */
void SoundManager::saveSoundBufferTo(QString filename,bufferType type)
{
    /** \todo check here */
    saveChunkToAs(filename,0,getFrames(),sfinfo.format,0,type);
}

void SoundManager::saveChunkTo(QString filename,long start,long end,long start_at)
{
    //convert to 32 bit fp output
    saveChunkToAs(filename,start,end,toBpsCode(32),start_at);
}


bool SoundManager::previousSoundBuffer(bufferType type){
    if(!fileManager->hasPrevious()) return FALSE;
    QString previous=fileManager->getPreviousFilename();
    std::cout<<"reverting to "<<previous<<"\n";
    SoundHolder::loadBuffer(previous, ORIGINAL_WAVE);
    zapInstantEBuffer();
    return TRUE;
}

bool SoundManager::previousSoundBuffer(long start_sample,long end_sample){
    assert(start_sample>=0 && end_sample<=getFrames());
    if(!fileManager->hasPrevious()) return FALSE;
    QString previous=fileManager->getPreviousFilename();
    std::cout<<"reverting to "<<previous<<"\n";
    loadBuffer(previous,start_sample,end_sample);
    return TRUE;
}


bool SoundManager::nextSoundBuffer(){
    if(!fileManager->hasNext())return FALSE;
    QString next=fileManager->getNextFilename();
    std::cout<<"redoing "<<next<<"\n";
    SoundHolder::loadBuffer(next,ORIGINAL_WAVE);
    zapInstantEBuffer();
     return TRUE;
}

int SoundManager::loadBuffer(QString path,long start_sample,long end_sample){
    assert(start_sample>=0 && end_sample<=getFrames());
    SNDFILE* tmp_sndfile= sf_open(path, SFM_READ, &sfinfo);
    if(tmp_sndfile==NULL) return 1;
    float* wavefile=new float[(end_sample-start_sample)*sfinfo.channels];
    if(start_sample!=0) sf_seek  (tmp_sndfile, (sf_count_t)start_sample, SEEK_SET);
    sf_readf_float(tmp_sndfile, wavefile,(sf_count_t)(end_sample-start_sample));
    int count=0;
    for (int i=start_sample;i<end_sample;i++){
        getLeftChannel(ORIGINAL_WAVE)[i]=wavefile[count];
        getRightChannel(ORIGINAL_WAVE)[i]=wavefile[count+1];
        count+=sfinfo.channels;
    }

    zaparr(wavefile);
    sf_close(tmp_sndfile);
    return 0;

}



/*!
    \fn SoundManager::saveChunksTo(QString,long*)
 */
QStringList* SoundManager::saveChunksTo(QString filename,long beats[],int length,int format)
{
    QDir dir;
    dir.mkdir(filename);
    QStringList* resultFilenames=new QStringList();
    std::sort(beats,beats+length);
    std::cout<<"size: "<<length<<"\n";
    for (int i=0;i<length-1;i++){
        QString tempfilename=QString::number(i).rightJustify(8,'0').append(".wav").prepend(filename+"/");
        saveChunkToAs(tempfilename,beats[i],beats[i+1],format,0);
        resultFilenames->push_back(tempfilename.section( '/', -1 ));
    }
    postProcessEffects->clear();
    return resultFilenames;
}

QStringList* SoundManager::saveChunksTo(QString filename,long beats[],int length)
{
    return saveChunksTo(filename,beats,length,sfinfo.format);
}


/*!
    \fn SoundManager::applyPlugin()
 */
void SoundManager::applyPlugin(LADSPAEffect* effect,long start_sample, long end_sample)
{
    assert(start_sample>=0 && end_sample<=getFrames());

    QProgressDialog progress("Applying pugin...","Abort",getFrames(),NULL,"progress",TRUE);
    progress.setMinimumDuration(1000);
    progress.setProgress(0);

    //audio ports

    float* inputBufferLeft=SoundHolder::getLeftChannel(BEFORE_LADSPA);
    float* inputBufferRight=SoundHolder::getRightChannel(BEFORE_LADSPA);

    float* outputBufferLeft=SoundHolder::getLeftChannel(AFTER_LADSPA);
    float* outputBufferRight=SoundHolder::getRightChannel(AFTER_LADSPA);

    //end audio ports

    effect->init(getRate(),inputBufferLeft,inputBufferRight,outputBufferLeft,outputBufferRight);
    effect->startPortValues(start_sample);
    cout<<"applying plugin from sample: "<<start_sample<<" to sample:"<<end_sample<<"\n";
    long processedSamples=0;
    long lastChunk=(end_sample-start_sample)%1024;
    for (int i=0;i<(end_sample-start_sample);i+=1024){
            if((end_sample-start_sample)-i>lastChunk){
                effect->process(1024);
                processedSamples+=1024;
                effect->advancePortValues(1024);
            }else{
                effect->process(lastChunk);
                processedSamples+=lastChunk;
                effect->advancePortValues(lastChunk);
            }
            if(i%(102400)==0){
                progress.setProgress(i);
                progress.setLabelText("Applying pugin...(frame: "+QString::number(i)+")");
            }
            if(progress.wasCanceled()){
                break;
            }
            assert(processedSamples<=getFrames());
    }
    progress.close();
    if(hasEnvelope){
        applyEnvelope();
    }else{
        //force spectrogram repaint
        if(hiresfreq_subband_energies) zaparr(hiresfreq_subband_energies);
        //this->zapInstantEBuffer();
        emit(waveChanged()); //is ..
    }
}


void SoundManager::applyPlugin(LADSPAEffect* effect){
    applyPlugin(effect,0,getFrames());
}




/*!
    \fn SoundManager::getEffects()
 */
LADSPAEffects* SoundManager::getEffects()
{
    return effects;
}








/*!
    \fn SoundManager::zapInstantEBuffer()
 */
void SoundManager::zapInstantEBuffer()
{
    cout<<"Zapping Instant Energy buffers\n";
    if(instant_buffer) zaparr(instant_buffer);
    if(hirestemp_subband_energies) zaparr(hirestemp_subband_energies);
    if(hiresfreq_subband_energies) zaparr(hiresfreq_subband_energies);
    foundBeats.resize(0);
}




/*!
    \fn SoundManager::compute_subbands_energies(int,int,long,long)
 */
float** SoundManager::compute_subband_energies(int subband_number,
                                                                                                    int frames,int advance_frames,int padding,
                                                                                                    long start_sample,long end_sample,
                                                                                                    int startfreq, int endfreq,
                                                                                                    float& fftMinima,float& fftMaxima,
                                                                                                    bufferType type)
{

    cout<<"Computing fft\n";
    assert(start_sample>=0 && end_sample<=getFrames());
    QProgressDialog progress("Computing FFT...","Abort",end_sample-start_sample,NULL,"progress",TRUE);
    progress.setMinimumDuration(2000);
    progress.setProgress(0);

    //convert start/end frequency to fft index
    float start_index=(startfreq*frames)/getRate();
    float end_index=(endfreq*frames)/getRate();
    //end convert start/end frequency to fft index


    fftMaxima=FLT_MIN;
    fftMinima=FLT_MAX;

    float* lchannel=0;
    float* rchannel=0;
    if(derivateSignal){
        lchannel=exportDerivatedLeftBuffer(start_sample,end_sample,type);
        rchannel=exportDerivatedRightBuffer(start_sample,end_sample,type);
    }else{
        lchannel=&getLeftChannel(type)[start_sample];
        rchannel=&getRightChannel(type)[start_sample];
    }



    int historyNb=(int)((float)getFrames()/(float)advance_frames)+1;
    //int subbandSize=(frames/2)/subband_number;
    int subbandSize=round((end_index-start_index)/subband_number);
    float** subband_energies=new float*[subband_number];
    for (int i=0;i<subband_number;i++){
            subband_energies[i]=new float[historyNb];
    }

    fftw_complex* in  = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*frames);
    fftw_complex* out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex)*frames);
    fftw_plan p = fftw_plan_dft_1d(frames, in, out, FFTW_FORWARD, FFTW_ESTIMATE);

    float* subband_buffer=new float[frames/2];
    int lastChunk=(end_sample-start_sample)%(frames);
    float* gaussian=0;
    if(windowMode==WINDOW_GAUSS) gaussian=new float[frames]; //create gaussian window holder
    for (int i=start_sample;i<end_sample-frames;i+=advance_frames){
            //fill in and out buffers
            if(windowMode==WINDOW_GAUSS){
                float std_dev=
                        MyMath::standard_deviation(&getLeftChannel(type)[i],advance_frames);
                MyMath::gaussian_window(gaussian,advance_frames,MyMath::linear_regression(0,1,2,8,std_dev));
            }
            for (int i2=i;i2<i+frames;i2++){
                float w=1;
                if(i2<i+advance_frames || padding==PAD_SIGNAL){
                    if(windowMode==WINDOW_HANNING){
                        w = (0.5 - 0.5*cos((2*M_PI * i2) / (advance_frames))); //Hanning
                    }else if(windowMode==WINDOW_GAUSS){
                        w=gaussian[i2-i];
                    }
                    in[i2-i][0]=lchannel[i2]*w;
                    in[i2-i][1]=rchannel[i2]*w;
                }else{
                    in[i2-i][0]=0;
                    in[i2-i][1]=0;
                }
            }
            //end fill in and out buffers
            fftw_execute(p); //compute fft


            //convert to polar coordinates (magnitude)
            for (int j=0;j<int(frames/2);j++){
                    subband_buffer[j]=sqrt((out[j][0]*out[j][0])+(out[j][1]*out[j][1]));
            }
            //end convert to polar coordinates (magnitude)
            //compute the energy of n subbands
            //cout<<"start_index: "<<start_index<<" end_index:"<<end_index<<" = "<<ceil(end_index-start_index)<<endl;
            int uplimit=round(end_index-start_index);
            uplimit=uplimit/subbandSize>subband_number?subband_number*subbandSize:uplimit;
            for (int i2=0;i2<uplimit-subbandSize;i2+=subbandSize){
                double subbandE=0;
                for (int i3=i2+start_index;i3<i2+start_index+subbandSize;i3++){
                    subbandE+=subband_buffer[i3];
                }

                float energy=subbandE*((double)(subband_number)/(double)frames);
                //float energy=subbandE/(subbandSize/2);

                //cout<<"i2/subbandSize: "<<i2/subbandSize<<endl;

                subband_energies[i2/subbandSize][i/advance_frames]=energy;



                //update minimas and maximas
                if(energy>fftMaxima)
                    fftMaxima=energy;
                if(energy<fftMinima)
                    fftMinima=energy;
                //end update minimas and maximas
            }
            //end compute the energy of n subbands
            if((i-start_sample)%(advance_frames*100)==0){
                progress.setProgress(i-start_sample);
                progress.setLabelText("Computing FFT...(frame: "+QString::number(i)+")");
            }
    }
    if(gaussian) zaparr(gaussian);
    zaparr(subband_buffer);
    fftw_destroy_plan(p);
    fftw_free(in);
    fftw_free(out);
    if(derivateSignal){
        zaparr(lchannel);
        zaparr(rchannel);
    }
    return subband_energies;
}


/*!
    \fn SoundManager::setAnalysisMethod(int)
 */
void SoundManager::setAnalysisMethod(int am)
{
    analysisMethod=am;
    zapInstantEBuffer();
}


/*!
    \fn SoundManager::setDerivateSignal(bool)
 */
void SoundManager::setDerivateSignal(bool _ds)
{
    derivateSignal=_ds;
}


/*!
    \fn SoundManager::keepUnique(QMemArray<long)
 */
QMemArray<long> SoundManager::keepUnique(QMemArray<long> array)
{
    QMemArray<long> temparray;
    temparray.resize(0);
    std::sort(array.begin(),array.end());
    long* end=std::unique(array.begin(),array.end());
    for (int i=0;&array[i]!=end;i++){
        temparray.resize(temparray.size()+1);
        temparray[temparray.size()-1]=array[i];
    }
    array.detach();
    delete array;
    return temparray;
}






/*!
    \fn SoundManager::loadEffects()
 */
void SoundManager::loadEffects()
{
    effects=new LADSPAEffects();
    connect(this,SIGNAL(nbFramesChanged(long)),effects,SLOT(setNbFrames(long)));
    QString ladspa_path=std::getenv("LADSPA_PATH");
    if (ladspa_path!=0)
        cout<<"LADSPA path from env: "<<ladspa_path<<"\n";
    else{
        cout<<"No LADSPA_PATH environment variable!, trying /usr/lib/ladspa\n";
        ladspa_path="/usr/lib/ladspa/";
    }
    if(QDir().exists(ladspa_path)){
        effects->loadEffects(ladspa_path);
    }
}


/*!
    \fn SoundManager::getSubbandEnergies()
 */
float** SoundManager::getHiresTempSubbandEnergies()
{
     if(!hirestemp_subband_energies)
        hirestemp_subband_energies=
            compute_subband_energies(hirestemp_nbbands,hirestemp_fft_width,hirestemp_fft_advance,
                                                                hirestemp_padding,0,frames,
                                                                hirestemp_startfreq,hirestemp_endfreq,
                                                                hirestemp_fftMinima,hirestemp_fftMaxima,
                                                                ORIGINAL_WAVE);
    return hirestemp_subband_energies;
}

float** SoundManager::getHiresFreqSubbandEnergies()
{
     if(!hiresfreq_subband_energies)
        hiresfreq_subband_energies=
            compute_subband_energies(hiresfreq_nbbands,hiresfreq_fft_width,hiresfreq_fft_advance,
                                                                     hiresfreq_padding,0,frames,
                                                                     hiresfreq_startfreq,hiresfreq_endfreq,
                                                                    hiresfreq_fftMinima,hiresfreq_fftMaxima,FOR_OUTPUT);
    return hiresfreq_subband_energies;
}








/*!
    \fn SoundManager::getInstantEnergyBuffersize()
 */
int SoundManager::getInstantEnergyBuffersize()
{
    return amp_domain_instant_energy_buffersize;
}







void SoundManager::recompute_subband_energies(){
    if(hirestemp_subband_energies) zaparr(hirestemp_subband_energies);
    hirestemp_subband_energies=
                        compute_subband_energies(hirestemp_nbbands,hirestemp_fft_width,
                                                                                hirestemp_fft_advance,hirestemp_padding,
                                                                                0,getFrames(),
                                                                                hirestemp_startfreq,hirestemp_endfreq,
                                                                                hirestemp_fftMinima,hirestemp_fftMaxima,
                                                                                ORIGINAL_WAVE);
    if(hiresfreq_subband_energies) zaparr(hiresfreq_subband_energies);
    hiresfreq_subband_energies=
                        compute_subband_energies(hiresfreq_nbbands,hiresfreq_fft_width,
                                                                              hiresfreq_fft_advance,hiresfreq_padding,
                                                                                0,getFrames(),
                                                                              hiresfreq_startfreq,hiresfreq_endfreq,
                                                                                hiresfreq_fftMinima,hiresfreq_fftMaxima,
                                                                                FOR_OUTPUT);
}








/*!
    \fn SoundManager::setFFTParams(int,int,int,int,int,in,int,int,bool)
 */
void SoundManager::setFFTParams(int hf_w,int hf_a,int hf_nb,int hf_p,int hf_sf,int hf_ef,
int ht_w,int ht_a,int ht_nb, int ht_p,int ht_sf,int ht_ef,
int w, bool ssm)
{


        hiresfreq_nbbands=hf_nb;
        hiresfreq_fft_width=hf_w;
        hiresfreq_fft_advance=hf_a;
        hiresfreq_padding=hf_p;
        hiresfreq_startfreq=hf_sf;
        hiresfreq_endfreq=hf_ef;

        hirestemp_nbbands=ht_nb;
        hirestemp_fft_width=ht_w;
        hirestemp_fft_advance=ht_a;
        hirestemp_padding=ht_p;
        hirestemp_startfreq=ht_sf;
        hirestemp_endfreq=ht_ef;

        windowMode=w;

        substractSpectralMean=ssm;

        //recompute_subband_energies();
        //this->zapInstantEBuffer();
        if(!getHasEnvelope() && !getHasLADSPA()) this->zapInstantEBuffer();
        emit(fftParamsChanged());
        emit(analysisParamsChanged());
}

void SoundManager::setAmpDomainParams(int instant_energy_buffersize,int average_energy_buffersize)
{
    this->amp_domain_instant_energy_buffersize=instant_energy_buffersize;
    this->amp_domain_average_energy_buffersize=average_energy_buffersize;
    if(instant_buffer) zaparr(instant_buffer);
    if(this->analysisMethod==SoundManager::AMP_DOMAIN)
        emit(analysisParamsChanged());
}

/*!
    \fn SoundManager::addPostProcessEffect(CustomEffect*);
 */
void SoundManager::addPostProcessEffect(CustomEffect* ppe)
{
    postProcessEffects->add(ppe);
}


/*!
    \fn SoundManager::getLeftChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert)
 */
void SoundManager::getLeftChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert)
{
    getBand(getLeftChannel(FOR_OUTPUT),data,start_sample,end_sample, start_band_freq, end_band_freq, invert);

}


/*!
    \fn SoundManager::getRightChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert)
 */
void SoundManager::getRightChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert)
{
    getBand(getRightChannel(FOR_OUTPUT),data,start_sample,end_sample, start_band_freq, end_band_freq, invert);
}


/*!
    \fn SoundManager::startBandChanged(int)
 */
void SoundManager::startBandChanged(int sb)
{
    startBand=sb;
}


/*!
    \fn SoundManager::endBandChanged(int)
 */
void SoundManager::endBandChanged(int eb)
{
    endBand=eb;
}


/*!
    \fn SoundManager::inversionBandChanged(bool tf)
 */
void SoundManager::inversionBandChanged(bool tf)
{
    inversionBand=tf;
}


/*!
    \fn SoundManager::getLeftChannelBand(float* data, long start_sample, long end_sample)
 */
void SoundManager::getLeftChannelBand(float* data, long start_sample, long end_sample)
{
    getLeftChannelBand(data,start_sample,end_sample, startBand, endBand, inversionBand);
}


/*!
    \fn SoundManager::getRightChannelBand(float* data, long start_sample, long end_sample)
 */
void SoundManager::getRightChannelBand(float* data, long start_sample, long end_sample)
{
    getRightChannelBand(data,start_sample,end_sample, startBand, endBand, inversionBand);
}


/*!
    \fn SoundManager::getBand(float* channel, float* data, long start_sample, long end_sample, int start_band_freq, int end_band_freq, bool inverted)
 */
void SoundManager::getBand(float* channel, float* data, long start_sample, long end_sample, int start_band_freq, int end_band_freq, bool inverted)
{
    long frames=end_sample-start_sample;

    double* in=new double[frames];
    fftw_complex* out= (fftw_complex *)fftw_malloc(sizeof(fftw_complex)*frames);
    fftw_plan p1 = fftw_plan_dft_r2c_1d(frames, in, out, FFTW_ESTIMATE);
    fftw_plan p2 = fftw_plan_dft_c2r_1d(frames, out, in, FFTW_ESTIMATE);

    for (int i=0;i<frames;i++){
        //float w = (0.5 - 0.5*cos((2*M_PI * i) / (frames))); //Hanning
        in[i]=channel[start_sample+i];//*w;
    }
    fftw_execute(p1); //to frequency domain
    for (int i=0;i<frames;i++){
        if(i<frames/2){
            if(i<start_band_freq || i>end_band_freq){
                out[i][0]=out[i][0]*0.01;
            }
        }else{
            if(i>frames-start_band_freq || i<frames-end_band_freq){
                out[i][0]=out[i][0]*0.01;
            }
        }
    }

    fftw_execute(p2); //to amplitude domain
    for (int i=0;i<frames;i++){
        data[i]=in[i]/frames; //normalize
    }
    fftw_destroy_plan(p1);
    fftw_destroy_plan(p2);
    fftw_free(out);
    zaparr(in);
}


/*!
    \fn SoundManager::setPlayingBandOnly(bool tf)
 */
void SoundManager::setPlayingBandOnly(bool tf)
{
    playingBandOnly=tf;
}


/*!
    \fn SoundManager::isPlayingBandOnly()
 */
bool SoundManager::isPlayingBandOnly()
{
    return playingBandOnly;
}


/*!
    \fn SoundManager::exportMultiplexedShortChannelBand(short* data, long start_sample, long end_sample)
 */
void SoundManager::exportMultiplexedShortChannelBand(short* buffer, long start_sample, long end_sample)
{

    float* leftChannelBand=new float[end_sample-start_sample];
    float* rightChannelBand=new float[end_sample-start_sample];

    getLeftChannelBand(leftChannelBand,start_sample,end_sample);
    getRightChannelBand(rightChannelBand,start_sample,end_sample);

    for (long i=0;i<end_sample-start_sample;i++){
       buffer[i]=(short)(leftChannelBand[i]*float(SHRT_MAX));
       buffer[i]=(short)(rightChannelBand[i]*float(SHRT_MAX));
   }
   zaparr(leftChannelBand);
   zaparr(rightChannelBand);
}


/*!
    \fn SoundManager::setSubstractSpectralMean(bool)
 */
void SoundManager::setSubstractSpectralMean(bool tf)
{
    substractSpectralMean=tf;
}


/*!
    \fn SoundManager::getSubstractSpectralMean()
 */
bool SoundManager::getSubstractSpectralMean()
{
    return substractSpectralMean;
}


/*!
    \fn SoundManager::getAverageenergyBuffersize()
 */
int SoundManager::getAverageEnergyBuffersize()
{
    return amp_domain_average_energy_buffersize;
}


/*!
    \fn SoundManager::findPreciseBeat(QMemArray<beat> input,int search_distance,int beatline_distance)
 */
QMemArray <SoundManager::beat> SoundManager::findPreciseBeat(QMemArray<beat> input,
                        int search_distance,int beatline_distance,float distance_to_min_conf,float lr_diff_conf)
{
    QMemArray<beat> tempbeats(0); // array of beats
    for (int i=0;i<input.size();i++){
        float distance_to_min=FLT_MAX;
        float left_right_diff=0;
        float left_energy=0;
        float right_energy=0;
        long pos=0;
        int start_pos=0;
        int end_pos=0;
        if(input[i].sample_pos<beatline_distance) {start_pos=beatline_distance*2;end_pos=0;}
        else if(input[i].sample_pos>getFrames()-beatline_distance){ start_pos=getFrames();end_pos=getFrames()-beatline_distance*2;}
        else{start_pos=input[i].sample_pos+beatline_distance;end_pos=input[i].sample_pos-beatline_distance;}
        float temp_abs_en_left=0;
        float temp_abs_en_right=0;
        float temp_en_left=0;
        float temp_en_right=0;
        for (int j=start_pos;j>end_pos;j--){
                if(j<search_distance){
                    pos=0;
                }else if(j+search_distance>getFrames()){
                    pos=getFrames();
                }else{
		    if(search_distance==0){
                        temp_abs_en_left=fabs(getLeftChannel(ORIGINAL_WAVE)[j]);
                        temp_en_left+=getLeftChannel(ORIGINAL_WAVE)[j];
                        temp_abs_en_right=fabs(getLeftChannel(ORIGINAL_WAVE)[j+1]);
                        temp_en_right+=getLeftChannel(ORIGINAL_WAVE)[j+1];
			left_energy=temp_abs_en_left;
                        right_energy=temp_abs_en_right;
		    }else{
                        if(j==start_pos){
                            //first run, compute all
                            for (int k=j-search_distance;k<j;k++){
                                temp_abs_en_left+=fabs(getLeftChannel(ORIGINAL_WAVE)[k]);
                                temp_en_left+=getLeftChannel(ORIGINAL_WAVE)[k];
                            }
                            for (int k=j;k<j+search_distance;k++){
                                temp_abs_en_right+=fabs(getLeftChannel(ORIGINAL_WAVE)[k]);
                                temp_en_right+=getLeftChannel(ORIGINAL_WAVE)[k];
                            }
                        }else{
                            //add/substaract differences
                            temp_abs_en_left=temp_abs_en_left-fabs(getLeftChannel(ORIGINAL_WAVE)[j])+
                                    fabs(getLeftChannel(ORIGINAL_WAVE)[j-search_distance]);
                            temp_en_left=temp_en_left-getLeftChannel(ORIGINAL_WAVE)[j]+
                                    getLeftChannel(ORIGINAL_WAVE)[j-search_distance];

                            temp_abs_en_right=
                                    temp_abs_en_right-fabs(getLeftChannel(ORIGINAL_WAVE)[j+search_distance])+
                                    fabs(getLeftChannel(ORIGINAL_WAVE)[j]);
                            temp_en_right=temp_en_right-getLeftChannel(ORIGINAL_WAVE)[j+search_distance]+
                                    getLeftChannel(ORIGINAL_WAVE)[j];
                        }
		    }
                    if(temp_abs_en_left<=distance_to_min_conf*distance_to_min){
                            if(((temp_en_right<0) == (temp_en_left <0)) || search_distance>32){
                                //same sign: left-right distance of abs values
                                if(fabs(temp_abs_en_right-temp_abs_en_left)>=lr_diff_conf*left_right_diff){
                                    distance_to_min=fabs(temp_abs_en_left);
                                    left_right_diff=fabs(temp_abs_en_right-temp_abs_en_left);
                                    pos=j;
                                    left_energy=temp_abs_en_left/search_distance;
                                    right_energy=temp_abs_en_right/search_distance;
                                }
                            }
                            else if(search_distance<=32){
                                //different sign: left-right distance of ponderated values
                                    if((temp_abs_en_right+temp_abs_en_left)/2>=lr_diff_conf*1.1*left_right_diff){
                                    distance_to_min=fabs(temp_abs_en_left);
                                    left_right_diff=(temp_abs_en_right+temp_abs_en_left)/2;
                                    pos=j;
                                    left_energy=temp_abs_en_left/search_distance;
                                    right_energy=temp_abs_en_right/search_distance;
                                }
                            }
                    }
                }
        }
        tempbeats.resize(tempbeats.size()+1);
        tempbeats[tempbeats.size()-1]=input[i];
        tempbeats[tempbeats.size()-1].sample_pos=pos;
        tempbeats[tempbeats.size()-1].energy_at_pos=fabs(getLeftChannel(ORIGINAL_WAVE)[pos]);
        tempbeats[tempbeats.size()-1].left_half_energy=left_energy;
        tempbeats[tempbeats.size()-1].right_half_energy=right_energy;
    }
    return tempbeats;
}


/*!
    \fn SoundManager::countNbZeroCrossing(float* data, int distance, int& zc_left, int &zc_right)
 */
void SoundManager::countNbZeroCrossing(float* data, int distance, int& zc_left, int &zc_right)
{
    zc_left=0;
    zc_right=0;
    for (int i=-distance;i<distance;i++){
        if(i<0 && ((data[i-1]<0)!=(data[i+1]<0))) zc_left++;
        else if(i>0 && ((data[i-1]<0)!=(data[i+1]<0))) zc_right++;
    }
}


/*!
    \fn SoundManager::removeBeatLineAt(long pos)
 */
void SoundManager::removeBeatLineAt(long pos)
{
    for (int i=0;i<foundBeats.size();i++){
        if(foundBeats[i].sample_pos==pos){
            foundBeats[i].active=FALSE;
            return;
        }
    }
}


/*!
    \fn SoundManager::updateMaxAmplitudes(QMemArray<beat> beatlines, int search_distance)
 */
void SoundManager::updateMaxAmplitudes(QMemArray<beat> beatlines, int search_distance)
{
    for (int i=0;i<beatlines.size();i++){
        //compute the max amplitude after the beatline (for the treshold slider)
        long pos=beatlines[i].sample_pos;
        float max_amplitude=0;
        int search_amplitude_end=pos+search_distance;
        if(search_amplitude_end>getFrames()) search_amplitude_end=getFrames();
        for (int j=pos;j<search_amplitude_end;j++){
            if (fabs(getLeftChannel(ORIGINAL_WAVE)[j])>max_amplitude){
                max_amplitude=fabs(getLeftChannel(ORIGINAL_WAVE)[j]);
            }
        }
        beatlines[i].max_amplitude=max_amplitude;
    }
}


/*!
    \fn SoundManager::removeRedundantBeatlines(QMemArray<beat> input, int treshold)
 */
QMemArray<SoundManager::beat> SoundManager::removeRedundantBeatlines(QMemArray<beat> input, int treshold)
{
    QMemArray <beat> output(0);
    for (int i=0;i<input.size();i++){
        if(output.size()==0 || input[i].sample_pos-output[output.size()-1].sample_pos>treshold){
            output.resize(output.size()+1);
            output[output.size()-1]=input[i];
        }
    }
    return output;
}


/*!
    \fn SoundManager::applyEnvelope()
 */
void SoundManager::applyEnvelope()
{
    if(hasEnvelope){
        float* inputBufferLeft=SoundHolder::getLeftChannel(BEFORE_ENVELOPE);
        float* inputBufferRight=SoundHolder::getRightChannel(BEFORE_ENVELOPE);
        float* outputBufferLeft=SoundHolder::getLeftChannel(AFTER_ENVELOPE);
        float* outputBufferRight=SoundHolder::getRightChannel(AFTER_ENVELOPE);
        if(!envelopeData || !envelopeArray || lockedBeatLines.count()==0 || !envelopeData->isEnabled()){
                std::memcpy(outputBufferLeft,inputBufferLeft,sizeof(long)*getFrames());
                std::memcpy(outputBufferRight,inputBufferRight,sizeof(long)*getFrames());
        }else{
            cout<<"Applying Envelope: "<<envelopeData->toString()<<endl;
            cout<<"lockedBeatLines.count(): "<<lockedBeatLines.count()<<endl;
            long applyNbFrames=(envelopeData->env_duration/1000.0)*float(getRate());
            long ahd_duration=
                    ((envelopeData->attack+
                    envelopeData->hold+
                    envelopeData->decay)*envelopeData->env_duration/1000.0)*float(getRate());
            long r_duration=(envelopeData->release*envelopeData->env_duration/1000.0)*float(getRate());
            long s_duration=applyNbFrames-r_duration-ahd_duration;
            for (long j=0;j<lockedBeatLines.count();j++){
                long end=j<lockedBeatLines.count()-1?lockedBeatLines[j+1]:getFrames();
                for (long i=lockedBeatLines[j];i<end;i++){
                    if(envelopeData->env_mode!=EnvelopeData::FIT_TO_SAMPLES &&
                    i-lockedBeatLines[j]<applyNbFrames){
                        outputBufferLeft[i]=inputBufferLeft[i]*envelopeArray[i-lockedBeatLines[j]];
                        outputBufferRight[i]=inputBufferRight[i]*envelopeArray[i-lockedBeatLines[j]];
                    }else if(envelopeData->env_mode==EnvelopeData::FIT_TO_SAMPLES){
                        if(i-lockedBeatLines[j]<=ahd_duration){
                            outputBufferLeft[i]=inputBufferLeft[i]*envelopeArray[i-lockedBeatLines[j]];
                            outputBufferRight[i]=inputBufferRight[i]*envelopeArray[i-lockedBeatLines[j]];
                        }else if(i-lockedBeatLines[j]>ahd_duration &&i<end-r_duration){
                            //apply sustain
                            outputBufferLeft[i]=inputBufferLeft[i]*envelopeData->sustain;
                            outputBufferRight[i]=inputBufferRight[i]*envelopeData->sustain;
                        }else{
                            //apply release
                            outputBufferLeft[i]=inputBufferLeft[i]*
                                    envelopeArray[applyNbFrames-(end-i)];
                            outputBufferRight[i]=inputBufferRight[i]*
                                    envelopeArray[applyNbFrames-(end-i)];
                        }
                    }else{
                        if(envelopeData->env_mode!=EnvelopeData::FIT_TO_DURATION_CONTINUE){
                            outputBufferLeft[i]=inputBufferLeft[i];
                            outputBufferRight[i]=inputBufferRight[i];
                        }else{ //EnvelopeData::FIT_TO_DURATION_CONTINUE
                            outputBufferLeft[i]=inputBufferLeft[i]*envelopeArray[applyNbFrames-1];
                            outputBufferRight[i]=inputBufferRight[i]*envelopeArray[applyNbFrames-1];
                        }
                    }
                }
            }
        }
    }
    cout<<"Envelope applied\n";
    //force spectrogram repaint
    if(hiresfreq_subband_energies) zaparr(hiresfreq_subband_energies);
    //this->zapInstantEBuffer();
    //emit(waveChanged(start_sample,end_sample));  //should be..
    emit(waveChanged()); //is ..
}


/*!
    \fn SoundManager::lockedBeatLinesChanged(QValueList<long> beatlines)
 */
void SoundManager::lockedBeatLinesChanged(QValueList<long> beatlines)
{
    lockedBeatLines=beatlines;
    applyEnvelope();
}


/*!
    \fn SoundManager::beatLinesChanged(QValueList<long> beatlines)
 */
void SoundManager::beatLinesChanged(QValueList<long> beatlines)
{
    beatLines=beatlines;
}


/*!
    \fn SoundManager::envelopeChanged(EnvelopeData* envData)
 */
void SoundManager::envelopeChanged(EnvelopeData* envData)
{
    //compute the envelope
    if(envData){
        envelopeData=envData;
        long applyNbFrames=(envelopeData->env_duration/1000.0)*float(getRate());
        if(envelopeArray) zaparr(envelopeArray);
        envelopeArray=new float[applyNbFrames];
        for (int i=0;i<applyNbFrames;i++){
            envelopeArray[i]=envelopeData->getEnvAt(float(i)/float(applyNbFrames),
                                            100.0/float(applyNbFrames));
        }
        applyEnvelope();
    }
}


/*!
    \fn SoundManager::getHasLADSPA()
 */
bool SoundManager::getHasLADSPA()
{
    return hasLADSPA;
}


/*!
    \fn SoundManager::getHasEnvelope()
 */
bool SoundManager::getHasEnvelope()
{
    return hasEnvelope;
}


/*!
    \fn SoundManager::getFileExtension()
 */
QString  SoundManager::getFileExtension()
{
    return fileManager->getFilenameExtension();
}


/*!
    \fn SoundManager::getTempDir()
 */
QString SoundManager::getTempDir()
{
    return fileManager->getTempDir();
}


/*!
    \fn SoundManager::canOpen(QString file)
 */
bool SoundManager::canOpen(QString file)
{
    bool openable=FALSE;
    SF_INFO tempsfinfo;
    SNDFILE* tmp_sndfile= sf_open(file, SFM_READ, &tempsfinfo);
    if(tmp_sndfile==0) openable=FALSE; else openable=TRUE;
    sf_close(tmp_sndfile);
    return openable;
}
