/***************************************************************************
 *   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.             *
 ***************************************************************************/
#ifndef SOUNDMANAGER_H
#define SOUNDMANAGER_H
#include "defs.h"

#ifdef HAS_SOUNDTOUCH
    #include <soundtouch/SoundTouch.h>
#endif
#include <qmemarray.h>
#include <qstring.h>
#include <limits.h>
#include <float.h>
#include <sndfile.h>
#include <list>
#include <qstringlist.h>
#include <qprogressdialog.h>
#include <qapplication.h>
#include <qptrlist.h>
#include <qvaluelist.h>
#include <qeventloop.h>
#include <ladspa.h>
#include <dlfcn.h>
#include <string.h>
#include <fftw3.h>
#include <math.h>
#include <algorithm>
#ifdef HAS_AUBIO
    #include <aubio/aubio.h>
#endif // HAS_AUBIO
#include "filemanager.h"
#include "ladspaeffect.h"
#include "ladspaeffects.h"
#include "mymath.h"
#include "soundholder.h"
#include "fftholder.h"
#include "envelopedata.h"
#include "beatlinedata.h"

using std::list;

/**
@author Predrag Viceic
*/
class SoundManager :public SoundHolder,public FFTHolder{
Q_OBJECT
public:
    SoundManager();

    ~SoundManager();
    struct beat{
        long sample_pos;
        int band;
        float instant_energy;
        float energy_at_pos;
        float max_amplitude;
        float min_amplitude;
        float left_half_energy;
        float right_half_energy;
        long min_pos;
        long max_pos;
        double computed_treshold;
        bool active;
    };

    enum BeatMatchingMethod {AMP_DOMAIN,FREQ_DOMAIN
    #ifdef HAS_AUBIO
        ,COMPLEX_DOMAIN,PHASE_DIFF,SPEC_DIFF,HFC,KL,MKL
    #endif // HAS_AUBIO
    };

    #ifdef HAS_SOUNDTOUCH
        void resample(list<long>,double);
        void pitchshift(long start_sample, long end_sample, double ratio);
    #endif
    QMemArray<long> analyse_time_domain(const double,const int,long,long);
    QMemArray<long> analyse_freq_domain(const double,const int,long,long);
    #ifdef HAS_AUBIO
               QMemArray<long> analyse_aubio_onsets(const double,const int,long,long);
    #endif // HAS_AUBIO
    QMemArray<long> analyse(const double,const int,long,long);
    QMemArray<long> analyse(const double,const int);
    QMemArray<long> beats;
    int loadFile();


    bool hasSound();
    double getMaxValue();
    void crop(long,long);
    long insertFileAt(QString filename,long start_sample);
    void setFileManager(FileManager*);
    void pushSoundBuffer(bufferType type);
    void saveSoundBuffer(long,long);
    void saveSoundBufferTo(QString,bufferType type);
    bool previousSoundBuffer(bufferType type);
    bool previousSoundBuffer(long,long);
    bool nextSoundBuffer();
    QStringList* saveChunksTo(QString filename,QValueList <beatline_data> bld,int format);
    QStringList* saveChunksTo(QString filename,QValueList <beatline_data> bld);
    void saveChunkTo(QString,long start_sample,long end_sample,long start_at=0);




    LADSPAEffects* getEffects();
    void zapInstantEBuffer();
    float** compute_subband_energies(int,int,int,int,long,long,int,int,bool,int,float&,float&,bufferType type);
    void setAnalysisMethod(int);
    void setDerivateSignal(bool);
    QMemArray<long> keepUnique(QMemArray<long>);
    void loadEffects();
    float** getHiresTempSubbandEnergies();
    float** getHiresFreqSubbandEnergies();

    int getInstantEnergyBuffersize();


    void setFFTParams( int hf_w,int hf_a,int hf_nb,int hf_p,int hf_sf,int hf_ef, bool hf_bark, int hf_bark_sub,
                            int ht_w,int ht_a,int ht_nb, int ht_p,int ht_sf,int ht_ef, bool ht_bark,int ht_bark_sub,
                            int w, bool ssm);
    void setAmpDomainParams(int,int);
    void addPostProcessEffect(CustomEffect*);
    void getLeftChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert);
    void getRightChannelBand(float* data,long start_sample, long end_sample,int start_band_freq, int end_band_freq, bool invert);
    void inversionBandChanged(bool tf);
    void getLeftChannelBand(float* data, long start_sample, long end_sample);
    void getRightChannelBand(float* data, long start_sample, long end_sample);
    void getBand(float* channel, float* data, long start_sample, long end_sample, int start_band_freq, int end_band_freq, bool inverted);
    void setPlayingBandOnly(bool tf);
    bool isPlayingBandOnly();
    void exportMultiplexedShortChannelBand(short* data, long start_sample, long end_sample);
    void setSubstractSpectralMean(bool);
    bool getSubstractSpectralMean();
    int getAverageEnergyBuffersize();
    QMemArray<beat> findPreciseBeat(QMemArray<beat> input,
                        int search_distance,int beatline_distance,
                        float distance_to_min_conf,float lr_diff_conf);
    void countNbZeroCrossing(float* data, int distance, int& zc_left, int &zc_right);
    void removeBeatLineAt(long pos);
    void updateMaxAmplitudes(QMemArray<beat> beatlines, int search_distance);
    QMemArray<beat> removeRedundantBeatlines(QMemArray<beat> input, int treshold);
    bool getHasLADSPA();
    bool getHasEnvelope();
    QString  getFileExtension();
    QString getTempDir();
    bool canOpen(QString file);

    

protected:
    FileManager* fileManager;
    LADSPAEffects* effects;
    int analysisMethod;
    bool derivateSignal;


    int amp_domain_instant_energy_buffersize;
    int amp_domain_average_energy_buffersize;


    int windowMode;
    CustomEffects* postProcessEffects;
    bool playingBandOnly;
    bool substractSpectralMean;
    QMemArray <beat> foundBeats;
    QValueList <beatline_data> beatlines;
    QValueList <beatline_data> lockedBeatlines;
    EnvelopeData* envelopeData;
    float* envelopeArray;
    bool loadingProject;


private:
    
    float* instant_buffer;
    int startBand;
    int endBand;
    bool inversionBand;
    QValueList <beatline_data> beatLines;
    QValueList <beatline_data> lockedBeatLines;
    int loadBuffer(QString,long,long);

public slots:
    void applyPlugin(LADSPAEffect*);
    void applyPlugin(LADSPAEffect* effect,long start_sample, long end_sample);
    void recompute_subband_energies();
    void startBandChanged(int);
    void endBandChanged(int);
    void applyEnvelope(bool forceRedraw=TRUE);
    void lockedBeatLinesChanged(QValueList<beatline_data> beatlines);
    void beatLinesChanged(QValueList<beatline_data> beatlines);
    void envelopeChanged(EnvelopeData* envData);
    void replaceFileAt(QString filename, long start, long end);

	void setLoadingProject(bool theValue);
	

	bool getLoadingProject() const;
	

signals:
    void waveChanged();
    void waveChanged(long,long);
    void newBeatLineAvailable(long);
    void analysisFinished();
    void cropped(long,long);
    void fftParamsChanged();
    void analysisParamsChanged();
    void reinitLADSPAValues();
    void replaced(long start, long end);
    void inserted(long,long);
};

#endif
