/***************************************************************************
 *   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 "soundholder.h"

SoundHolder::SoundHolder(QObject *parent, const char *name)
 : QObject(parent, name)
{
    leftRightBuffer orig;
    orig.left=0;
    orig.right=0;
    orig.init(0,ORIGINAL_WAVE);

    leftRightBuffer afterLadspa;
    afterLadspa.left=0;
    afterLadspa.right=0;
    afterLadspa.init(0,AFTER_LADSPA);

    leftRightBuffer afterEnvelope;
    afterEnvelope.left=0;
    afterEnvelope.right=0;
    afterEnvelope.init(0,AFTER_ENVELOPE);

    buffers.append(orig);
    buffers.append(afterLadspa);
    buffers.append(afterEnvelope);

    _hasSound=FALSE;
    frames=0;
    channels=0;
    rate=0;

    hasLADSPA=FALSE;
    hasEnvelope=FALSE;
}


SoundHolder::~SoundHolder()
{
    zapBuffers();
}

/*!
    \fn SoundManager::getRate()
 */
int SoundHolder::getRate()
{
    return rate;
}


/*!
    \fn SoundManager::getChannels()
 */
int SoundHolder::getChannels()
{
    return channels;
}


/*!
    \fn SoundManager::getFrames
 */
sf_count_t SoundHolder::getFrames()
{
    return frames;
}

/*!
    \fn SoundHolder::exportLeftFloatBuffer(long,long)
 */
float* SoundHolder::exportLeftBuffer(long start_sample,long end_sample,bufferType type)
{
   float* leftAudio=getLeftChannel(type);
   assert(start_sample>=0 && end_sample<=getFrames());
   float* buffer=new float[(end_sample-start_sample)];
   int count=0;
   for (long i=start_sample;i<end_sample;i++){
       buffer[count++]=leftAudio[i];
   }
   return buffer;
}

/*!
    \fn SoundHolder::exportLeftFloatBuffer(long,long)
 */
float* SoundHolder::exportRightBuffer(long start_sample,long end_sample,bufferType type)
{
   float* rightAudio=getRightChannel(type);
   assert(start_sample>=0 && end_sample<=getFrames());
   float* buffer=new float[(end_sample-start_sample)];
   int count=0;
   for (long i=start_sample;i<end_sample;i++){
       buffer[count++]=rightAudio[i];
   }
   return buffer;
}

/*!
    \fn SoundHolder::exportDerivatedLeftFloatBuffer(long,long)
 */
float* SoundHolder::exportDerivatedLeftBuffer(long start_sample,
                                              long end_sample,bufferType type)
{
   float* leftAudio=getLeftChannel(type);
   assert(start_sample>=0 && end_sample<=getFrames());
   float* buffer=new float[end_sample-start_sample];
   int count=0;
   for (long i=start_sample;i<end_sample-1;i++){
       if(i==0) buffer[count++]=leftAudio[i+1]-leftAudio[i];
       else if(i==getFrames()-1)
           buffer[count++]=leftAudio[i]-leftAudio[i-1];
       else buffer[count++]=leftAudio[i+1]-leftAudio[i-1];
   }
   return buffer;
}

/*!
    \fn SoundHolder::exportDerivatedRightFloatBuffer(long,long)
 */
float* SoundHolder::exportDerivatedRightBuffer(long start_sample,
                                               long end_sample,bufferType type)
{
    float* rightAudio=getRightChannel(type);
   assert(start_sample>=0 && end_sample<=getFrames());
   float* buffer=new float[end_sample-start_sample];
   int count=0;
   for (long i=start_sample;i<end_sample;i++){
       if(i==0) buffer[count++]=rightAudio[i+1]-rightAudio[i];
       else if(i==getFrames()-1)
           buffer[count++]=rightAudio[i]-rightAudio[i-1];
       else buffer[count++]=rightAudio[i+1]-rightAudio[i-1];
   }
   return buffer;
}


/*!
    \fn SoundHolder::importLeftFloatBuffer(float*)
 */
