// ----
// ---- file   : StSamplePlayer.h
// ---- author : Bastian Spiegel <bs@tkscript.de>
// ---- legal  : (c) 2009-2019 by Bastian Spiegel. 
// ----          Distributed under terms of the GNU LESSER GENERAL PUBLIC LICENSE (LGPL). See 
// ----          http://www.gnu.org/licenses/licenses.html#LGPL or COPYING for further information.
// ----
// ---- info   : This is part of the "syntracker" midi sequencer.
// ----
// ---- created: 23Sep2009
// ----          
// ---- changed: 26Dec2009, 27Dec2009, 29Dec2009, 02Jan2010, 05Jan2010, 07Jan2010, 15Jan2010
// ----          19Jan2010, 21Jan2010, 01Mar2010, 29Apr2010, 28Jun2010, 29Jun2010, 01Jul2010
// ----          05Sep2010, 09Sep2010, 10Mar2011, 28Mar2011, 13Dec2018, 29Dec2018, 05Jan2019
// ----          23Mar2019, 24Mar2019, 30Mar2019, 12May2019, 15May2019
// ----
// ----
// ----

#ifndef __ST_SAMPLEPLAYER_H__
#define __ST_SAMPLEPLAYER_H__

YG("tksampler");


/* @class StSamplePlayer,Object
  Manages a finite number of §StSampleVoice objects
 */

