/*
  ZynAddSubFX - a software synthesizer

  ADnote.h - The "additive" synthesizer
  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 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 (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

*/

#ifndef AD_NOTE_H
#define AD_NOTE_H

#include "SynthNote.h"
#include "Envelope.h"
#include "LFO.h"
#include "../Params/ADnoteParameters.h"
#include "../Params/Controller.h"

//Globals

/**FM amplitude tune*/
#define FM_AMP_MULTIPLIER 14.71280603f

#define OSCIL_SMP_EXTRA_SAMPLES 5

/**The "additive" synthesizer*/
class ADnote:public SynthNote
{
    public:
        /**Constructor.
         * @param pars Note Parameters
         * @param ctl_ Pointer to system Controller
         * @param freq Base frequency for note
         * @param velocity Velocity of note
         * @param portamento_ 1 if the note has portamento
         * @param midinote_ The midi number of the note
         * @param besilent Start silent note if true*/
        ADnote(ADnoteParameters *pars, Controller *ctl_, float freq,
               float velocity, int portamento_, int midinote_,
               bool besilent);
        /**Destructor*/
        ~ADnote();

        /**Alters the playing note for legato effect*/
        void legatonote(float freq, float velocity, int portamento_,
                        int midinote_, bool externcall);

        int noteout(float *outl, float *outr);
        void relasekey();
        int finished() const;
    private:

        /**Changes the frequency of an oscillator.
         * @param nvoice voice to run computations on
         * @param in_freq new frequency*/
        void setfreq(int nvoice, float in_freq);
        /**Set the frequency of the modulator oscillator*/
        void setfreqFM(int nvoice, float in_freq);
        /**Computes relative frequency for unison and unison's vibratto.
         * Note: Must be called before setfreq* functions.*/
        void compute_unison_freq_rap(int nvoice);
        /**Compute parameters for next tick*/
        void computecurrentparameters();
        /**Initializes All Parameters*/
        void initparameters();
        /**Deallocate/Cleanup given voice*/
        void KillVoice(int nvoice);
        /**Deallocate Note resources and voice resources*/
        void KillNote();
        /**Get the Voice's base frequency*/
        inline float getvoicebasefreq(int nvoice) const;
        /**Get modulator's base frequency*/
        inline float getFMvoicebasefreq(int nvoice) const;
        /**Compute the Oscillator's samples.
         * Affects tmpwave_unison and updates oscposhi/oscposlo*/
        inline void ComputeVoiceOscillator_LinearInterpolation(int nvoice);
        /**Compute the Oscillator's samples.
         * Affects tmpwave_unison and updates oscposhi/oscposlo
         * @todo remove this declaration if it is commented out*/
        inline void ComputeVoiceOscillator_CubicInterpolation(int nvoice);
        /**Computes the Oscillator samples with morphing.
         * updates tmpwave_unison*/
        inline void ComputeVoiceOscillatorMorph(int nvoice);
        /**Computes the Ring Modulated Oscillator.*/
        inline void ComputeVoiceOscillatorRingModulation(int nvoice);
        /**Computes the Frequency Modulated Oscillator.
         * @param FMmode modulation type 0=Phase 1=Frequency*/
        inline void ComputeVoiceOscillatorFrequencyModulation(int nvoice,
                                                              int FMmode);
        //  inline void ComputeVoiceOscillatorFrequencyModulation(int nvoice);
        /**TODO*/
        inline void ComputeVoiceOscillatorPitchModulation(int nvoice);

        /**Generate Noise Samples for Voice*/
        inline void ComputeVoiceNoise(int nvoice);

        /**Fadein in a way that removes clicks but keep sound "punchy"*/
        inline void fadein(float *smps) const;


        //GLOBALS
        ADnoteParameters *partparams;
        unsigned char     stereo; //if the note is stereo (allows note Panning)
        int   midinote;
        float velocity, basefreq;

        ONOFFTYPE   NoteEnabled;
        Controller *ctl;

        /*****************************************************************/
        /*                    GLOBAL PARAMETERS                          */
        /*****************************************************************/

        struct Global {
            void kill();
            void initparameters(const ADnoteGlobalParam &param,
                                float basefreq, float velocity,
                                bool stereo);
            /******************************************
            *     FREQUENCY GLOBAL PARAMETERS        *
            ******************************************/
            float Detune;  //cents

            Envelope *FreqEnvelope;
            LFO      *FreqLfo;

            /********************************************
            *     AMPLITUDE GLOBAL PARAMETERS          *
            ********************************************/
            float Volume;  // [ 0 .. 1 ]

            float Panning;  // [ 0 .. 1 ]

            Envelope *AmpEnvelope;
            LFO      *AmpLfo;

