//===========================================================================
//
//    DeicsOnze an emulator of the YAMAHA DX11 synthesizer
//
//    Version 0.5.5
//
//
//
//
//  Copyright (c) 2004-2006 Nil Geisweiller
//
//
//
// 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., 51 Franklin Street, Fifth Floor, Boston, MA
// 02111-1301, USA or point your web browser to http://www.gnu.org.
//===========================================================================


#ifndef __DEICSONZE_H
#define __DEICSONZE_H

#include <list>

#include "common_defs.h"
#include "deicsonzepreset.h"
#include "deicsonzegui.h"
#include "deicsonzeplugin.h"
#include "deicsonzefilter.h"
#include "libsynti/mess.h"
#include "libsimpleplugin/simpler_plugin.h"

#define DEICSONZESTR "deicsonze"

#define MAXPITCHBENDVALUE 8191

#define RESOLUTION 96000

#define MAXFXBUFFERSIZE 48000
#define NBRFXINPUTS 2
#define NBRFXOUTPUTS 2

#define NBRCTRLS 127

#define NBRPRESETS 128

#define LOWERNOTEFREQ 8.176

#define DB0LEVEL 90

#define LENGTHNAME 20
#define LENGTHCATEGORY 20
#define LENGTHSUBCATEGORY 20

#define MAXVELO 127
#define MAXVOLUME 100.0
#define MAXSTRLENGTHINITSETPATH 256
#define MAXSTRLENGTHBACKGROUNDPIXPATH 256
#define MAXSTRLENGTHFXLIB 256
#define MAXSTRLENGTHFXLABEL 256

#define DB_MIN 25.0

//coef determined by ear to sound like the YAMAHA DX11
#define COEFFEEDBACK 0.3
#define COEFPLFO(x) (x==0?0.0:(x==1?0.06:(x==2?0.12:(x==3?0.25:(x==4?0.5:(x==5?0.9:(x==6?3.9:7.9))))))) //return pitch amplitude with respect to sensitivity pitch
#define COEFALFO(x) (x==0?0.0:(x==1?0.4:(x==2?0.9:1.0)))
#define MAX(x,y) (x<y?y:x)
#define COEFLEVEL 1.0//19.0
#define COEFMAXATTACK 7.5
#define COEFERRDECSUS 0.01 //for the transition between DECAY and SUSTAIN
#define COEFERRSUSREL 0.001 //from SUSTAIN or RELEASE until no sound
//#define ERRPORTA 0.001 //detection to stop portamento
#define COEFPORTA 0.01 //adjusted such that 10 second/octave with max porta
#define COEFPITCHENV 0.00000025 //adjust according to a real DX11....???
#define COEFDECAY 1.0
#define COEFSUSTAIN 0.2
#define COEFRELEASE 1.0
#define COEFDETUNE 0.008
#define COEFLEVELSCALE 0.0005
#define COEFGATTACK 0.9
#define COEFGRELEASE 0.9
#define LEVELSCALENOTE 24.0

#define NBRWAVES 8 //number wave forms, do not change
#define NBRBANKPRESETS 32
#define MAXNBRVOICES 64
#define NBRCHANNELS 16

#define SYSEX_INIT_DATA 1
#define SYSEX_INIT_DATA_VERSION 1
//#define SYSEX_INIT_DATA_VERSION_2 2
///#define SAVEINITLENGTH 2
#define SAVEINITLENGTH 4    // MFG ID, synth ID, init data command, init data version

