/*
  ZynAddSubFX - a software synthesizer

  Resonance.cpp - 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 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 <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();
    }
}