            struct {
                int   Enabled;
                float initialvalue, dt, t;
            } Punch;

            /******************************************
            *        FILTER GLOBAL PARAMETERS        *
            ******************************************/
            class Filter * GlobalFilterL, *GlobalFilterR;

            float FilterCenterPitch;  //octaves
            float FilterQ;
            float FilterFreqTracking;

            Envelope *FilterEnvelope;

            LFO *FilterLfo;
        } NoteGlobalPar;



        /***********************************************************/
        /*                    VOICE PARAMETERS                     */
        /***********************************************************/
        struct Voice {
            void releasekey();
            void kill();
            /* If the voice is enabled */
            ONOFFTYPE Enabled;

            /* Voice Type (sound/noise)*/
            int noisetype;

            /* Filter Bypass */
            int filterbypass;

            /* Delay (ticks) */
            int DelayTicks;

            /* Waveform of the Voice */
            float *OscilSmp;

            /************************************
            *     FREQUENCY PARAMETERS          *
            ************************************/
            int fixedfreq; //if the frequency is fixed to 440 Hz
            int fixedfreqET; //if the "fixed" frequency varies according to the note (ET)

            // cents = basefreq*VoiceDetune
            float Detune, FineDetune;

            Envelope *FreqEnvelope;
            LFO      *FreqLfo;


            /***************************
            *   AMPLITUDE PARAMETERS   *
            ***************************/

            /* Panning 0.0f=left, 0.5f - center, 1.0f = right */
            float Panning;
            float Volume;  // [-1.0f .. 1.0f]

            Envelope *AmpEnvelope;
            LFO      *AmpLfo;

            /*************************
            *   FILTER PARAMETERS    *
            *************************/

            class Filter * VoiceFilterL;
            class Filter * VoiceFilterR;

            float FilterCenterPitch;  /* Filter center Pitch*/
            float FilterFreqTracking;

            Envelope *FilterEnvelope;
            LFO      *FilterLfo;


            /****************************
            *   MODULLATOR PARAMETERS   *
            ****************************/

            FMTYPE FMEnabled;

            int FMVoice;

            // Voice Output used by other voices if use this as modullator
            float *VoiceOut;

            /* Wave of the Voice */
            float *FMSmp;

            float FMVolume;
            float FMDetune;  //in cents

            Envelope *FMFreqEnvelope;
            Envelope *FMAmpEnvelope;
        } NoteVoicePar[NUM_VOICES];


        /********************************************************/
        /*    INTERNAL VALUES OF THE NOTE AND OF THE VOICES     */
        /********************************************************/

        //time from the start of the note
        float time;

        //the size of unison for a single voice
        int unison_size[NUM_VOICES];

        //the stereo spread of the unison subvoices (0.0f=mono,1.0f=max)
        float unison_stereo_spread[NUM_VOICES];

        //fractional part (skip)
        float *oscposlo[NUM_VOICES], *oscfreqlo[NUM_VOICES];

        //integer part (skip)
        int *oscposhi[NUM_VOICES], *oscfreqhi[NUM_VOICES];

        //fractional part (skip) of the Modullator
        float *oscposloFM[NUM_VOICES], *oscfreqloFM[NUM_VOICES];

        //the unison base_value
        float *unison_base_freq_rap[NUM_VOICES];

        //how the unison subvoice's frequency is changed (1.0f for no change)
        float *unison_freq_rap[NUM_VOICES];

        //which subvoice has phase inverted
        bool *unison_invert_phase[NUM_VOICES];

        //unison vibratto
        struct {
            float  amplitude; //amplitude which be added to unison_freq_rap
            float *step; //value which increments the position
            float *position; //between -1.0f and 1.0f
        } unison_vibratto[NUM_VOICES];


        //integer part (skip) of the Modullator
        unsigned int *oscposhiFM[NUM_VOICES], *oscfreqhiFM[NUM_VOICES];

        //used to compute and interpolate the amplitudes of voices and modullators
        float oldamplitude[NUM_VOICES],
              newamplitude[NUM_VOICES],
              FMoldamplitude[NUM_VOICES],
              FMnewamplitude[NUM_VOICES];

        //used by Frequency Modulation (for integration)
        float *FMoldsmp[NUM_VOICES];

        //temporary buffer
        float  *tmpwavel;
        float  *tmpwaver;
        int     max_unison;
        float **tmpwave_unison;

        //Filter bypass samples
        float *bypassl, *bypassr;

        //interpolate the amplitudes
        float globaloldamplitude, globalnewamplitude;

        //1 - if it is the fitst tick (used to fade in the sound)
        char firsttick[NUM_VOICES];

        //1 if the note has portamento
        int portamento;

        //how the fine detunes are made bigger or smaller
        float bandwidthDetuneMultiplier;
};

#endif