#define DEICSONZECONFIGURATIONSTR "deicsOnzeConfiguation"
#define SYSEX_MASTERVOL 4
#define MASTERVOLSTR "MasterVolume"
#define MAXMASTERVOLUME 255
#define INITMASTERVOL 192
#define SYSEX_QUALITY 5
#define QUALITYSTR "Quality"
#define HIGHSTR "High"
#define MIDDLESTR "Middle"
#define LOWSTR "Low"
#define ULTRALOWSTR "UltraLow"
#define SYSEX_FONTSIZE 6
#define FONTSIZESTR "fontSize"
#define SYSEX_SAVECONFIG 7
#define SAVECONFIGSTR "SaveConfig"
#define SYSEX_SAVEONLYUSED 8
#define SAVEONLYUSEDSTR "SaveOnlyUsed"
#define YESSTRDEI "yes"
#define NOSTRDEI "no"
#define SYSEX_LOADSET 10
#define SYSEX_ISINITSET 12
#define ISINITSETSTR "IsInitSet"
#define SYSEX_INITSETPATH 13
#define INITSETPATHSTR "InitSetPath"
#define SYSEX_ISBACKGROUNDPIX 14
#define ISBACKGROUNDPIXSTR "IsBackgroundPix"
#define SYSEX_BACKGROUNDPIXPATH 15
#define BACKGROUNDPIXPATHSTR "backgroundPixPath"
#define SYSEX_COLORGUI 20
#define TEXTCOLORSTR "TextColor"
#define BACKGROUNDCOLORSTR "BackgroundColor"
#define EDITTEXTCOLORSTR "EditTextColor"
#define EDITBACKGROUNDCOLORSTR "EditBackgroundColor"
#define COLORSYSEXLENGTH 12
#define SYSEX_UPDATESETGUI 25
#define SYSEX_PANIC 30
#define SYSEX_CHORUSACTIV 40
#define SYSEX_CHORUSPARAM 41
#define SYSEX_REVERBACTIV 60
#define SYSEX_REVERBPARAM 61
#define SYSEX_CHORUSRETURN 80
#define SYSEX_REVERBRETURN 81
#define MAXFXRETURN 255
#define INITFXRETURN 192
#define SYSEX_SELECTREVERB 82
#define SYSEX_SELECTCHORUS 83
#define SYSEX_BUILDGUIREVERB 84
#define SYSEX_BUILDGUICHORUS 85
#define SYSEX_FILTER 90
#define FILTERSTR "Filter"
#define SYSEX_DELAYACTIV 91
#define SYSEX_DELAYRETURN 92
#define SYSEX_DELAYBPM 93
#define SYSEX_DELAYBEATRATIO 94
#define SYSEX_DELAYFEEDBACK 95
#define SYSEX_DELAYLFOFREQ 96
#define SYSEX_DELAYLFODEPTH 97
#define SYSEX_DELAYWETDRYMIX 98

//REVERB PARAMETERS

#define DEFAULTVOL 200

