/*
  ZynAddSubFX - a software synthesizer
 
  Resonance.C - Resonance
  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) 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 <math.h>
#include <stdlib.h>
#include "Resonance.h"


#include <stdio.h>

Resonance::Resonance():Presets(){
    setpresettype("Presonance");
    defaults();
};

Resonance::~Resonance(){
};


void Resonance::defaults(){
    Penabled=0;
    PmaxdB=20;
    Pcenterfreq=64;//1 kHz
    Poctavesfreq=64;
    Pprotectthefundamental=0;
    ctlcenter=1.0;
    ctlbw=1.0;
    for (int i=0;i<N_RES_POINTS;i++) Prespoints[i]=64;
};

/*
 * Set a point of resonance function with a value
 */
void Resonance::setpoint(int n,unsigned char p){
    if ((n<0)||(n>=N_RES_POINTS)) return;
    Prespoints[n]=p;
};

/*
 * Apply the resonance to FFT data
 */
void Resonance::applyres(int n,FFTFREQS fftdata,REALTYPE freq){
    if (Penabled==0) return;//if the resonance is disabled
    REALTYPE sum=0.0,
	     l1=log(getfreqx(0.0)*ctlcenter),
	     l2=log(2.0)*getoctavesfreq()*ctlbw;

    for (int i=0;i<N_RES_POINTS;i++) if (sum<Prespoints[i]) sum=Prespoints[i];
    if (sum<1.0) sum=1.0;

    for (int i=1;i<n;i++){
        REALTYPE x=(log(freq*i)-l1)/l2;//compute where the n-th hamonics fits to the graph
	if (x<0.0) x=0.0;

	x*=N_RES_POINTS;
	REALTYPE dx=x-floor(x);x=floor(x);
	int kx1=(int)x; if (kx1>=N_RES_POINTS) kx1=N_RES_POINTS-1;
	int kx2=kx1+1;if (kx2>=N_RES_POINTS) kx2=N_RES_POINTS-1;
	REALTYPE y=(Prespoints[kx1]*(1.0-dx)+Prespoints[kx2]*dx)/127.0-sum/127.0;
	
	y=pow(10.0,y*PmaxdB/20.0);
	
	if ((Pprotectthefundamental!=0)&&(i==1)) y=1.0;
	
        fftdata.c[i]*=y;
        fftdata.s[i]*=y;
    };    
};

/*
 * Gets the response at the frequency "freq"
 */

REALTYPE Resonance::getfreqresponse(REALTYPE freq){
    REALTYPE l1=log(getfreqx(0.0)*ctlcenter),
	     l2=log(2.0)*getoctavesfreq()*ctlbw,sum=0.0;
	
    for (int i=0;i<N_RES_POINTS;i++) if (sum<Prespoints[i]) sum=Prespoints[i];
    if (sum<1.0) sum=1.0;

    REALTYPE x=(log(freq)-l1)/l2;//compute where the n-th hamonics fits to the graph
    if (x<0.0) x=0.0;
    x*=N_RES_POINTS;
    REALTYPE dx=x-floor(x);x=floor(x);
    int kx1=(int)x; if (kx1>=N_RES_POINTS) kx1=N_RES_POINTS-1;
    int kx2=kx1+1;if (kx2>=N_RES_POINTS) kx2=N_RES_POINTS-1;
    REALTYPE result=(Prespoints[kx1]*(1.0-dx)+Prespoints[kx2]*dx)/127.0-sum/127.0;
    result=pow(10.0,result*PmaxdB/20.0);
    return(result);
};


/*
 * Smooth the resonance function
 */
void Resonance::smooth(){
    REALTYPE old=Prespoints[0];
    for (int i=0;i<N_RES_POINTS;i++){
        old=old*0.4+Prespoints[i]*0.6;
	Prespoints[i]=(int) old;
    };
    old=Prespoints[N_RES_POINTS-1];
    for (int i=N_RES_POINTS-1;i>0;i--){
        old=old*0.4+Prespoints[i]*0.6;
	Prespoints[i]=(int) old+1;
	if (Prespoints[i]>127) Prespoints[i]=127;
    };
};

