/*
 *
 * Copyright (C) 2003  Peter Hanappe and others.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public License
 * as published by the Free Software Foundation; either version 2 of
 * the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
 * 02111-1307, USA
 */


#ifndef __FLUID_S_H__
#define __FLUID_S_H__

#include "synti.h"
#include "rev.h"

namespace FluidS {

class Voice;
class SFont;
class Preset;
class Sample;
class Channel;
class Mod;
class Reverb;
class Chorus;
class Fluid;

#define FLUID_MAX_BUFSIZE       4096
#define FLUID_NUM_PROGRAMS      129

enum fluid_loop {
      FLUID_UNLOOPED            = 0,
      FLUID_LOOP_DURING_RELEASE = 1,
      FLUID_NOTUSED             = 2,
      FLUID_LOOP_UNTIL_RELEASE  = 3
      };

enum fluid_synth_status {
      FLUID_SYNTH_CLEAN,
      FLUID_SYNTH_PLAYING,
      FLUID_SYNTH_QUIET,
      FLUID_SYNTH_STOPPED
      };

//---------------------------------------------------------
//   BankOffset
//---------------------------------------------------------

struct BankOffset {
      int sfont_id;
      int offset;
      };

enum fluid_midi_control_change {
      BANK_SELECT_MSB = 0x00,
      MODULATION_MSB = 0x01,
      BREATH_MSB = 0x02,
      FOOT_MSB = 0x04,
      PORTAMENTO_TIME_MSB = 0x05,
      DATA_ENTRY_MSB = 0x06,
      VOLUME_MSB = 0x07,
      BALANCE_MSB = 0x08,
      PAN_MSB = 0x0A,
      EXPRESSION_MSB = 0x0B,
      EFFECTS1_MSB = 0x0C,
      EFFECTS2_MSB = 0x0D,
      GPC1_MSB = 0x10, /* general purpose controller */
      GPC2_MSB = 0x11,
      GPC3_MSB = 0x12,
      GPC4_MSB = 0x13,
      BANK_SELECT_LSB = 0x20,
      MODULATION_WHEEL_LSB = 0x21,
      BREATH_LSB = 0x22,
      FOOT_LSB = 0x24,
      PORTAMENTO_TIME_LSB = 0x25,
      DATA_ENTRY_LSB = 0x26,
      VOLUME_LSB = 0x27,
      BALANCE_LSB = 0x28,
      PAN_LSB = 0x2A,
      EXPRESSION_LSB = 0x2B,
      EFFECTS1_LSB = 0x2C,
      EFFECTS2_LSB = 0x2D,
      GPC1_LSB = 0x30,
      GPC2_LSB = 0x31,
      GPC3_LSB = 0x32,
      GPC4_LSB = 0x33,
      SUSTAIN_SWITCH = 0x40,
      PORTAMENTO_SWITCH = 0x41,
      SOSTENUTO_SWITCH = 0x42,
      SOFT_PEDAL_SWITCH = 0x43,
      LEGATO_SWITCH = 0x45,
      HOLD2_SWITCH = 0x45,
      SOUND_CTRL1 = 0x46,
      SOUND_CTRL2 = 0x47,
      SOUND_CTRL3 = 0x48,
      SOUND_CTRL4 = 0x49,
      SOUND_CTRL5 = 0x4A,
      SOUND_CTRL6 = 0x4B,
      SOUND_CTRL7 = 0x4C,
      SOUND_CTRL8 = 0x4D,
      SOUND_CTRL9 = 0x4E,
      SOUND_CTRL10 = 0x4F,
      GPC5 = 0x50,
      GPC6 = 0x51,
      GPC7 = 0x52,
      GPC8 = 0x53,
      PORTAMENTO_CTRL = 0x54,
      EFFECTS_DEPTH1 = 0x5B,
      EFFECTS_DEPTH2 = 0x5C,
      EFFECTS_DEPTH3 = 0x5D,
      EFFECTS_DEPTH4 = 0x5E,
      EFFECTS_DEPTH5 = 0x5F,
      DATA_ENTRY_INCR = 0x60,
      DATA_ENTRY_DECR = 0x61,
      NRPN_LSB = 0x62,
      NRPN_MSB = 0x63,
      RPN_LSB = 0x64,
      RPN_MSB = 0x65,
      ALL_SOUND_OFF = 0x78,
      ALL_CTRL_OFF = 0x79,
      LOCAL_CONTROL = 0x7A,
      ALL_NOTES_OFF = 0x7B,
      OMNI_OFF = 0x7C,
      OMNI_ON = 0x7D,
      POLY_OFF = 0x7E,
      POLY_ON = 0x7F
      };

/**
 * Generator (effect) numbers (Soundfont 2.01 specifications section 8.1.3)
 */
enum fluid_gen_type {
  GEN_STARTADDROFS,		/**< Sample start address offset (0-32767) */
  GEN_ENDADDROFS,		      /**< Sample end address offset (-32767-0) */
  GEN_STARTLOOPADDROFS,		/**< Sample loop start address offset (-32767-32767) */
  GEN_ENDLOOPADDROFS,		/**< Sample loop end address offset (-32767-32767) */
  GEN_STARTADDRCOARSEOFS,	/**< Sample start address coarse offset (X 32768) */
  GEN_MODLFOTOPITCH,		/**< Modulation LFO to pitch */
  GEN_VIBLFOTOPITCH,		/**< Vibrato LFO to pitch */
  GEN_MODENVTOPITCH,		/**< Modulation envelope to pitch */
  GEN_FILTERFC,			/**< Filter cutoff */
  GEN_FILTERQ,			/**< Filter Q */
  GEN_MODLFOTOFILTERFC,		/**< Modulation LFO to filter cutoff */
  GEN_MODENVTOFILTERFC,		/**< Modulation envelope to filter cutoff */
  GEN_ENDADDRCOARSEOFS,		/**< Sample end address coarse offset (X 32768) */
  GEN_MODLFOTOVOL,		/**< Modulation LFO to volume */
  GEN_UNUSED1,			/**< Unused */
  GEN_CHORUSSEND,		      /**< Chorus send amount */
  GEN_REVERBSEND,		      /**< Reverb send amount */
  GEN_PAN,			      /**< Stereo panning */
  GEN_UNUSED2,			/**< Unused */
  GEN_UNUSED3,			/**< Unused */
  GEN_UNUSED4,			/**< Unused */
  GEN_MODLFODELAY,		/**< Modulation LFO delay */
  GEN_MODLFOFREQ,		      /**< Modulation LFO frequency */
  GEN_VIBLFODELAY,		/**< Vibrato LFO delay */
  GEN_VIBLFOFREQ,		      /**< Vibrato LFO frequency */
  GEN_MODENVDELAY,		/**< Modulation envelope delay */
  GEN_MODENVATTACK,		/**< Modulation envelope attack */
  GEN_MODENVHOLD,		      /**< Modulation envelope hold */
  GEN_MODENVDECAY,		/**< Modulation envelope decay */
  GEN_MODENVSUSTAIN,		/**< Modulation envelope sustain */
  GEN_MODENVRELEASE,		/**< Modulation envelope release */
  GEN_KEYTOMODENVHOLD,		/**< Key to modulation envelope hold */
  GEN_KEYTOMODENVDECAY,		/**< Key to modulation envelope decay */
  GEN_VOLENVDELAY,		/**< Volume envelope delay */
  GEN_VOLENVATTACK,		/**< Volume envelope attack */
  GEN_VOLENVHOLD,		      /**< Volume envelope hold */
  GEN_VOLENVDECAY,		/**< Volume envelope decay */
  GEN_VOLENVSUSTAIN,		/**< Volume envelope sustain */
  GEN_VOLENVRELEASE,		/**< Volume envelope release */
  GEN_KEYTOVOLENVHOLD,		/**< Key to volume envelope hold */
  GEN_KEYTOVOLENVDECAY,		/**< Key to volume envelope decay */
  GEN_INSTRUMENT,		      /**< Instrument ID (shouldn't be set by user) */
  GEN_RESERVED1,		      /**< Reserved */
  GEN_KEYRANGE,			/**< MIDI note range */
  GEN_VELRANGE,			/**< MIDI velocity range */
  GEN_STARTLOOPADDRCOARSEOFS,	/**< Sample start loop address coarse offset (X 32768) */
  GEN_KEYNUM,			/**< Fixed MIDI note number */
  GEN_VELOCITY,			/**< Fixed MIDI velocity value */
  GEN_ATTENUATION,		/**< Initial volume attenuation */
  GEN_RESERVED2,		      /**< Reserved */
  GEN_ENDLOOPADDRCOARSEOFS,	/**< Sample end loop address coarse offset (X 32768) */
  GEN_COARSETUNE,		      /**< Coarse tuning */
  GEN_FINETUNE,			/**< Fine tuning */
  GEN_SAMPLEID,			/**< Sample ID (shouldn't be set by user) */
  GEN_SAMPLEMODE,		      /**< Sample mode flags */
  GEN_RESERVED3,		      /**< Reserved */
  GEN_SCALETUNE,		      /**< Scale tuning */
  GEN_EXCLUSIVECLASS,		/**< Exclusive class number */
  GEN_OVERRIDEROOTKEY,		/**< Sample root note override */