enum {
  NUM_MASTERVOL = SAVEINITLENGTH,
  NUM_CHANNEL_ENABLE,
  NUM_CHANNEL_VOL = NUM_CHANNEL_ENABLE + NBRCHANNELS + 1,
  NUM_CHANNEL_PAN = NUM_CHANNEL_VOL + NBRCHANNELS + 1,
  NUM_CHANNEL_BRIGHTNESS = NUM_CHANNEL_PAN + NBRCHANNELS + 1,
  NUM_CHANNEL_MODULATION = NUM_CHANNEL_BRIGHTNESS + 2*NBRCHANNELS +1,
  NUM_CHANNEL_DETUNE = NUM_CHANNEL_MODULATION + NBRCHANNELS + 1,
  NUM_CHANNEL_ATTACK = NUM_CHANNEL_DETUNE + NBRCHANNELS + 1,
  NUM_CHANNEL_RELEASE = NUM_CHANNEL_ATTACK + NBRCHANNELS + 1,
  NUM_CHANNEL_REVERB = NUM_CHANNEL_RELEASE + NBRCHANNELS + 1,
  NUM_CHANNEL_CHORUS = NUM_CHANNEL_REVERB + NBRCHANNELS + 1,  
  NUM_CHANNEL_DELAY = NUM_CHANNEL_CHORUS + NBRCHANNELS + 1,
  NUM_CURRENTPROG = NUM_CHANNEL_DELAY + NBRCHANNELS + 1,
  NUM_CURRENTLBANK = NUM_CURRENTPROG + NBRCHANNELS + 1,
  NUM_CURRENTHBANK = NUM_CURRENTLBANK + NBRCHANNELS + 1,
  NUM_NBRVOICES  = NUM_CURRENTHBANK + NBRCHANNELS + 1,
  NUM_SAVEONLYUSED  = NUM_NBRVOICES + NBRCHANNELS + 1,
  NUM_SAVECONFIG,
  NUM_RED_TEXT,
  NUM_GREEN_TEXT,
  NUM_BLUE_TEXT,
  NUM_RED_BACKGROUND,
  NUM_GREEN_BACKGROUND,
  NUM_BLUE_BACKGROUND,
  NUM_RED_EDITTEXT,
  NUM_GREEN_EDITTEXT,
  NUM_BLUE_EDITTEXT,
  NUM_RED_EDITBACKGROUND,
  NUM_GREEN_EDITBACKGROUND,
  NUM_BLUE_EDITBACKGROUND,
  NUM_QUALITY,
  NUM_FILTER,
  NUM_FONTSIZE,
  NUM_ISINITSET,
  NUM_INITSETPATH,
  NUM_ISBACKGROUNDPIX = NUM_INITSETPATH + MAXSTRLENGTHINITSETPATH + 1,
  NUM_BACKGROUNDPIXPATH,
  NUM_IS_REVERB_ON = NUM_BACKGROUNDPIXPATH + MAXSTRLENGTHBACKGROUNDPIXPATH + 1,
  NUM_REVERB_RETURN,
  NUM_REVERB_PARAM_NBR,
  NUM_REVERB_LIB,
  NUM_REVERB_LABEL = NUM_REVERB_LIB + MAXSTRLENGTHFXLIB + 1,
  NUM_IS_CHORUS_ON = NUM_REVERB_LABEL + MAXSTRLENGTHFXLABEL + 1,
  NUM_CHORUS_RETURN,
  NUM_CHORUS_PARAM_NBR,
  NUM_CHORUS_LIB,
  NUM_CHORUS_LABEL = NUM_CHORUS_LIB + MAXSTRLENGTHFXLIB + 1,
  NUM_IS_DELAY_ON = NUM_CHORUS_LABEL + MAXSTRLENGTHFXLABEL + 1,
  NUM_DELAY_RETURN,
  NUM_DELAY_BPM,
  NUM_DELAY_BEATRATIO = NUM_DELAY_BPM + sizeof(float),
  NUM_DELAY_FEEDBACK = NUM_DELAY_BEATRATIO + sizeof(float),
  NUM_DELAY_LFO_FREQ = NUM_DELAY_FEEDBACK + sizeof(float),
  NUM_DELAY_LFO_DEPTH = NUM_DELAY_LFO_FREQ + sizeof(float),
  NUM_CONFIGLENGTH = NUM_DELAY_LFO_DEPTH + sizeof(float)
  //NUM_DELAY_WET_DRY_MIX = NUM_DELAY_LFO_DEPTH + sizeof(float),
  //NUM_CONFIGLENGTH = NUM_DELAY_WET_DRY_MIX + sizeof(float)
};

class DeicsOnzeGui;
class DeicsOnzePlugin;

//---------------------------------------------------------
// outLevel2Amp, Amp for amplitude //between 0.0 and 2.0 or more
//  100->2.0, 90->1.0, 80->0.5 ...
//---------------------------------------------------------
inline double outLevel2Amp(int ol);

//---------------------------------------------------------
// level2amp, 
//  255->0dB->1.0, 0->-27dB->0
//---------------------------------------------------------
inline double level2amp(int l);

//---------------------------------------------------------
// amp2level
// 1.0->0dB->255, 0->-27dB->0
//---------------------------------------------------------
inline int amp2level(double amp);

//---------------------------------------------------------
// amp2lowlevel
// 1.0->0dB->127, 0->-27dB->0
//---------------------------------------------------------
inline int amp2lowlevel(double amp);

//---------------------------------------------------------
// lowlevel2amp, 
//  127->0dB->1.0, 0->-27dB->0
//---------------------------------------------------------
inline double lowlevel2amp(int l);