/*
 * Randomize the resonance function
 */
void Resonance::randomize(int type){
    int r=(int)(RND*127.0);
    for (int i=0;i<N_RES_POINTS;i++){
	Prespoints[i]=r;
	if ((RND<0.1)&&(type==0)) r=(int)(RND*127.0);
	if ((RND<0.3)&&(type==1)) r=(int)(RND*127.0);
	if (type==2) r=(int)(RND*127.0);
    };
    smooth();
};

/*
 * Interpolate the peaks
 */
void Resonance::interpolatepeaks(int type){
    int x1=0,y1=Prespoints[0];
    for (int i=1;i<N_RES_POINTS;i++){
	if ((Prespoints[i]!=64)||(i+1==N_RES_POINTS)){
	    int y2=Prespoints[i];
	    for (int k=0;k<i-x1;k++){
		float x=(float) k/(i-x1);
		if (type==0) x=(1-cos(x*PI))*0.5;
		Prespoints[x1+k]=(int)(y1*(1.0-x)+y2*x);
	    };
	    x1=i;
	    y1=y2;
	};
    };
};

/*
 * Get the frequency from x, where x is [0..1]; x is the x coordinate
 */
REALTYPE Resonance::getfreqx(REALTYPE x){
    if (x>1.0) x=1.0;
    REALTYPE octf=pow(2.0,getoctavesfreq());
    return(getcenterfreq()/sqrt(octf)*pow(octf,x));
};

/*
 * Get the x coordinate from frequency (used by the UI)
 */
REALTYPE Resonance::getfreqpos(REALTYPE freq){
    return((log(freq)-log(getfreqx(0.0)))/log(2.0)/getoctavesfreq());
};

/*
 * Get the center frequency of the resonance graph
 */
REALTYPE Resonance::getcenterfreq(){
    return(10000.0*pow(10,-(1.0-Pcenterfreq/127.0)*2.0));
};

/*
 * Get the number of octave that the resonance functions applies to
 */
REALTYPE Resonance::getoctavesfreq(){
    return(0.25+10.0*Poctavesfreq/127.0);
};

void Resonance::sendcontroller(MidiControllers ctl,REALTYPE par){
    if (ctl==C_resonance_center) ctlcenter=par;
	else ctlbw=par;
};




void Resonance::add2XML(XMLwrapper *xml){
    xml->addparbool("enabled",Penabled);
    
    if ((Penabled==0)&&(xml->minimal)) return;

    xml->addpar("max_db",PmaxdB);
    xml->addpar("center_freq",Pcenterfreq);
    xml->addpar("octaves_freq",Poctavesfreq);
    xml->addparbool("protect_fundamental_frequency",Pprotectthefundamental);
    xml->addpar("resonance_points",N_RES_POINTS);
    for (int i=0;i<N_RES_POINTS;i++){
	xml->beginbranch("RESPOINT",i);
	    xml->addpar("val",Prespoints[i]);
	xml->endbranch();
    };
};


void Resonance::getfromXML(XMLwrapper *xml){
    Penabled=xml->getparbool("enabled",Penabled);

    PmaxdB=xml->getpar127("max_db",PmaxdB);
    Pcenterfreq=xml->getpar127("center_freq",Pcenterfreq);
    Poctavesfreq=xml->getpar127("octaves_freq",Poctavesfreq);
    Pprotectthefundamental=xml->getparbool("protect_fundamental_frequency",Pprotectthefundamental);
    for (int i=0;i<N_RES_POINTS;i++){
	if (xml->enterbranch("RESPOINT",i)==0) continue;
	    Prespoints[i]=xml->getpar127("val",Prespoints[i]);
	  xml->exitbranch();
    };
};