/*
  ZynAddSubFX - a software synthesizer

  EffectMgr.cpp - Effect manager, an interface betwen the program and effects
  Copyright (C) 2002-2005 Nasca Octavian Paul
  Author: Nasca Octavian Paul

  This program is free software; you can redistribute it and/or modify
  it under the terms of version 2 of the GNU General Public License
  as published by the Free Software Foundation.

  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 (version 2 or later) for more details.

  You should have received a copy of the GNU General Public License (version 2)
  along with this program; if not, write to the Free Software Foundation,
  Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA

*/

#include "EffectMgr.h"
#include <iostream>
using namespace std;

EffectMgr::EffectMgr(int insertion_, pthread_mutex_t *mutex_)
    :insertion(insertion_),
      efxoutl(new REALTYPE[SOUND_BUFFER_SIZE]),
      efxoutr(new REALTYPE[SOUND_BUFFER_SIZE]),
      filterpars(NULL), nefx(0), efx(NULL), mutex(mutex_), dryonly(false)
{
    setpresettype("Peffect"); /**\todo Figure out what this is doing
                               * , as it might be another leaky abstraction.*/
//    efx=NULL;
//    nefx=0;
//    insertion=insertion_;
//    mutex=mutex_;
//    efxoutl=new REALTYPE[SOUND_BUFFER_SIZE];
//    efxoutr=new REALTYPE[SOUND_BUFFER_SIZE];
    for(int i = 0; i < SOUND_BUFFER_SIZE; i++) {
        efxoutl[i] = 0.0;
        efxoutr[i] = 0.0;
    }
//    filterpars=NULL;
//    dryonly=false;
    defaults();
}


EffectMgr::~EffectMgr()
{
    if(efx != NULL)
        delete efx;
    delete [] efxoutl;
    delete [] efxoutr;
}

void EffectMgr::defaults()
{
    changeeffect(0);
    setdryonly(false);
}

/*
 * Change the effect
 */
void EffectMgr::changeeffect(int nefx_)
{
    cleanup();
    if(nefx == nefx_)
        return;
    nefx = nefx_;
    for(int i = 0; i < SOUND_BUFFER_SIZE; i++) {
        efxoutl[i] = 0.0;
        efxoutr[i] = 0.0;
    }

    if(efx != NULL)
        delete efx;
    switch(nefx) {  /**\todo replace leaky abstraction*/
    case 1:
        efx = new Reverb(insertion, efxoutl, efxoutr);
        break;
    case 2:
        efx = new Echo(insertion, efxoutl, efxoutr);
        break;
    case 3:
        efx = new Chorus(insertion, efxoutl, efxoutr);
        break;
    case 4:
        efx = new Phaser(insertion, efxoutl, efxoutr);
        break;
    case 5:
        efx = new Alienwah(insertion, efxoutl, efxoutr);
        break;
    case 6:
        efx = new Distorsion(insertion, efxoutl, efxoutr);
        break;
    case 7:
        efx = new EQ(insertion, efxoutl, efxoutr);
        break;
    case 8:
        efx = new DynamicFilter(insertion, efxoutl, efxoutr);
        break;
    //put more effect here
    default:
        efx = NULL;
        break; //no effect (thru)
    }

    if(efx != NULL)
        filterpars = efx->filterpars;
}

/*
 * Obtain the effect number
 */
int EffectMgr::geteffect()
{
    return nefx;
}

/*
 * Cleanup the current effect
 */
void EffectMgr::cleanup()
{
    if(efx != NULL)
        efx->cleanup();
}


/*
 * Get the preset of the current effect
 */

unsigned char EffectMgr::getpreset()
{
    if(efx != NULL)
        return efx->Ppreset;
    else
        return 0;
}

/*
 * Change the preset of the current effect
 */
void EffectMgr::changepreset_nolock(unsigned char npreset)
{
    if(efx != NULL)
        efx->setpreset(npreset);
}

/*
 * Change the preset of the current effect(with thread locking)
 */
void EffectMgr::changepreset(unsigned char npreset)
{
    pthread_mutex_lock(mutex);
    changepreset_nolock(npreset);
    pthread_mutex_unlock(mutex);
}


/*
 * Change a parameter of the current effect
 */
void EffectMgr::seteffectpar_nolock(int npar, unsigned char value)
{
    if(efx == NULL)
        return;
    efx->changepar(npar, value);
}

/*
 * Change a parameter of the current effect (with thread locking)
 */
void EffectMgr::seteffectpar(int npar, unsigned char value)
{
    pthread_mutex_lock(mutex);
    seteffectpar_nolock(npar, value);
    pthread_mutex_unlock(mutex);
}