//---------------------------------------------------------
// envAR2s
//  return the time in second of the ATTACK duration
//---------------------------------------------------------
inline double envAR2s(int ar);

//---------------------------------------------------------
// coefAttack
//  convert the attack value to a coef for envInct
//---------------------------------------------------------
inline double coefAttack(unsigned char attack);

//---------------------------------------------------------
// envRR2coef
//  return the coefficient for the exponential decrease
//  with respect to rr and sampleRate, sr
//---------------------------------------------------------
inline double envRR2coef(int rr, double sr, unsigned char release);

//--------------------------------------------------------
// DeicsOnzeCtrl
//--------------------------------------------------------
struct DeicsOnzeCtlr
{
  std::string name;
  int num;
  int min, max;
};

//---------------------------------------------------------
// EnvState
//---------------------------------------------------------

enum EnvState{
  ATTACK,
  DECAY,
  SUSTAIN,
  RELEASE,
  OFF
};

//---------------------------------------------------------
// OpVoice
//---------------------------------------------------------

struct OpVoice {
  double index;
  double inct;
  double targetInct; //used if portamento
  double amp; //between 0 and 1
  double ampVeloNote; //keeps the ratio amplitude from velo2AmpR and note2Amp
                      //in order to change independently the output level
                      //after pressing the note
  EnvState envState;
  double envIndex;
  double envInct;
  double envLevel;
  double coefVLevel;
};

//---------------------------------------------------------
// PitchEnvState
//---------------------------------------------------------
enum PitchEnvState{
  PHASE1,
  PHASE2,
  RELEASE_PE,
  OFF_PE
};

//---------------------------------------------------------
// Voice
//---------------------------------------------------------

struct Voice {
  bool hasAttractor;//true iff the voice has an attractor (portamento occurring)
  double attractor; //contains some coefficient for portamento TODO
  PitchEnvState pitchEnvState;
  double pitchEnvCoefInct;
  double pitchEnvCoefInctPhase1;
  double pitchEnvCoefInctPhase2;
  double pitchEnvCoefInctPhase3;
  double pitchEnvCoefInctRelease;
  double pitchEnvCoefInctInct;
  bool isOn;
  bool keyOn;
  bool isSustained;
  int pitch; //number of the note
  double volume;
  OpVoice op[NBROP];
  float sampleFeedback;
};

//---------------------------------------------------------
// Channel
//---------------------------------------------------------
struct Channel {
  bool isEnable;
  float ampLeft;
  float ampRight;
  int volume; //0 to 255
  int pan; //TODO -63 +64 or -127 +128 
  int modulation;//0 to 127
  int detune;//-31 to 31
  int brightness; //0 to 4095
  int attack; //0 to 127
  int release; //0 to 127    
  float feedbackAmp;
  float lfoFreq;
  float lfoPitch;
  float lfoMaxCoefInct;
  float lfoCoefInct;
  float lfoCoefInctInct;
  unsigned int lfoIndex;
  unsigned int lfoMaxIndex;
  float lfoMaxAmp;
  float lfoMaxDAmp;
  float lfoAmp;
  float lfoCoefAmp;
  double lfoDelayIndex;
  double lfoDelayInct;
  double lfoDelayMaxIndex;
  bool delayPassed;
  bool sustain;
  double pitchBendCoef;//speed coef to read the sample
  unsigned char nbrVoices;
  Voice voices[MAXNBRVOICES];
  double lastInc[NBROP];
  std::list<int> lastVoiceKeyOn; //stack of the voice number
  int lastVoiceKeyOff;
  bool isLastNote;
  //FX
  float chorusAmount; //between 0.0 and 1.0
  float reverbAmount; //between 0.0 and 1.0
  float delayAmount; //between 0.0 and 1.0
};

//---------------------------------------------------------
// Global
//---------------------------------------------------------
enum Quality {
  high,
  middle,
  low,
  ultralow
};