  /* the initial pitch is not a "standard" generator. It is not
   * mentioned in the list of generator in the SF2 specifications. It
   * is used, however, as the destination for the default pitch wheel
   * modulator. */
  GEN_PITCH,			/**< Pitch (NOTE: Not a real SoundFont generator) */
  GEN_LAST			      /**< Value defines the count of generators (#fluid_gen_type) */
      };

//---------------------------------------------------------
//   Channel
//---------------------------------------------------------

class Channel {
      Fluid* synth;

      unsigned int sfontnum;
      unsigned int banknum;
      unsigned int prognum;
      Preset* _preset;

   public:
      int channum;
      short key_pressure;
      short channel_pressure;
      short pitch_bend;
      short pitch_wheel_sensitivity;

      short cc[128];          // controller values

      /* cached values of last MSB values of MSB/LSB controllers */
      unsigned char bank_msb;
      int interp_method;

      /* NRPN system */
      short nrpn_select;

      /* The values of the generators, set by NRPN messages, or by
       * fluid_synth_set_gen(), are cached in the channel so they can be
       * applied to future notes. They are copied to a voice's generators
       * in fluid_voice_init(), wihich calls fluid_gen_init().  */

      float gen[GEN_LAST];

      /* By default, the NRPN values are relative to the values of the
       * generators set in the SoundFont. For example, if the NRPN
       * specifies an attack of 100 msec then 100 msec will be added to the
       * combined attack time of the sound font and the modulators.
       *
       * However, it is useful to be able to specify the generator value
       * absolutely, completely ignoring the generators of the sound font
       * and the values of modulators. The gen_abs field, is a boolean
       * flag indicating whether the NRPN value is absolute or not.
       */
      char gen_abs[GEN_LAST];