void SoundHolder::importLeftBuffer(float* buffer,long start_sample,
                                   long end_sample,bufferType type)
{
    float* leftAudio= getLeftChannel(type);
    assert(start_sample>=0 && end_sample<=getFrames());
    int count=0;
    for (int i=start_sample;i<end_sample;i++){
        leftAudio[i]=buffer[count++];
    }
}


/*!
    \fn SoundHolder::importRightFloatBuffer(float*)
 */
void SoundHolder::importRightBuffer(float* buffer,long start_sample,
                                     long end_sample,bufferType type)
{
    float* rightAudio= getRightChannel(type);
    assert(start_sample>=0 && end_sample<=getFrames());
    int count=0;
    for (int i=start_sample;i<end_sample;i++){
        rightAudio[i]=buffer[count++];
    }
}

/*!
    \fn SoundHolder::insertMultiplexedShortBuffer(float*,long,long)
 */
void SoundHolder::importMultiplexedBuffer(float* buffer,long length,bufferType type)
{
    int count=0;
    initBuffer(type,length);
    float* leftAudio=getLeftChannel(type);
    float* rightAudio=getRightChannel(type);
    for (int i=0;i<length;i++){
        leftAudio[i]=buffer[count++];
        rightAudio[i]=buffer[count++];
    }
    setNbFrames(length);
}

void SoundHolder::importMultiplexedShortBuffer(short* buffer,long length,bufferType type)
{
    int count=0;
    initBuffer(type,length);
    float* leftAudio=getLeftChannel(type);
    float* rightAudio=getRightChannel(type);
    for (int i=0;i<length;i++){
        leftAudio[i]=(float)(buffer[count++]/float(SHRT_MAX));
       rightAudio[i]=(float)(buffer[count++]/float(SHRT_MAX));
    }
    setNbFrames(length);
}

float* SoundHolder::exportMultiplexedBuffer(long start_sample,long end_sample,
                                            bufferType type)
{
    assert(start_sample>=0);
    assert(end_sample<=getFrames());
    assert(end_sample>start_sample);
   float* buffer=new float[(end_sample-start_sample)*getChannels()];
   int count=0;
   float* leftAudio=getLeftChannel(type);
    float* rightAudio=getRightChannel(type);
   for (long i=start_sample;i<end_sample;i++){
       buffer[count++]=leftAudio[i];
       buffer[count++]=rightAudio[i];
   }
   return buffer;
}


/*!
    \fn SoundHolder::exportMuliplexedShortBuffer(long,long)
 */
void SoundHolder::exportMultiplexedBuffer(float* buffer, long start_sample,
                long end_sample,bufferType type)
{
   float* leftAudio=getLeftChannel(type);
    float* rightAudio=getRightChannel(type);
   int count=0;
   for (long i=start_sample;i<end_sample;i++){
       buffer[count++]=(leftAudio[i]);
       buffer[count++]=(rightAudio[i]);
   }
}

void SoundHolder::exportMultiplexedShortBuffer(short* buffer,
                                               long start_sample,long end_sample,bufferType type)
{
   float* leftAudio=getLeftChannel(type);
    float* rightAudio=getRightChannel(type);
   assert(start_sample>=0 && end_sample<=getFrames());
   int count=0;
   for (long i=start_sample;i<end_sample;i++){
       buffer[count++]=(short)(leftAudio[i]*float(SHRT_MAX));
       buffer[count++]=(short)(rightAudio[i]*float(SHRT_MAX));
   }
}

/*!
    \fn SoundHolder::getLeftChannel()
 */
float* SoundHolder::getLeftChannel(bufferType type)
{
    return buffers[getBufferIndex(type)].left;
}


/*!
    \fn SoundHolder::getRightChannel()
 */
float* SoundHolder::getRightChannel(bufferType type)
{
    return buffers[getBufferIndex(type)].right;
}

/*!
    \fn SoundHolder::setNbFrames(long)
 */
void SoundHolder::setNbFrames(long nbf)
{
    frames=nbf;
    //if(effects) effects->setNbFrames(getFrames());
    emit(nbFramesChanged(getFrames()));
}