struct Global {
  float masterVolume;
  Quality quality; //high, middle, low
  int qualityCounter; //counter to skip some sample depending on quality
  int qualityCounterTop; //number of sample - 1 to skip
  double deiSampleRate; //depending on quality deicsOnze sample rate varies
  bool filter; //low passe filter used when the sampling is low
  int fontSize;
  float lastLeftSample;
  float lastRightSample;
  float lastInputLeftChorusSample;
  float lastInputRightChorusSample;
  float lastInputLeftReverbSample;
  float lastInputRightReverbSample;
  float lastInputLeftDelaySample;
  float lastInputRightDelaySample;
  Channel channel[NBRCHANNELS];
  bool isChorusActivated;
  float chorusReturn;
  bool isReverbActivated;
  float reverbReturn;
  bool isDelayActivated;
  float delayReturn;
};

//---------------------------------------------------------
//   DeicsOnze : DX11 emulator
//---------------------------------------------------------

class DeicsOnze : public Mess {
  DeicsOnzeGui* _gui;
      
  unsigned char* initBuffer;
  int initLen;

  static int useCount;
  static float waveTable[NBRWAVES][RESOLUTION];

 private:
  void parseInitData(int length, const unsigned char* data);
  void loadConfiguration(QString fileName);
  void setupInitBuffer(int len);

 public:
  float** tempInputChorus;
  float** tempOutputChorus;
  float** tempInputReverb;
  float** tempOutputReverb;
  float** tempInputDelay;
  float** tempOutputDelay;

  float* getSinusWaveTable();

  int nbrCtrl;

  QString _initSetPath;
  bool _isInitSet;
  QString _backgroundPixPath;
  bool _isBackgroundPix;
  bool _saveOnlyUsed;
  bool _saveConfig;
  DeicsOnzeCtlr _ctrl[NBRCTRLS];
  Global _global;
  Preset* _preset[NBRCHANNELS];
  Preset* _initialPreset;

  //FX
  MusESimplePlugin::PluginI* _pluginIReverb;
  MusESimplePlugin::PluginI* _pluginIChorus;
  MusESimplePlugin::PluginI* _pluginIDelay;

  void initPluginReverb(MusESimplePlugin::Plugin*);
  void initPluginChorus(MusESimplePlugin::Plugin*);
  void initPluginDelay(MusESimplePlugin::Plugin*);
  
  void setReverbParam(int i, float val);
  float getReverbParam(int i) const;
  void setChorusParam(int i, float val);
  float getChorusParam(int i) const;
  void setDelayParam(int i, float val);
  float getDelayParam(int i) const;
  void setDelayBPM(float val);
  void setDelayBeatRatio(float val);
  void setDelayFeedback(float val);
  void setDelayLFOFreq(float val);
  void setDelayLFODepth(float val);
  void setDelayDryWet(float val);
  float getDelayDryWet() const; 
  float getDelayBPM() const;
  float getDelayBeatRatio() const;
  float getDelayFeedback() const;
  float getDelayLFOFreq() const;
  float getDelayLFODepth() const;

  //Filter
  LowFilter* _dryFilter;
  LowFilter* _chorusFilter;
  LowFilter* _reverbFilter;
  LowFilter* _delayFilter;

  mutable MidiPatch _patch;
  mutable int _numPatchProg; //used by getPatchInfo

  //preset tree 
  Set* _set;
  