   public:
      Channel(Fluid* synth, int num);

      bool sustained() const              { return cc[SUSTAIN_SWITCH] >= 64; }
      void setGen(int n, float v, char a) { gen[n] = v; gen_abs[n] = a; }
      float getGen(int n) const           { return gen[n]; }
      char getGenAbs(int n) const         { return gen_abs[n]; }
      void init();
      void initCtrl();
      void setCC(int n, int val)          { cc[n] = val; }
      void reset();
      void setPreset(Preset* p);
      Preset* preset() const              { return _preset;  }
      unsigned int getSfontnum() const    { return sfontnum; }
      void setSfontnum(unsigned int s)    { sfontnum = s;    }
      unsigned int getBanknum() const     { return banknum;  }
      void setBanknum(unsigned int b)     { banknum = b;     }
      void setPrognum(int p)              { prognum = p;     }
      int getPrognum() const              { return prognum;  }
      void setcc(int ctrl, int val);
      void pitchBend(int val);
      int getPitchBend() const            { return pitch_bend; }
      void pitchWheelSens(int val);
      int getCC(int num);
      int getNum() const                  { return channum;    }
      void setInterpMethod(int m)         { interp_method = m; }
      int getInterpMethod() const         { return interp_method; }
      };

//---------------------------------------------------------
//   Fluid
//---------------------------------------------------------

class Fluid : public Synth {
      static const int SILENT_BLOCKS = 32;
      int silentBlocks;