/*!
    \fn SoundHolder::getBps()
 */
int SoundHolder::getBps()
{
     if((sfinfo.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_16) return 16;
    else if((sfinfo.format & SF_FORMAT_SUBMASK) == SF_FORMAT_PCM_24) return 24;
    else if((sfinfo.format & SF_FORMAT_SUBMASK) == SF_FORMAT_FLOAT) return 32;
    else return 16;
}


/*!
    \fn SoundHolder::toBpsCode(int)
 */
int SoundHolder::toBpsCode(int value)
{
    switch(value){
            case 16: return (sfinfo.format & SF_FORMAT_TYPEMASK) |
                            (sfinfo.format & SF_FORMAT_ENDMASK) |SF_FORMAT_PCM_16;break;
            case 24: return (sfinfo.format & SF_FORMAT_TYPEMASK) |
                            (sfinfo.format & SF_FORMAT_ENDMASK) |SF_FORMAT_PCM_24;break;
            case 32: return (sfinfo.format & SF_FORMAT_TYPEMASK) |
                            (sfinfo.format & SF_FORMAT_ENDMASK) |SF_FORMAT_FLOAT;break;
            default: return sfinfo.format;
    }
}


/*!
    \fn SoundHolder::cropBuffers(long start_sample, long end_sample);
 */
void SoundHolder::cropBuffers(long start_sample, long end_sample)
{
    cout<<"Started cropping buffers\n";
    QValueList<SoundHolder::leftRightBuffer>::iterator it;
    for ( it = buffers.begin(); it != buffers.end(); ++it ){
        int count=0;
        if((*it).left &&(*it).right){
            float* croppedLeftChannel=new float[end_sample-start_sample];
            float* croppedRightChannel=new float[end_sample-start_sample];
            for (int i=start_sample;i<end_sample;i++){
                croppedLeftChannel[count]=(*it).left[i];
                croppedRightChannel[count]=(*it).right[i];
                count++;
            }
            (*it).zap();
            (*it).setLeft(croppedLeftChannel);
            (*it).setRight(croppedRightChannel);
        }
    }
    cout<<"Buffers cropped\n";
}


/*!
    \fn SoundHolder::zapBuffers()
 */
void SoundHolder::zapBuffers()
{
    QValueList<SoundHolder::leftRightBuffer>::iterator it;
    for ( it = buffers.begin(); it != buffers.end(); ++it ) (*it).zap();
}


/*!
    \fn SoundHolder::zap(bufferType type)
 */
void SoundHolder::zap(bufferType type)
{
    buffers[getBufferIndex(type)].zap();
}


/*!
    \fn SoundHolder::initBuffer(buferType type, long length)
 */
void SoundHolder::initBuffer(bufferType type, long length)
{
    cout<<"+++++++init buffer type: "<<type<<" no: "<<getBufferIndex(type)<<" length: "<<length<<"\n";
    buffers[getBufferIndex(type)].init(length,ANY);
}


/*!
    \fn SoundHolder::getBufferIndex(bufferType type)
 */
int SoundHolder::getBufferIndex(bufferType type)
{
    //if(type==FOR_OUTPUT) cout<<"type asked: "<<type<<" hasLadspa: "<<hasLADSPA<<" hasEnvelope "<<hasEnvelope<<"\n";
    switch(type){
        case ORIGINAL_WAVE:
        case BEFORE_LADSPA:
        {
            return 0;
        }
        case AFTER_LADSPA:
        case BEFORE_ENVELOPE:{
            if(hasLADSPA)
                return 1;
            else
                return 0;
            break;
        }
        case AFTER_ENVELOPE:
        case FOR_OUTPUT:{
            if(hasEnvelope && hasLADSPA)
                return 2;
            else if((hasEnvelope && !hasLADSPA) ||(!hasEnvelope && hasLADSPA)) //de morgan anyone?
                return 1;
            else return 0;
        }
        default: return 0;
    }
}


/*!
    \fn SoundHolder::setHasLadspa(bool hl)
 */
void SoundHolder::setHasLadspa(bool hl)
{
    if(!hl && hasLADSPA){
        zap(AFTER_LADSPA);
        hasLADSPA=hl;
        if(hasEnvelope) initBuffer(AFTER_ENVELOPE,getFrames());
    }else if(hl && !hasLADSPA){
        hasLADSPA=hl;
        initBuffer(AFTER_LADSPA,getFrames());
        if(hasEnvelope) initBuffer(AFTER_ENVELOPE,getFrames());
    }

}

void SoundHolder::saveChunkToAs(QString filename,long start,long end,
                                int format,long start_at,bufferType type)
{
    std::cout<<"start writing "<<end-start<<" samples to: "<<filename<<" from: "<<start<<" to "<<end<<"\n";
    SF_INFO tempsfinfo=sfinfo;
    if(format!=-1) tempsfinfo.format=format;
    SNDFILE* tmp_sndfile = sf_open (filename, SFM_RDWR, &tempsfinfo);
    cout<<"sfseek: "<<sf_seek  (tmp_sndfile, (sf_count_t)start_at, SEEK_SET)<<"\n";
    float* tmp_wave=exportMultiplexedBuffer(start,end,type);
    sf_writef_float  (tmp_sndfile, tmp_wave,end-start);
    sf_close(tmp_sndfile);
    zaparr(tmp_wave);
}

int SoundHolder::loadBuffer(QString path,bufferType type){
    SNDFILE* tmp_sndfile= sf_open(path, SFM_READ, &sfinfo);
    if (!tmp_sndfile) return 1;
    sf_command(tmp_sndfile,SFC_CALC_NORM_SIGNAL_MAX,&maxValue,sizeof(double));


    //std::cout<<maxValue;
    channels=sfinfo.channels;
    rate=sfinfo.samplerate;
    setNbFrames(sfinfo.frames);

    QProgressDialog progress("Loading File...",NULL,sfinfo.frames,NULL,"progress",TRUE);
    progress.setMinimumDuration(2000);


    zapBuffers();
    initBuffer(ORIGINAL_WAVE,sfinfo.frames);

    float* wavefile=new float[sfinfo.frames*2/*sfinfo.channels*/];
    sf_readf_float(tmp_sndfile, wavefile,(sf_count_t)sfinfo.frames);
    //demux channels
    int count=0;
    float* leftAudio= getLeftChannel(type);
    float* rightAudio= getRightChannel(type);
    for (int i=0;i<sfinfo.frames;i++){
        leftAudio[i]=wavefile[count];
        if(channels==2)
            rightAudio[i]=wavefile[count+1];
        else if(channels==1){
            rightAudio[i]=wavefile[count];
        }
        count+=sfinfo.channels;
        if(i%10000==0){
            progress.setProgress(i);
            progress.setLabelText("Loading File...(frame: "+QString::number(i)+")");
        }
    }
    //end demux channels
    progress.close();

    zaparr(wavefile);
    sf_close(tmp_sndfile);

    _hasSound=TRUE;
    if (channels==1) channels=2;
    sfinfo.channels=2;
    return 0;
}

/*!
    \fn SoundHolder::insertShortArray(short*,short*,long,long)
 */
short* SoundHolder::insertShortArray(short* dest,short* src,
                                            long position,long size)
{
    for (int i=position*channels;i<(position+size)*channels;i++){
        dest[i]=src[i-(position*channels)];
    }
    return dest;
}

/*!
    \fn SoundHolder::insertArray(float*,float*,long,long)
 */
float* SoundHolder::insertArray(float* dest,float* src,long position,long size)
{
    for (int i=position*channels;i<(position+size)*channels;i++){
        dest[i]=src[i-(position*channels)];
    }
    return dest;
}





/*!
    \fn SoundHolder::setHasEnvelope(bool tf)
 */
void SoundHolder::setHasEnvelope(bool tf)
{
    if(tf && !hasEnvelope){
        hasEnvelope=tf;
        initBuffer(AFTER_ENVELOPE,getFrames());
    }else if(!tf && hasEnvelope){
        zap(AFTER_ENVELOPE);
        hasEnvelope=tf;
    }
}


/*!
    \fn SoundHolder::leftRightBuffer::setRight(float*)
 */
void SoundHolder::leftRightBuffer::setRight(float* r)
{
    right=r;
}


/*!
    \fn SoundHolder::leftRightBuffer::setLeft(float*)
 */
void SoundHolder::leftRightBuffer::setLeft(float* l)
{
    left=l;
}


/*!
    \fn SoundHolder::getChunk(long start, long end, bufferType type)
 */
QByteArray SoundHolder::getChunkAsAudioFile(long start, long end, bufferType type)
{

    SF_INFO tempsfinfo=sfinfo;
    SNDFILE* tmp_sndfile =
            sf_open ("/tmp/freecycle_temp", SFM_RDWR, &tempsfinfo);
    float* tmp_wave=exportMultiplexedBuffer(start,end,type);
    sf_writef_float  (tmp_sndfile, tmp_wave,end-start);
    sf_close(tmp_sndfile);
    zaparr(tmp_wave);

    QByteArray  output;
    QFile file( "/tmp/freecycle_temp" );
    if (file.open(IO_ReadOnly))
    {
        output = file.readAll();
        file.close();
   }
   file.remove();
    return output;
}


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


/*!
    \fn SoundHolder::insertFileAt(QString filename,long start_sample)
 */
long SoundHolder::insertFileAt(QString filename,long start_sample)
{
    
    
    SF_INFO tempsfinfo;
    SNDFILE* tmp_sndfile= sf_open(filename, SFM_READ, &tempsfinfo);
    if (!tmp_sndfile) return 0;

    float* wavefile=new float[tempsfinfo.frames*2/*sfinfo.channels*/];
    sf_readf_float(tmp_sndfile, wavefile,(sf_count_t)tempsfinfo.frames);

    enlargeBuffersAt(start_sample,tempsfinfo.frames);

    //demux channels
    int count=0;
    float* leftAudio= getLeftChannel(ORIGINAL_WAVE);
    float* rightAudio= getRightChannel(ORIGINAL_WAVE);
    for (int i=start_sample;i<start_sample+sfinfo.frames;i++){
          leftAudio[i]=wavefile[count];
        if(channels==2){
            rightAudio[i]=wavefile[count+1];
        }
    }
    //end demux channels

    sf_close(tmp_sndfile);
    setNbFrames(getFrames()+tempsfinfo.frames);
    return tempsfinfo.frames;
}


/*!
    \fn SoundHolder::replaceFileAt(QString filename,long start_sample,long end_sample)
 */
void SoundHolder::replaceFileAt(QString filename,long start_sample,long end_sample)
{
   
    SF_INFO tempsfinfo;
    SNDFILE* tmp_sndfile= sf_open(filename, SFM_READ, &tempsfinfo);
    if (!tmp_sndfile){
        cout<<"Error opening "<<filename<<"\n";
        return;
    }

    float* wavefile=new float[tempsfinfo.frames*2/*sfinfo.channels*/];
    sf_readf_float(tmp_sndfile, wavefile,(sf_count_t)tempsfinfo.frames);

    //demux channels
    int count=0;
     float* leftAudio=getLeftChannel(ORIGINAL_WAVE);
    float* rightAudio=getRightChannel(ORIGINAL_WAVE);
    for (int i=start_sample;i<end_sample;i++){
        if(i<start_sample+tempsfinfo.frames){
            leftAudio[i]=wavefile[count++];
            if(channels==2){
                rightAudio[i]=wavefile[count++];
            }
        }else{
            leftAudio[i]=0;
            if(channels==2){
                rightAudio[i]=0;
            }
        }
    }
    //end demux channels

    sf_close(tmp_sndfile);
    zaparr(wavefile);
    cout<<"File inserted from "<<start_sample<<" to "<<end_sample<<"\n";
}


/*!
    \fn SoundHolder::enlargeBuffersAt(long start_sample,long nb_frames)
 */
void SoundHolder::enlargeBuffersAt(long /*start_sample*/,long /*nb_frames*/)
{

}


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