  void setSampleRate(int sr);
  Preset* findPreset(int hbank, int lbank, int prog) const;
  Subcategory* findSubcategory(int hbank, int lbank) const;
  Category* findCategory(int hbank) const;
  void initCtrls();
  void initGlobal();
  void initChannels();
  void initChannel(int c);
  void resetVoices(); //when panic is pressed
  void initVoice(int c, int v);
  void initVoices(int c);
  void setPreset(int c);
  void setFeedback(int c);
  void setLfo(int c);
  void setOutLevel(int c, int k); //set the output level of the op k
  void setOutLevel(int c); //do the same for all operators
  void setEnvAttack(int c, int v, int k); //set envInct of voice v and op k
  void setEnvAttack(int c, int k); //do the same for all voices of operator k
  void setEnvAttack(int c); //do the same for all voices all operators
  void setEnvRelease(int c, int v, int k); //set coefVLevel of voice v and op k
  void setEnvRelease(int c, int k); //do the same for all voices of operator k
  void setEnvRelease(int c); //do the same for all voices all operators  
  void setPitchEnvRelease(int c, int v);
  void setQuality(Quality q);
  void setFilter(bool f);
  double brightness2Amp(int c, int k); //get the brightness of the operator k
  void loadSutulaPresets();
  void loadSet(QString s);
  int noteOff2Voice(int c); //return the first free voice
  int minVolu2Voice(int c);
  int pitchOn2Voice(int c, int pitch);
  void programSelect(int c, int hbank, int lbank, int prog);
  bool existsKeyOn(int ch);
  void setNbrVoices(int c, int nv);
  void setMasterVol(int v);
  void setChannelEnable(int c, bool e);
  void setChannelVol(int c, int v);
  void setChannelPan(int c, int v);
  void applyChannelAmp(int c);
  void setChannelDetune(int c, int d);
  void setChannelBrightness(int c, int b);
  void setChannelModulation(int c, int m);
  void setChannelAttack(int c, int a);
  void setChannelRelease(int c, int r);
  void setChannelReverb(int c, int r);
  void setChannelChorus(int c, int val);
  void setChannelDelay(int c, int val);
  void setChorusReturn(int val);
  void setReverbReturn(int val);
  void setDelayReturn(int val);
  bool getChannelEnable(int c) const;
  int getNbrVoices(int c) const;
  int getMasterVol(void) const;
  bool getFilter(void) const;
  int getChannelVol(int c) const;
  int getChannelPan(int c) const;
  int getChannelDetune(int c) const;
  int getChannelBrightness(int c) const;
  int getChannelModulation(int c) const;
  int getChannelAttack(int c) const;
  int getChannelRelease(int c) const;
  int getChannelReverb(int c) const;
  int getChannelChorus(int c) const;
  int getChannelDelay(int c) const;
  int getChorusReturn(void) const;
  int getReverbReturn(void) const;
  int getDelayReturn(void) const;
  void setPitchBendCoef(int c, int val);
  void setModulation(int c, int val); //TODO check between setChannelModulation
  void setSustain(int c, int val);

  void readConfiguration(QDomNode qdn);
  void writeConfiguration(AL::Xml* xml);

  bool setController(int ch, int ctrl, int val, bool fromGui);
  virtual bool setController(int ch, int ctrl, int val);
  bool sysex(int length, const unsigned char* data, bool fromGui); 
  virtual bool sysex(int l, const unsigned char* d);
  
  virtual const char* getPatchName(int ch, int number, bool) const; 
  virtual const MidiPatch* getPatchInfo(int, const MidiPatch *) const;

  virtual int getControllerInfo(int arg1, const char** arg2, 
				int* arg3, int* arg4, int* arg5, int* arg6) const;
  virtual void getInitData(int* length, const unsigned char** data);
  // This is only a kludge required to support old songs' midistates. Do not use in any new synth.
  virtual int oldMidiStateHeader(const unsigned char** data) const;

  virtual bool playNote(int channel, int pitch, int velo);
  virtual void processMessages();
  virtual void process(unsigned pos, float** buffer, int offset, int n);
  
  // GUI interface routines
  virtual bool hasNativeGui() const { return true; }
  virtual bool nativeGuiVisible() const;
  virtual void showNativeGui(bool);
  virtual void getNativeGeometry(int* x, int* y, int* w, int* h) const;
  virtual void setNativeGeometry(int, int, int, int);
  
  DeicsOnze();
  virtual ~DeicsOnze();
};

extern QString DEI_configPath;
extern QString DEI_sharePath;
extern unsigned int DEI_segmentSize;
extern int DEI_sampleRate;
extern bool DEI_useDenormalBias;
extern float DEI_denormalBias;

#endif /* __DEICSONZE_H */