      QList<SFont*> sfonts;               // the loaded soundfonts
      QList<BankOffset*> bank_offsets;    // the offsets of the soundfont banks
      QList<MidiPatch*> patches;

      QList<Voice*> freeVoices;           // unused synthesis processes
      QList<Voice*> activeVoices;         // active synthesis processes
      QString _error;                     // last error message

      static bool initialized;
      static void init();

      double sample_rate;                 // The sample rate
      float _masterTuning;                // usually 440.0
      double _tuning[128];                // the pitch of every key, in cents

      QMutex mutex;
      void updatePatchList();

   protected:
      int state;                          // the synthesizer state

      unsigned int sfont_id;

      double _gain;                       // master gain
      QList<Channel*> channel;            // the channels

      unsigned int noteid;                // the id is incremented for every new note. it's used for noteoff's

      float* left_buf;
      float* right_buf;
      float* fx_buf[2];

      Reverb* reverb;
      Chorus* chorus;

   public:
      Fluid();
      ~Fluid();
      virtual void init(int sampleRate);

      virtual bool loadSoundFont(const QString& s);
      virtual QString soundFont() const;

      virtual void play(const Event&);
      virtual const QList<MidiPatch*>& getPatchInfo() const { return patches; }

      virtual double masterGain() const            { return _gain; }
      virtual void setMasterGain(double val)       { _gain = val;  }
      virtual double effectParameter(int effect, int parameter);
      virtual void setEffectParameter(int ffect, int parameter, double value);

      bool log(const char* fmt, ...);

      bool set_reverb_preset(int num);

      Preset* get_preset(unsigned int sfontnum, unsigned int banknum, unsigned int prognum);
      Preset* find_preset(unsigned int banknum, unsigned int prognum);
      void all_notes_off(int chan);
      void all_sounds_off(int chan);
      void modulate_voices(int chan, bool is_cc, int ctrl);
      void modulate_voices_all(int chan);
      void damp_voices(int chan);
      int kill_voice(Voice * voice);
      void print_voice();

      /** This function assures that every MIDI channels has a valid preset
       *  (NULL is okay). This function is called after a SoundFont is
       *  unloaded or reloaded. */
      void update_presets();

      BankOffset* get_bank_offset0(int sfont_id) const;
      void remove_bank_offset(int sfont_id);

      int get_cc(int chan, int num) const { return channel[chan]->cc[num]; }

      void system_reset();
      void program_change(int chan, int prognum);

      int get_bank_offset(int sfont_id);
      int set_bank_offset(int sfont_id, int offset);
      void set_gen2(int chan, int param, float value, int absolute, int normalized);
      float get_gen(int chan, int param);
      void set_gen(int chan, int param, float value);
      void set_interp_method(int chan, int interp_method);