YC class StSamplePlayer : public YAC_Object {


/* @constant STSAMPLEPLAYER_MAXVOICES,int
 */
#define STSAMPLEPLAYER_MAXVOICES YCI 256


/* @constant STSAMPLEPLAYER_POLYMODE_OLDEST,int
 */
#define STSAMPLEPLAYER_POLYMODE_OLDEST  YCI 0


/* @constant STSAMPLEPLAYER_POLYMODE_LOWEST,int
 */
#define STSAMPLEPLAYER_POLYMODE_LOWEST  YCI 1


/* @constant STSAMPLEPLAYER_POLYMODE_HIGHEST,int
 */
#define STSAMPLEPLAYER_POLYMODE_HIGHEST YCI 2


/* @constant STSAMPLEPLAYER_NUM_POLYMODES,int
 */
#define STSAMPLEPLAYER_NUM_POLYMODES    YCI 3


  public:
   StSampleVoice **voices; // StSampleVoice instances
   sUI num_voices;

   sF32 volume; // post-sample scale factor, 1=no scaling

   sUI poly_mode; // Voice alloc. strategy, See STSAMPLEPLAYER_POLYMODE_xxx

   sF32            default_mixrate;
   YAC_FloatArray *default_freq_table;

   sUI next_voice_key;

   sF32 mod_vol;  // all voices modifiers
   sF32 mod_pan;
   sF32 mod_freq;   // e.g. pitch randomization
   sF32 mod_freq2;  // e.g. pitch bend
   sF32 mod_mod;
   sF32 mod_timestretch;
   sF32 mod_timestretch_bend;
   sF32 mod_sampleoff;
   sF32 mod_cyclelen;
   sF32 mod_sampleshift;
   sF32 mod_sampleshift_endofloop;
   sF32 mod_altvolume;

   sF32 perf_ctl[STSAMPLEPLAYER_NUM_PERFCTL];

   sUI global_tick_nr;

  public:
   StSamplePlayer(void);
   ~StSamplePlayer();

   YAC(StSamplePlayer);

   void freeVoices(void);

   void applyMaxVoicesConstraint (StSample *_sample);
   void applyMaxSampleBankVoicesConstraint (StSampleBank *_sb);

   void stopSampleBankNotes (StSampleBank *_sb, sSI _note);

   StSampleVoice *findBestMatchVoice (sBool _bSoftStoppedOnly, sBool _bReleaseOnly);
   StSampleVoice *allocSampleVoice (StSample *_sample, sSI _note);
   StSampleVoice *allocSampleVoiceGlide (StSample *_sample, sSI _note);

   YAC_FloatArray *getCurrentFreqTable (YAC_Object *_altTable);

  
   /* @method resetModulator
      Reset all modulators
   */
   YM void resetModulators(void);

   /* @method allocVoices,int maxPolyphony:boolean
      Allocate internal voice pool

     @arg mayPolyphony Number of voices to allocate
     @return 1(true) if the allocation succeeded, 0(false) otherwise.
   */
   YM sSI allocVoices(sUI _maxPolyphony);


   /* @method getNumVoices:int
      Query polyphony
   */
   YM sUI getNumVoices(void);


   /* @method getNumActiveVoices:int
      Query number of currently used (allocated) voices
   */
   YM sUI getNumActiveVoices(void);


   /* @method getNumPlayingVoices:int
      Query number of currently used (allocated+playing) voices
   */
   YM sUI getNumPlayingVoices(void);


   /* @method getNumActiveNotes:int
      Query number of currently (pressed) notes
   */
   YM sUI getNumActiveNotes(void);


   /* @method getVoice,int idx:StSampleVoice
      Query voice object
   */
   YM YAC_Object *_getVoice(sUI _idx);


   /* @method setDefaultMixRate,float mixRate
      Set default mixrate for new sample voices

      (initial value is 48000Hz)
   */
   YM void setDefaultMixRate(sF32 _mixRate);


   /* @method getDefaultMixRate:float
      Query default mixrate
   */
   YM sF32 getDefaultMixRate(void);


   /* @method setDefaultFreqTable,FloatArray freqTable
      Set default frequency table for new sample voices

      This table is used when the freqTable argument in calls to §startSample or §startSampleBank is null.
   */
   YM void setDefaultFreqTable(YAC_Object *_freqTable);


   /* @method getDefaultFreqTable:FloatArray
      Query default frequency table
   */
   YM YAC_Object *getDefaultFreqTable(void);


   /* @method setVolume,float volume
    */
   YM void setVolume(sF32 _volume);


   /* @method getVolume:float
    */
   YM sF32 getVolume(void);

   
   /* @method setPolyMode,int mode
      Select voice allocation strategy

      @mode See STSAMPLEPLAYER_POLYMODE_xxx constants, e.g. STSAMPLEPLAYER_POLYMODE_OLDEST
   */
   YM void setPolyMode(sUI _mode);


   /* @method getPolyMode:int
      Query voice allocation strategy 

      @return Current poly mode (default = §STSAMPLEPLAYER_POLYMODE_OLDEST)
   */
   YM sUI getPolyMode(void);


   /* @method allNotesOff
      Stop all currently playing voices (with fadeout)
   */
   YM void allNotesOff(void);


   /* @method reset
      Immediately reset / stop all voices (no fadeout)
   */
   YM void resetVoices(void);


   /* @method stopVoicesByKey,int voiceKey
      Immediately stop all voices that are assigned the given key (no fadeout)
      This is called when muting a track.
   */
   YM void stopVoicesByKey(sSI _key);


   /* @method stopVoicesBySample,StSample sample
      Immediately stop all voices that play the given sample (no fadeout)
      This is called when modifying the sample's waveform.
   */
   YM void stopVoicesBySample(YAC_Object *_sample);


   /* @method stopLFOVolBySample,StSample sample
      Immediately stop all volume LFOs that belong to voices that play the given sample.
      This is called when deleting an LFO in the UI while the voice is playing.
   */
   YM void stopLFOVolBySample(YAC_Object *_sample);


   /* @method stopLFOPanBySample,StSample sample
      Immediately stop all pan LFOs that belong to voices that play the given sample.
      This is called when deleting an LFO in the UI while the voice is playing.
   */
   YM void stopLFOPanBySample(YAC_Object *_sample);


   /* @method stopLFOFreqBySample,StSample sample
      Immediately stop all frequency LFOs that belong to voices that play the given sample.
      This is called when deleting an LFO in the UI while the voice is playing.
   */
   YM void stopLFOFreqBySample(YAC_Object *_sample);


   /* @method stopADSRVolBySample,StSample sample
      Immediately stop all volume ADSRs that belong to voices that play the given sample.
      This is called when deleting an ADSR in the UI while the voice is playing.
   */
   YM void stopADSRVolBySample(YAC_Object *_sample);


   /* @method stopADSRPanBySample,StSample sample
      Immediately stop all pan ADSRs that belong to voices that play the given sample.
      This is called when deleting an LFO in the UI while the voice is playing.
   */
   YM void stopADSRPanBySample(YAC_Object *_sample);


   /* @method stopADSRFreqBySample,StSample sample
      Immediately stop all frequency ADSRs that belong to voices that play the given sample.
      This is called when deleting an ADSR in the UI while the voice is playing.
   */
   YM void stopADSRFreqBySample(YAC_Object *_sample);


   /* @method noteOff,int note,float velocity:int
      Begin release phase of all voices that were triggered on the given key
      
      @arg note MIDI note (0..127)
      @arg velocity Note-off velocity (0..1)
      @return Number of voices affected (0..n)
   */
   YM sSI noteOff(sSI _note, sF32 velocity);


   /* @method noteOffByKey,int voiceKey,float velocity:int
      Begin release phase of all voices that share the given voice key
      
      @arg voiceKey Voice key
      @arg velocity Note-off velocity (0..1)
      @return Number of voices affected (0..n)
   */
   YM sSI noteOffByKey(sSI _key, sF32 velocity);

   
   /* @method startSample,StSample sample,FloatArray freqTable,int note,float velocity,float mod:int
      Allocate voice and prepare to play the given sample.

      Any voice that plays a sample belonging to the same mutex group will be canceled.

      initStartedVoicesByKey() must be called to actually initialize the voice.

      @arg sample Which sample to play
      @arg freqTable Frequency table (scale)
      @arg note Which note to play (0..127)
      @arg velocity Note-on velocity (0..1)
      @arg mod Modulation
      @return Voice key (incremental)

      @see startSampleBank
      @see noteOff
      @see allNotesOff
   */
   YM sSI startSample(YAC_Object *_sample, YAC_Object *_freqTable, sSI _note, sF32 _velocity, sF32 _mod, sF32 _vol, sF32 _pan, sF32 _freq);


   /* @method startSampleBank,StSampleBank sampleBank,FloatArray freqTable,int note,float velocity,float mod:int
      Allocate voice(s) and play the samplebank samples that match the given note/velo/mod zones.

      Any voice that plays a sample belonging to the same mutex group(s) will be canceled.

      initStartedVoicesByKey() must be called to actually initialize the voice.

      @arg sample Which sample to play
      @arg freqTable Frequency table (scale)
      @arg note Which note to play (0..127)
      @arg velocity Note-on velocity (0..1)
      @arg mod Modulation
      @return Voice key (incremental)

      @see startSample
      @see noteOff
      @see allNotesOff
   */
   YM sSI startSampleBank(YAC_Object *_sampleBank, YAC_Object *_freqTable, sSI _note, sF32 _velocity, sF32 _mod, sF32 _vol, sF32 _pan, sF32 _freq);

   
   /* @method findVoicesByKey,int key,PointerArray ret:int
      Find all voices that are assigned the given key.

      @return Number of voices added to array.
   */
   YM sSI _findVoicesByKey(sSI _key, YAC_Object *_retPA);


   /* @method initStartedVoicesByKey,int key
      Really trigger note-on. Should be called after (initial) params have been set.
    */
   YM void _initStartedVoicesByKey(sSI _key);


   /* @method restartVoicesByKey,int key
      Reset playing voices.
    */
   YM void _restartVoicesByKey(sSI _key);


   /* @method render,FloatArray buf
      Render currently playing voices to the given (stereo) buffer (add).

      @arg buf Render buffer. numElements/2 determines the number of samples to render.
   */
   YM void _render (YAC_Object *_fa);


   /* @method updateVol,float v
      Update volume modulation of all active voices
   */
   YM void updateVol(sF32 _v);
   YM void updateVolByKey(sSI _voiceKey, sF32 _v);


   /* @method updatePan,float v
      Update panning modulation of all active voices
   */
   YM void updatePan(sF32 _v);
   YM void updatePanByKey(sSI _voiceKey, sF32 _v);


   /* @method updateFreq,float v
      Update frequency modulation of all active voices
   */
   YM void updateFreq(sF32 _v);
   YM void updateFreqByKey(sSI _voiceKey, sF32 _v);


   /* @method updateFreq2,float v
      Update frequency modulation #2 of all active voices
   */
   YM void updateFreq2(sF32 _v);
   YM void updateFreq2ByKey(sSI _voiceKey, sF32 _v);



   /* @method updateMod,float v
      Update modulation of all active voices
   */
   YM void updateMod(sF32 _v);
   YM void updateModByKey(sSI _voiceKey, sF32 _v);


   /* @method updateTimestretch,float v
      Update timestretch modulation of all active voices
   */
   YM void updateTimestretch(sF32 _v);
   YM void updateTimestretchByKey(sSI _voiceKey, sF32 _v);


   /* @method updateTimestretchBend,float v
      Update timestretch phase bend modulation of all active voices
   */
   YM void updateTimestretchBend(sF32 _v);
   YM void updateTimestretchBendByKey(sSI _voiceKey, sF32 _v);



   /* @method updateSampleOff,float v
      Update sample offset modulation of all active voices
   */
   YM void updateSampleOff(sF32 _v);
   YM void updateSampleOffByKey(sSI _voiceKey, sF32 _v);



   /* @method updateCycleLen,float v
      Update timestretch granularity modulation of all active voices
   */
   YM void updateCycleLen(sF32 _v);
   YM void updateCycleLenByKey(sSI _voiceKey, sF32 _v);



   /* @method updateSampleShift,float v
      Update sample shift modulation of all active voices
   */
   YM void updateSampleShift(sF32 _v);
   YM void updateSampleShiftByKey(sSI _voiceKey, sF32 _v);


   /* @method updateSampleShiftEndOfLoop,float v
      Update sample shift modulation of all active voices (queue until end of loop)
   */
   YM void updateSampleShiftEndOfLoop(sF32 _v);
   YM void updateSampleShiftEndOfLoopByKey(sSI _voiceKey, sF32 _v);


   /* @method updateAltVolume,float v
      Update alternative volume modulation of all active voices
   */
   YM void updateAltVolume(sF32 _v);
   YM void updateAltVolumeByKey(sSI _voiceKey, sF32 _v);


   /* @method updateRetrigMask,int v
      Retrigger envelope/LFO of all active voices
         0x01: reset freq envelope
         0x02: reset volume envelope
         0x04: reset pan envelope
         0x08: reset aux envelope
         0x10: reset freq LFO
         0x20: reset volume LFO
         0x40: reset pan LFO
         0x80: reset aux LFO
   */
   YM void updateRetrigMask(sUI _v);
   YM void updateRetrigMaskByKey(sSI _voiceKey, sUI _v);


   /* @method updateLFOFreqSpd,float spd
      Update frequency LFO speed of all active voices
   */
   YM void updateLFOFreqSpd(sF32 _spd);
   YM void updateLFOFreqSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateLFOFreqLvl,float lvl
      Update frequency LFO level of all active voices
   */
   YM void updateLFOFreqLvl(sF32 _lvl);
   YM void updateLFOFreqLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateLFOVolSpd,float spd
      Update volume LFO speed of all active voices
   */
   YM void updateLFOVolSpd(sF32 _spd);
   YM void updateLFOVolSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateLFOVolLvl,float lvl
      Update volume LFO level of all active voices
   */
   YM void updateLFOVolLvl(sF32 _lvl);
   YM void updateLFOVolLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateLFOPanSpd,float spd
      Update pan LFO speed of all active voices
   */
   YM void updateLFOPanSpd(sF32 _spd);
   YM void updateLFOPanSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateLFOPanLvl,float lvl
      Update pan LFO level of all active voices
   */
   YM void updateLFOPanLvl(sF32 _lvl);
   YM void updateLFOPanLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateLFOAuxSpd,float spd
      Update aux LFO speed of all active voices
   */
   YM void updateLFOAuxSpd(sF32 _spd);
   YM void updateLFOAuxSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateLFOAuxLvl,float lvl
      Update aux LFO level of all active voices
   */
   YM void updateLFOAuxLvl(sF32 _lvl);
   YM void updateLFOAuxLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateADSRFreqSpd,float spd
      Update frequency ADSR speed of all active voices
   */
   YM void updateADSRFreqSpd(sF32 _spd);
   YM void updateADSRFreqSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateADSRFreqLvl,float lvl
      Update frequency ADSR level of all active voices
   */
   YM void updateADSRFreqLvl(sF32 _lvl);
   YM void updateADSRFreqLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateADSFreqInt,float int
      Update frequency ADSR intensity of all active voices
   */
   YM void updateADSRFreqInt(sF32 _int);
   YM void updateADSRFreqIntByKey(sSI _voiceKey, sF32 _int);


   /* @method updateADSRVolSpd,float spd
      Update volume ADSR speed of all active voices
   */
   YM void updateADSRVolSpd(sF32 _spd);
   YM void updateADSRVolSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateADSRVolLvl,float lvl
      Update volume ADSR level of all active voices
   */
   YM void updateADSRVolLvl(sF32 _lvl);
   YM void updateADSRVolLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateADSRVolInt,float int
      Update volume ADSR intensity of all active voices
   */
   YM void updateADSRVolInt(sF32 _int);
   YM void updateADSRVolIntByKey(sSI _voiceKey, sF32 _int);


   /* @method updateADSRPanSpd,float spd
      Update pan ADSR speed of all active voices
   */
   YM void updateADSRPanSpd(sF32 _spd);
   YM void updateADSRPanSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateADSRPanLvl,float lvl
      Update pan ADSR level of all active voices
   */
   YM void updateADSRPanLvl(sF32 _lvl);
   YM void updateADSRPanLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateADSRPanInt,float int
      Update pan ADSR intensity of all active voices
   */
   YM void updateADSRPanInt(sF32 _int);
   YM void updateADSRPanIntByKey(sSI _voiceKey, sF32 _int);


   /* @method updateADSRAuxSpd,float spd
      Update aux ADSR speed of all active voices
   */
   YM void updateADSRAuxSpd(sF32 _spd);
   YM void updateADSRAuxSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateADSRAuxLvl,float lvl
      Update aux ADSR level of all active voices
   */
   YM void updateADSRAuxLvl(sF32 _lvl);
   YM void updateADSRAuxLvlByKey(sSI _voiceKey, sF32 _lvl);


   /* @method updateADSRAuxInt,float int
      Update aux ADSR intensity of all active voices
   */
   YM void updateADSRAuxInt(sF32 _int);
   YM void updateADSRAuxIntByKey(sSI _voiceKey, sF32 _int);


   /* @method updateJumpToLoop,int idx
      Make all playing voices jump to the given loop
   */
   YM void updateJumpToLoop(sSI _idx);
   YM void updateJumpToLoopByKey(sSI _voiceKey, sSI _idx);


   /* @method updateJumpToLoopRel,int idx
      Make all playing voices jump to the given loop (0..1 => 0..num_loops-1)
   */
   YM void updateJumpToLoopRel(sF32 _relIdx);
   YM void updateJumpToLoopRelByKey(sSI _voiceKey, sF32 _relIdx);


   /* @method updateWaveguideIndex,int idx
      Make all playing voices jump to the given cycle index (granular mode)
   */
   YM void updateWaveguideIndex(sSI _idx);
   YM void updateWaveguideIndexByKey(sSI _voiceKey, sSI _idx);


   /* @method updateAFreqSpd,float int
      Update freq attack speed of all active voices
   */
   YM void updateAFreqSpd(sF32 _spd);
   YM void updateAFreqSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateSFreqSpd,float int
      Update freq sustain speed of all active voices
   */
   YM void updateSFreqSpd(sF32 _spd);
   YM void updateSFreqSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateRFreqSpd,float int
      Update freq release speed of all active voices
   */
   YM void updateRFreqSpd(sF32 _spd);
   YM void updateRFreqSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateAVolSpd,float int
      Update volume attack speed of all active voices
   */
   YM void updateAVolSpd(sF32 _spd);
   YM void updateAVolSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateSVolSpd,float int
      Update volume sustain speed of all active voices
   */
   YM void updateSVolSpd(sF32 _spd);
   YM void updateSVolSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateRVolSpd,float int
      Update volume release speed of all active voices
   */
   YM void updateRVolSpd(sF32 _spd);
   YM void updateRVolSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateAPanSpd,float int
      Update pan attack speed of all active voices
   */
   YM void updateAPanSpd(sF32 _spd);
   YM void updateAPanSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateSPanSpd,float int
      Update pan sustain speed of all active voices
   */
   YM void updateSPanSpd(sF32 _spd);
   YM void updateSPanSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateRPanSpd,float int
      Update pan release speed of all active voices
   */
   YM void updateRPanSpd(sF32 _spd);
   YM void updateRPanSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateAAuxSpd,float int
      Update aux attack speed of all active voices
   */
   YM void updateAAuxSpd(sF32 _spd);
   YM void updateAAuxSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateSAuxSpd,float int
      Update aux sustain speed of all active voices
   */
   YM void updateSAuxSpd(sF32 _spd);
   YM void updateSAuxSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateRAuxSpd,float int
      Update aux release speed of all active voices
   */
   YM void updateRAuxSpd(sF32 _spd);
   YM void updateRAuxSpdByKey(sSI _voiceKey, sF32 _spd);


   /* @method updateGlideSwitch,boolean bEnable
      Update glide switch of all active voices
   */
   YM void updateGlideSwitch(sBool _bEnable);
   YM void updateGlideSwitchByKey(sSI _voiceKey, sBool _bEnable);


   /* @method updateGlideSpeed,float speed
      Update glide speed of all active voices
   */
   YM void updateGlideSpeed(sF32 _speed);
   YM void updateGlideSpeedByKey(sSI _voiceKey, sF32 _speed);


   /* @method updatePerfCtl,int idx,float val
      Update performance controller (original MIDI range, 0..127 or 0..16383)
   */
   YM void updatePerfCtl (sUI _idx, sF32 _val);

   // for poly-pressure (STSAMPLEPLAYER_PERFCTL_PRESSURE)
   YM void updatePerfCtlByKey (sSI _voiceKey, sUI _idx, sF32 _val);
};


#endif // __ST_SAMPLEPLAYER_H__