/*
 * Get a parameter of the current effect
 */
unsigned char EffectMgr::geteffectpar(int npar)
{
    if(efx == NULL)
        return 0;
    return efx->getpar(npar);
}


/*
 * Apply the effect
 */
void EffectMgr::out(REALTYPE *smpsl, REALTYPE *smpsr)
{
    int i;
    if(efx == NULL) {
        if(insertion == 0)
            for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
                smpsl[i]   = 0.0;
                smpsr[i]   = 0.0;
                efxoutl[i] = 0.0;
                efxoutr[i] = 0.0;
            }
        ;
        return;
    }
    for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
        smpsl[i]  += denormalkillbuf[i];
        smpsr[i]  += denormalkillbuf[i];
        efxoutl[i] = 0.0;
        efxoutr[i] = 0.0;
    }
    efx->out(smpsl, smpsr);

    REALTYPE volume = efx->volume;

    if(nefx == 7) { //this is need only for the EQ effect
        /**\todo figure out why*/
        for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
            smpsl[i] = efxoutl[i];
            smpsr[i] = efxoutr[i];
        }
        return;
    }

    //Insertion effect
    if(insertion != 0) {
        REALTYPE v1, v2;
        if(volume < 0.5) {
            v1 = 1.0;
            v2 = volume * 2.0;
        }
        else {
            v1 = (1.0 - volume) * 2.0;
            v2 = 1.0;
        }
        if((nefx == 1) || (nefx == 2))
            v2 *= v2;                    //for Reverb and Echo, the wet function is not liniar

        if(dryonly) { //this is used for instrument effect only
            for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
                smpsl[i]   *= v1;
                smpsr[i]   *= v1;
                efxoutl[i] *= v2;
                efxoutr[i] *= v2;
            }
        }
        else {  //normal instrument/insertion effect
            for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
                smpsl[i] = smpsl[i] * v1 + efxoutl[i] * v2;
                smpsr[i] = smpsr[i] * v1 + efxoutr[i] * v2;
            }
        }
    }
    else {  //System effect
        for(i = 0; i < SOUND_BUFFER_SIZE; i++) {
            efxoutl[i] *= 2.0 * volume;
            efxoutr[i] *= 2.0 * volume;
            smpsl[i]    = efxoutl[i];
            smpsr[i]    = efxoutr[i];
        }
    }
}

/*
 * Get the effect volume for the system effect
 */
REALTYPE EffectMgr::sysefxgetvolume()
{
    if(efx == NULL)
        return 1.0;
    else
        return efx->outvolume;
}


/*
 * Get the EQ response
 */
REALTYPE EffectMgr::getEQfreqresponse(REALTYPE freq)
{
    if(nefx == 7)
        return efx->getfreqresponse(freq);
    else
        return 0.0;
}


void EffectMgr::setdryonly(bool value)
{
    dryonly = value;
}

void EffectMgr::add2XML(XMLwrapper *xml)
{
    xml->addpar("type", geteffect());

    if((efx == NULL) || (geteffect() == 0))
        return;
    xml->addpar("preset", efx->Ppreset);

    xml->beginbranch("EFFECT_PARAMETERS");
    for(int n = 0; n < 128; n++) {
        /**\todo evaluate who should oversee saving
             * and loading of parameters*/
        int par = geteffectpar(n);
        if(par == 0)
            continue;
        xml->beginbranch("par_no", n);
        xml->addpar("par", par);
        xml->endbranch();
    }
    if(filterpars != NULL) {
        xml->beginbranch("FILTER");
        filterpars->add2XML(xml);
        xml->endbranch();
    }
    xml->endbranch();
}

void EffectMgr::getfromXML(XMLwrapper *xml)
{
    changeeffect(xml->getpar127("type", geteffect()));

    if((efx == NULL) || (geteffect() == 0))
        return;

    efx->Ppreset = xml->getpar127("preset", efx->Ppreset);

    if(xml->enterbranch("EFFECT_PARAMETERS")) {
        for(int n = 0; n < 128; n++) {
            seteffectpar_nolock(n, 0); //erase effect parameter
            if(xml->enterbranch("par_no", n) == 0)
                continue;

            int par = geteffectpar(n);
            seteffectpar_nolock(n, xml->getpar127("par", par));
            xml->exitbranch();
        }
        if(filterpars != NULL) {
            if(xml->enterbranch("FILTER")) {
                filterpars->getfromXML(xml);
                xml->exitbranch();
            }
        }
        xml->exitbranch();
    }
    cleanup();
}