      Preset* get_channel_preset(int chan) const { return channel[chan]->preset(); }
      SFont* get_sfont_by_name(const QString& name);
      SFont* get_sfont_by_id(unsigned int id);
      SFont* get_sfont(unsigned int num)             { return sfonts[num];   }
      const SFont* get_sfont(unsigned int num) const { return sfonts[num];   }
      int sfcount() const                            { return sfonts.size(); }
      void remove_sfont(SFont* sf);
      int add_sfont(SFont* sf);
      int sfreload(unsigned int id);
      bool sfunload(unsigned int id, int reset_presets);
      int sfload(const QString& filename, int reset_presets);

      void start_voice(Voice* voice);
      Voice* alloc_voice(unsigned id, Sample* sample, int chan, int key, int vel, double vt);
      void free_voice_by_kill();

      virtual void process(unsigned len, float* lout, float* rout, int stride);

      void set_chorus(int nr, double level, double speed, double depth_ms, int type);
      void set_reverb(double roomsize, double damping, double width, double level);
      void program_reset();

      bool program_select2(int chan, char* sfont_name, unsigned bank_num, unsigned preset_num);
      bool program_select(int chan, unsigned sfont_id, unsigned bank_num, unsigned preset_num);
      void get_program(int chan, unsigned* sfont_id, unsigned* bank_num, unsigned* preset_num);
      void sfont_select(int chan, unsigned int sfont_id)    { channel[chan]->setSfontnum(sfont_id); }
      void bank_select(int chan, unsigned int bank)         { channel[chan]->setBanknum(bank); }

      Preset* get_preset(char* sfont_name, unsigned banknum, unsigned prognum);
      void get_pitch_wheel_sens(int chan, int* pval);
      void pitch_wheel_sens(int chan, int val);
      void get_pitch_bend(int chan, int* ppitch_bend);

      void freeVoice(Voice* v);

      double getPitch(int k) const   { return _tuning[k]; }
      float ct2hz_real(float cents)  { return powf(2.0f, (cents - 6900.0f) / 1200.0f) * _masterTuning; }

      float act2hz(float c)     { return 8.176 * pow(2.0, (double) c / 1200.0); }
      float ct2hz(float cents)  { return act2hz(qBound(1500.0f, cents, 13500.0f)); }

      virtual double masterTuning() const     { return _masterTuning; }
      virtual void setMasterTuning(double f)  { _masterTuning = f;    }

      QString error() const { return _error; }

      friend class Voice;
      };

  /*
   *
   * Chorus
   *
   */

enum fluid_chorus_mod {
      FLUID_CHORUS_MOD_SINE = 0,
      FLUID_CHORUS_MOD_TRIANGLE = 1
      };

/* Those are the default settings for the chorus. */
#define FLUID_CHORUS_DEFAULT_N      3
#define FLUID_CHORUS_DEFAULT_LEVEL  2.0f
#define FLUID_CHORUS_DEFAULT_SPEED  0.3f
#define FLUID_CHORUS_DEFAULT_DEPTH  8.0f
#define FLUID_CHORUS_DEFAULT_TYPE   FLUID_CHORUS_MOD_SINE


  /*
   *
   * Synthesis parameters
   *
   */

  /* Flags to choose the interpolation method */
enum fluid_interp {
      /* no interpolation: Fastest, but questionable audio quality */
      FLUID_INTERP_NONE     = 0,
      /* Straight-line interpolation: A bit slower, reasonable audio quality */
      FLUID_INTERP_LINEAR   = 1,
      /* Fourth-order interpolation: Requires 50 % of the whole DSP processing time, good quality
       * Default. */
      FLUID_INTERP_DEFAULT  = 4,
      FLUID_INTERP_4THORDER = 4,
      FLUID_INTERP_7THORDER = 7,
      FLUID_INTERP_HIGHEST  = 7
      };

#define fluid_sample_refcount(_sample) ((_sample)->refcount)


/** Sample types */

#define FLUID_SAMPLETYPE_MONO	      1
#define FLUID_SAMPLETYPE_RIGHT	2
#define FLUID_SAMPLETYPE_LEFT	      4
#define FLUID_SAMPLETYPE_LINKED	8
#define FLUID_SAMPLETYPE_ROM	      0x8000

/* Sets the sound data of the sample
 *     Warning : if copy_data is FALSE, data should have 8 unused frames at start
 *     and 8 unused frames at the end.
 */
int fluid_sample_set_sound_data(Sample* sample, short *data,
			       unsigned int nbframes, short copy_data, int rootkey);

/*
 *
 *  Utility functions
 */

  /* Maximum number of modulators in a voice */
#define FLUID_NUM_MOD           64

/**
 * SoundFont generator structure.
 */
class Generator {
   public:
      unsigned char flags; /**< Is the generator set or not (#fluid_gen_flags) */
      double val;          /**< The nominal value           */
      double mod;          /**< Change by modulators        */
      double nrpn;         /**< Change by NRPN messages     */

      void set_mod(double _val)  { mod  = _val; }
      void set_nrpn(double _val) { nrpn = _val; }
      };

/**
 * Enum value for 'flags' field of #_Generator (not really flags).
 */
enum fluid_gen_flags {
      GEN_UNUSED,		/**< Generator value is not set */
      GEN_SET,		/**< Generator value is set     */
      GEN_ABS_NRPN	/**< DOCME                      */
      };

void fluid_gen_set_default_values(Generator* gen);
  /*
   *  The interface to the synthesizer's voices
   *  Examples on using them can be found in fluid_defsfont.c
   */

  /* for fluid_voice_add_mod */
enum fluid_voice_add_mod {
      FLUID_VOICE_OVERWRITE,
      FLUID_VOICE_ADD,
      FLUID_VOICE_DEFAULT
      };

/* Disable FPE exception check */
#define fluid_check_fpe(expl)

unsigned int fluid_check_fpe_i386(char * explanation_in_case_of_fpe);

/*
 * interpolation data
 */
struct fluid_interp_coeff_t {
      float a0, a1, a2, a3;
      };

/* Flags telling the polarity of a modulator.  Compare with SF2.01
   section 8.2. Note: The numbers of the bits are different!  (for
   example: in the flags of a SF modulator, the polarity bit is bit
   nr. 9) */

enum fluid_mod_flags {
      FLUID_MOD_POSITIVE = 0,
      FLUID_MOD_NEGATIVE = 1,
      FLUID_MOD_UNIPOLAR = 0,
      FLUID_MOD_BIPOLAR  = 2,
      FLUID_MOD_LINEAR   = 0,
      FLUID_MOD_CONCAVE  = 4,
      FLUID_MOD_CONVEX   = 8,
      FLUID_MOD_SWITCH   = 12,
      FLUID_MOD_GC       = 0,
      FLUID_MOD_CC       = 16
      };

//---------------------------------------------------------
//   Mod
//---------------------------------------------------------

struct Mod
      {
      uchar dest;
      uchar src1;
      uchar flags1;
      uchar src2;
      uchar flags2;
      double amount;

      Mod();
      Mod(uchar a, uchar b, uchar c, uchar d, uchar e, double f)
         : dest(a), src1(b), flags1(c), src2(d), flags2(e), amount(f) {}

      void dump() const;
      bool has_source(bool cc, int ctrl) const {
            return (((src1 == ctrl) &&   (flags1 & FLUID_MOD_CC)  && cc)
                || (((src1 == ctrl) && (!(flags1 & FLUID_MOD_CC)) && !cc)))
                || (((src2 == ctrl) &&   (flags2 & FLUID_MOD_CC)  && cc)
                || (((src2 == ctrl) && (!(flags2 & FLUID_MOD_CC)) && !cc)));
            }
      bool has_dest(uchar gen) const            { return dest == gen; }
      void set_source1(int src, int flags)      { src1 = src; flags1 = flags; }
      void set_source2(int src, int flags)      { src2 = src; flags2 = flags; }
      void set_dest(int val)                    { dest = val;    }
      void set_amount(double val)               { amount = val;  }
      int get_source1() const                   { return src1;   }
      int get_flags1() const                    { return flags1; }
      int get_source2() const                   { return src2;   }
      int get_flags2() const                    { return flags2; }
      int get_dest() const                      { return dest;   }
      double get_amount() const                 { return amount; }
      float get_value(Channel* chan, Voice* voice);
      };

/* Flags telling the source of a modulator.  This corresponds to
 * SF2.01 section 8.2.1 */

enum fluid_mod_src {
      FLUID_MOD_NONE             = 0,
      FLUID_MOD_VELOCITY         = 2,
      FLUID_MOD_KEY              = 3,
      FLUID_MOD_KEYPRESSURE      = 10,
      FLUID_MOD_CHANNELPRESSURE  = 13,
      FLUID_MOD_PITCHWHEEL       = 14,
      FLUID_MOD_PITCHWHEELSENS   = 16
      };

/* Determines, if two modulators are 'identical' (all parameters
   except the amount match) */
bool test_identity(const Mod * mod1, const Mod * mod2);

void fluid_dump_modulator(Mod * mod);

/*
 *  phase
 */

#define FLUID_INTERP_BITS        8
#define FLUID_INTERP_BITS_MASK   0xff000000
#define FLUID_INTERP_BITS_SHIFT  24
#define FLUID_INTERP_MAX         256

#define FLUID_FRACT_MAX ((double)4294967296.0)

//---------------------------------------------------------
//   Phase
/* Purpose:
* Playing pointer for voice playback
*
* When a sample is played back at a different pitch, the playing pointer in the
* source sample will not advance exactly one sample per output sample.
* This playing pointer is implemented using Phase.
* It is a 64 bit number. The higher 32 bits contain the 'index' (number of
* the current sample), the lower 32 bits the fractional part.
* Access is possible in two ways:
* -through the 64 bit part 'b64', if the architecture supports 64 bit integers
* -through 'index' and 'fract'
* Note: b64 and index / fract share the same memory location!
*/

struct Phase {
      qint64 data;

      void operator+=(const Phase& p) { data += p.data; }
      void setInt(qint32 b)           { data = qint64(b) << 32; }
      void setFloat(double b)          {
             data = (((qint64)(b)) << 32) | (quint32) (((double)(b) - (int)(b)) * (double)FLUID_FRACT_MAX);
            }

      void operator-=(const Phase& b) { data -= b.data;  }
      void operator-=(int b)          { data -= (qint64(b) << 32);  }
      int index() const               { return data >> 32; }
      quint32 fract() const           { return quint32(data & 0xffffffff); }
      quint32 index_round() const     { return quint32((data+0x80000000) >> 32); }

      Phase() {}
      Phase(qint64 v) : data(v) {}
      };

/* Purpose:
 * Takes the fractional part of the argument phase and
 * calculates the corresponding position in the interpolation table.
 * The fractional position of the playing pointer is calculated with a quite high
 * resolution (32 bits). It would be unpractical to keep a set of interpolation
 * coefficients for each possible fractional part...
 */
#define fluid_phase_fract_to_tablerow(_x) \
  ((int)(((_x).fract() & FLUID_INTERP_BITS_MASK) >> FLUID_INTERP_BITS_SHIFT))

#define fluid_phase_double(_x) \
  ((double)((_x).index()) + ((double)((_x).fract()) / FLUID_FRACT_MAX))

/* Purpose:
 * The playing pointer is _phase. How many output samples are produced, until the point _p1 in the sample is reached,
 * if _phase advances in steps of _incr?
 */
#define fluid_phase_steps(_phase,_idx,_incr) \
  (int)(((double)(_idx) - fluid_phase_double(_phase)) / (double)_incr)

/* Purpose:
 * Creates the expression a.index++.
*/
#define fluid_phase_index_plusplus(a) (((a)._index)++)

}  // namespace Fluid

#endif  // __FLUID_S_H__
