// ----
// ---- file   : StSampleVoice.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, 27Jan2010, 25Feb2010, 28Feb2010, 01Mar2010, 11Apr2010
// ----          29Apr2010, 04May2010, 28Jun2010, 29Jun2010, 19Jul2010, 04Sep2010, 05Sep2010
// ----          09Sep2010, 05Mar2011, 10Mar2011, 28Mar2011, 29Mar2011, 09May2013, 13Sep2015
// ----          19Dec2018, 29Dec2018, 04Jan2019, 07Jan2019, 08Jan2019, 17Jan2019, 24Mar2019
// ----          24Mar2019, 30Mar2019, 31Mar2019, 11May2019, 12May2019, 13May2019, 15May2019
// ----          16May2019
// ----
// ----
// ----

#ifndef __ST_SAMPLEVOICE_H__
#define __ST_SAMPLEVOICE_H__

YG("tksampler")


class StSamplePlayer;

/// When defined, use crossfade buffer.
/// When undefined, use last frame interpolation
#define USE_SAMPLESHIFT_EOL_XFADE_BUF  defined

/// When defined, use xfade buffer if loop length is smaller than ramp size
#define USE_SAMPLESHIFT_EOL_XFADE_BUF_THRESHOLD  defined


class StSampleVoiceNoteOnParams {
   // Used for delayed note-ons (if voice is still playing-> fadeout first)
  public:
   sBool b_valid;

   StSample       *_sample;
   YAC_FloatArray *_freqTable;

   sF32 _mixRate;
   sSI  _note;

   // Initial ctls/params
   sF32 _vel;
   sF32 _mod;
   sF32 _vol;
   sF32 _vol2;        // randomization
   sF32 _pan;
   sF32 _pan2;        // randomization
   sF32 _freq;
   sF32 _freq2;       // randomization
   sF32 _timestretch;
   sF32 _timestretch_bend;
   sF32 _sampleoff;
   sF32 _sampleoff2;  // randomization
   sF32 _cyclelen;
   sF32 _sampleshift;
   sF32 _sampleshift_endofloop;
   sF32 _altvolume;
   sUI  _retrigmask;
   sF32 _lfofreqspd;
   sF32 _lfofreqlvl;
   sF32 _lfovolspd;
   sF32 _lfovollvl;
   sF32 _lfopanspd;
   sF32 _lfopanlvl;
   sF32 _lfoauxspd;
   sF32 _lfoauxlvl;
   sF32 _adsrfreqspd;
   sF32 _adsrfreqlvl;
   sF32 _adsrfreqint;
   sF32 _adsrvolspd;
   sF32 _adsrvollvl;
   sF32 _adsrvolint;
   sF32 _adsrpanspd;
   sF32 _adsrpanlvl;
   sF32 _adsrpanint;
   sF32 _adsrauxspd;
   sF32 _adsrauxlvl;
   sF32 _adsrauxint;
   sSI  _jumptoloop;
   sSI  _waveguideidx;
   sF32 _afreqspd;
   sF32 _sfreqspd;
   sF32 _rfreqspd;
   sF32 _avolspd;
   sF32 _svolspd;
   sF32 _rvolspd;
   sF32 _apanspd;
   sF32 _spanspd;
   sF32 _rpanspd;
   sF32 _aauxspd;
   sF32 _sauxspd;
   sF32 _rauxspd;
   sF32 _filter_cutoff;
   sF32 _filter_pan;
   sF32 _filter_offset;
   sF32 _filter_resonance;
   sBool _glide_switch;
   sF32  _glide_speed;
   sF32  _perfctl_poly_pressure;  // -1=use sampleplayer perfctl (channel pressure), 0..127 otherwise
};


//
// Biquad filter based on Robert Bristow-Johnson's Cookbook formulae for audio EQ biquad filter coefficients
//  <http://musicdsp.org/files/Audio-EQ-Cookbook.txt>
//
// and Tom St Denis' "biquad.c" public domain implemention 
//  http://musicdsp.org/files/biquad.c, <http://tomstdenis.home.dhs.org>
//
struct StSampleVoiceBiquad {
   // per channel 

   // Filter type
   enum {
      LPF  = 1,  // Low Pass
      HPF  = 2,  // High Pass
      BPF  = 3,  // Band Pass
      BRF  = 4,  // Notch */
   };

   struct {
      sF32 x1, x2;  // input delayed by 1/2 samples
      sF32 y1, y2;  // output delayed by 1/2 samples
   } state;

   inline void reset(void) {
      state.x1 = state.x2 = 0.0f;
      state.y1 = state.y2 = 0.0f;
   }

   struct {
      sF32 a0, a1, a2, a3, a4;
   } biquad;

   sF32 bandwidth, qScale;  // calc'd in calcQ()

   void calcQ(sF32 &freq, sF32 &bandwidth, sF32 &qScale, sF32 q);

   void calcCoeff(sSI type, 
                  sF32 dbGain,   // gain of filter
                  sF32 freq,     // center frequency
                  sF32 srate,    // sampling rate
                  sF32 bandwidth // bandwidth in octaves
                  );

   // calls calcQ() and calcCoeff()
   void calcParams (sSI _type,
                    sF32 _dbGain,
                    sF32 _freq,   // 0..1
                    sF32 _res,    // 0..1
                    sF32 _srate
                    );

   sF32 filter (const sF32 _inSmp);
};


// based on http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
//
//  Created by Nigel Redmon on 11/24/12
//  EarLevel Engineering: earlevel.com
//  Copyright 2012 Nigel Redmon
//
//  For a complete explanation of the Biquad code:
//  http://www.earlevel.com/main/2012/11/26/biquad-c-source-code/
//
//  License:
//
//  This source code is provided as is, without warranty.
//  You may copy and distribute verbatim copies of this document.
//  You may modify and use this source code to create binary code
//  for your own purposes, free or commercial.
//
struct StSampleVoiceBiquad2 {
   // per channel 

   // Filter type
   enum {
      LPF  = 1,  // Low Pass
      HPF  = 2,  // High Pass
      BPF  = 3,  // Band Pass
      BRF  = 4,  // Band Reject / Notch
      PEQ  = 5,  // Parametric EQ
      LSH  = 6,  // Low Shelf
      HSH  = 7   // High Shelf
   };

   sF64 x1, x2;  // input delayed by 1/2 samples
   sF64 y1, y2;  // output delayed by 1/2 samples
   sF64 z1, z2;

   inline void reset(void) {
      x1 = x2 = 0.0f;
      y1 = y2 = 0.0f;
      z1 = z2 = 0.0;

      a0 = 1.0;
      a1 = a2 = b1 = b2 = 0.0;

      cur_a0 = 1.0;
      cur_a1 = cur_a2 = cur_b1 = cur_b2 = 0.0;

      step_a0 = 1.0;
      step_a1 = step_a2 = step_b1 = step_b2 = 0.0;
   }

   sF64 a0, a1, a2, b1, b2;
   sF64 cur_a0, cur_a1, cur_a2, cur_b1, cur_b2;
   sF64 step_a0, step_a1, step_a2, step_b1, step_b2;

   void calcParams (sF32 _blkSz,
                    sSI _type,
                    sF32 _dbGain,
                    sF32 _freq,   // 0..1
                    sF32 _res     // 0..1
                    );

   sF32 filter (const sF32 _inSmp);
};



/* @class StSampleVoice,Object
 *  
 *  The replayer (linearly) interpolates frequency/volume and panning across a "sample block".
 *  Samples are also interpolated linearly.
 *  Volume ramping can be done via the A(DS)R envelopes.
 *  
 *  Updated vol/pan/freq modulation info is read when a new block is started (copied from "queued" shadow members).
 *  The default block size/length is STSAMPLEVOICE_BLOCK_LEN seconds, or 44.1 samples @44100Hz mixrate.
 *
 *  The update mechanism is not "sample realtime" but behaves more like a hardware MIDI/USB sampler. 
 *  The latency depends on the output buffer size. 
 *  I recommend 256 samples @ 48Khz, i.e. 5 millisec latency. This should be fast enough for LFOs and arpeggiators..
 *  (also see the tkportaudio plugin for low latency audio I/O via Steinberg's ASIO2 API on Win32)
 *
 *  The voice is marked inactive when 
 *    a) The end of the sample has been reached and there are no more loops
 *    b) The volume release envelope finishes (i.e. some time after a note-off or immediately if there is no vol. envelope)
 *
 *  The current replay note+finetune is mapped via a user-defined frequency table (can be changed per-voice).
 *
 *  Reverse play and "scratch" fx currently only work with loop samples (i.e. the whole sample will be looped).
 */
YC class StSampleVoice : public YAC_Object {

// number of ticks per sec => tickduration/blocklen is 
//     0.0005f sec @ 2000 ticks / 48KHz samplerate
//  or 0.0010f sec @ 1000 ticks
//  or 0.0020f sec @ 500 ticks
#define STSAMPLEVOICE_TICKSPERSEC YCF 1000.0f 

// in sec., determines e.g. envelope resolution (0.001= 1 millisec)
#define STSAMPLEVOICE_BLOCK_LEN YCF (1.0f/STSAMPLEVOICE_TICKSPERSEC)

// Number of samples to fade in/out when switching loop cycles
//  higher values=increased latency but lower freq. clicks
// (todo) should this be dependent on the mixrate (256 = tuned for 44.1kHz)
#define STSAMPLEVOICE_VOLRAMP_NUMSTEPS 256

#define STSAMPLEPLAYER_PERFCTL_PITCHBEND        YCI  0
#define STSAMPLEPLAYER_PERFCTL_PRESSURE         YCI  1
#define STSAMPLEPLAYER_PERFCTL_CC1_MODWHEEL     YCI  2
#define STSAMPLEPLAYER_PERFCTL_CC2_BREATHCTL    YCI  3
#define STSAMPLEPLAYER_PERFCTL_CC4_FOOTCTL      YCI  4
#define STSAMPLEPLAYER_PERFCTL_CC7_VOLUME       YCI  5
#define STSAMPLEPLAYER_PERFCTL_CC8_BALANCE      YCI  6
#define STSAMPLEPLAYER_PERFCTL_CC10_PAN         YCI  7
#define STSAMPLEPLAYER_PERFCTL_CC11_EXPRESSION  YCI  8
#define STSAMPLEPLAYER_PERFCTL_CC16_GENERAL_1   YCI  9
#define STSAMPLEPLAYER_PERFCTL_CC17_GENERAL_2   YCI 10
#define STSAMPLEPLAYER_PERFCTL_CC18_GENERAL_3   YCI 11
#define STSAMPLEPLAYER_PERFCTL_CC19_GENERAL_4   YCI 12
#define STSAMPLEPLAYER_PERFCTL_CC74_BRIGHTNESS  YCI 13
#define STSAMPLEPLAYER_NUM_PERFCTL              YCI 14

  public:
   StSample *sample; // ref, currently playing sample
   YAC_FloatArray *freq_table; // ref, used to convert MIDI note to Hz
   StSamplePlayer *sample_player; // set after voice allocation in StSamplePlayer

   // Same format as sample->sample_loops but overrides these
   //  (originally added for Synergy replay)
   YAC_IntArray *override_sample_loops;

   sBool b_used;      // 1=voice is currently active, 0=voice may still be retriggered but is free for allocation
   sBool b_playing;   // 1:voice is active and initialized/playing, 0=voice -may- be active or unused
   sBool b_release;   // 1=voice is in release phase (after note-off)
   sBool b_alloc;     // 1=voice has just been allocated (valid for current tick)
   sBool b_glide;     // 1=voice has been allocated via allocSampleVoiceGlide() (=> apply retrig flags in initStartedVoicesByKey())

   sUI voice_key;     // used to identify voices after (samplebank-) noteon. >0: voice is active. assigned in prepareToPlay()
   sUI voice_idx;     // index into StSamplePlayer voices array
   sUI replay_ticks;  // replay time, number of blocks (default block size= 1 millisec)

   sF32 float_block_off;      // subprecision correction factor
   sF32 float_block_size;     // # samples, depends on mixrate and STSAMPLEVOICE_BLOCK_LEN
   sUI  current_block_size;   // # samples, depends on mixrate and STSAMPLEVOICE_BLOCK_LEN
   sUI  current_block_offset; // # samples

   sSI  current_delay_countdown; // #samples
   sF64 current_play_offset; // 0..current_sample_len
   sF32 last_relsmpoff; // used to calculate current_play_offset, see mod_sampleoff
   sUI  current_sample_offset;
   sUI  current_sample_len;  // calc'd each frame, modulated by mod_cyclelen if granular mode is disabled
   sUI  current_orig_sample_len;
   sSI  current_loop_idx;
   sSI  current_loop_countdown; // 0=inf
   sSI samples_until_end_of_loopstep; // if loopms=1
   sBool b_timedloop; // sample->b_timedloop + sample_len must be 0 + sample_loops must be available
                      //  (note) this is reset to 0 to make handleEndOfLoop() correctly handle endless repeat
   sF32 timedloop_base; // multiply loop repeatcount by 1sec/timedloop_base. requires b_loopms=1. Default is 1000.0f (=> 1millisec)

   sSI fadein_countdown; // used if b_timedloop=1, fadein new loop
   sSI fadeout_countdown; // used if b_timedloop=1, fadeout current loop
   sBool b_zero;
   sBool b_fading_out;
   sBool b_restartfadein; // true after voice restart to avoid clicks

   sSI  num_sample_loops;

   sF32 mod_timestretch;
   sF32 mmdst_timestretch;
   sF32 mod_timestretch_bend;
   sF32 mmdst_timestretch_bend;  // added to sample->timestretch_bend
   sF32 current_ts_offset;
   sF32 mod_sampleoff;      // offset, 0..1 (==samplelen)
   sF32 mod_sampleoff2;     // offset from randomization and velocity (0..1)
   sF32 mmdst_sampleoff;    // from modmatrix
   sF32 mod_cyclelen;
   sF32 mmdst_cyclelen;     // from modmatrix
   sF32 mod_sampleshift;    // additional offset, 0..(orig_samplelen-sampleoff)
   sF32 mmdst_sampleshift;  // from modmatrix
   sF32 mod_sampleshift_endofloop;    // copied to mod_sampleshift at end of loop. autoresets to -1.
   sF32 mmdst_sampleshift_endofloop_last;  // -1 initially, used to check if sampleshift_eol has really changed
   sF32 mod_altvolume;      // alternative volume

   sUI  samples_since_cycle;
   sF32 fragment_interpol;

   sF32 mod_vol;
   sF32 mod_vol2;    // volume randomization (see StSampleBank::volume_rand_amount)
   sF32 current_vol;
   sF32 next_vol;
   sF32 cached_vel_vol; // velocity amount

   sF32 mod_pan;
   sF32 mod_pan2;    // pan randomization (see StSampleBank::pan_rand_amount)
   sF32 current_pan;
   sF32 next_pan;

   sF32 mod_freq;   // e.g. pitch bend
   sF32 mod_freq2;  // pitch randomization (see StSampleBank::freq_rand_amount)
   sF32 current_freq; // offset to "note", in linear "MIDI-note" space
   sF32 next_freq; // offset to "note", in linear "MIDI-note" space

   sF32 mod_mod;

   sF32 perfctl_poly_pressure;  // -1=use sampleplayer perfctl (channel pressure), 0..127 otherwise
   sF32 perf_ctl_on[STSAMPLEPLAYER_NUM_PERFCTL];  // copied from SamplePlayer @note-on
   sF32 perfctl_pressure_max;

   sF32 mix_rate; // in Hz
   sSI note; // in "MIDI-note" space (0..127)
   sF32 release_velocity;

   StSampleVoiceNoteOnParams queued_noteon;
   sSI  volramp_fadeout_countdown;
   sF32 volramp_fade_vol;
   sSI  volramp_fadein_countdown;

   sUI  anticlick_fadeout_countdown;
   sUI  anticlick_granular_smpoffinterpol_countdown;
   sUI  anticlick_granular_smpoffinterpol_winoff;      // last winOff
   sUI  anticlick_granular_smpoffinterpol_nextwinoff;  // last winOffNext
   sF32 anticlick_granular_smpoffinterpol_tsoff;       // last current_ts_off
   sF32 anticlick_granular_smpoffinterpol_fragoff;     // last fragment_interpol
   sF32 last_sample_l;
   sF32 last_sample_r;
   sF32 last_sample_sampleshift_eol_l;
   sF32 last_sample_sampleshift_eol_r;
   sUI  sampleshift_eol_xfade_frames_left;
#ifdef USE_SAMPLESHIFT_EOL_XFADE_BUF
   sF32 sampleshift_eol_xfade_buf_l[STSAMPLEVOICE_VOLRAMP_NUMSTEPS];
   sF32 sampleshift_eol_xfade_buf_r[STSAMPLEVOICE_VOLRAMP_NUMSTEPS];
   sUI  sampleshift_eol_xfade_loopstart_frameidx;  // 0..STSAMPLEVOICE_VOLRAMP_NUMSTEPS-1
#endif // USE_SAMPLESHIFT_EOL_XFADE_BUF

   StLFOPlayer lfo_freq;
   StLFOPlayer lfo_vol;
   StLFOPlayer lfo_pan;
   StLFOPlayer lfo_aux;

   StADSRPlayer adsr_freq;
   StADSRPlayer adsr_vol;
   StADSRPlayer adsr_pan;
   StADSRPlayer adsr_aux;

   sUI  loop_vars[16];
   sSI  mod_jumptoloop; // -1 = no jump. auto-resets to -1.

   sUI waveguide_idx;        // current cycleindex (or index into waveguide_table, if available)
   sUI next_waveguide_idx;   // next cycleindex (or index into waveguide_table, if available)
   sUI queued_waveguide_idx; // queued cycleindex (or index into waveguide_table, if available)
   sUI waveguide_next_play_offset;

   sF32 current_aux_adsr;
   sF32 next_aux_adsr;

   sF32 current_aux_lfo;
   sF32 next_aux_lfo;

   sSI  filter_type;  // copied during note on
   sF32 mod_filter_cutoff;
   sF32 mod_filter_pan;
   sF32 mod_filter_offset;
   sF32 mod_filter_resonance;

   sBool mod_glide_switch;
   sF32  mod_glide_speed;
   sF32  mmdst_glide_speed;

   // (note) LPF cutoff calculation seems off (maxfreq cuts off too many highs)
   StSampleVoiceBiquad biquad_a_1[2/*channels*/];
   StSampleVoiceBiquad biquad_a_2[2/*channels*/];   // for dual-filter configuration (LP+HP)

   StSampleVoiceBiquad2 biquad_b_1[2/*channels*/];
   StSampleVoiceBiquad2 biquad_b_2[2/*channels*/];   // for dual-filter configuration (LP+HP)

   sF32 glide_src_note;  // modified in STSAMPLE_GLIDE_FREQ mode, constant in TIME mode
   sF32 glide_src_note_orig;
   sF32 glide_time;      // 0..1, increases @1000Hz rate in STSAMPLE_GLIDE_TIME mode

   sF32 noteon_rand_pm[4];
   sF32 noteon_rand_p[4];

   sUI active_note_idx;

   sF32 modmatrix_amt[STSAMPLE_NUM_MODMATRIX_ENTRIES];

  public:
   // Get current interpolated glide note
   sF32 getCurrentGlideNote (void);

  protected:
   void tickGlide (void);  // called in handleNextBlock()

   void filterLastSample (void);

   void renderBlockTimestretch(sF32 *     buf,
                               sUI        blkSz,
                               sF32 *     smpDat,
                               const sF32 a,
                               const sF32 b,
                               sF32       cVolL,
                               sF32       cVolR,
                               const sF32 sVolL,
                               const sF32 sVolR
                               );

   sUI  renderBlockNormal(sF32 *     buf,
                          sUI        blkSz,
                          sF32 *     smpDat,
                          const sF32 a,
                          const sF32 b,
                          sF32       cVolL,
                          sF32       cVolR,
                          const sF32 sVolL,
                          const sF32 sVolR
                          );

   sUI renderFragmentOptFwdMonoSimple(sF32 *&    buf,
                                      const sUI  _fragSize,
                                      sUI &      genSizeHint,
                                      sF32 *&    smpDat,
                                      sF32 &     cVolL,
                                      sF32 &     cVolR,
                                      sF32 &     cRate
                                      );

   sUI renderFragmentGeneric(sF32 *&    buf,
                             const sUI  _fragSize,
                             sF32 *&    smpDat,
                             sF32 &     cVolL,
                             sF32 &     cVolR,
                             const sF32 sVolL,
                             const sF32 sVolR,
                             sF32 &     cRate,
                             const sF32 sRate
                             );

   sF32 current_cyclelen; // calc'd at block start
   void calcCurrentCycleLen (sF32 &ts);

   void calcCurrentSampleLen (void);
   void alignSampleLenToCyclelen (sUI &_sampleLen);
   void calcCurrentOffset (void);

   void calcSmpDat(sF32*&_smpDat,
                   const sUI _curSampleOffset, 
                   const sUI _curOrigSampleLen, 
                   const sUI _curSampleLen
                   );

   void reallyStartVoice(const StSampleVoiceNoteOnParams *_params,
                         /* StSample *_sample,  */
                         /* YAC_FloatArray *_freqTable,  */
                         /* sF32 _mixRate,  */
                         /* sSI _note,  */
                         /* sF32 _vel,  */
                         /* sF32 _mod,  */
                         /* sF32 _vol,  */
                         /* sF32 _pan,  */
                         /* sF32 _freq,  */
                         /* sF32 _timestretch,  */
                         /* sF32 _sampleoff, */
                         sBool _bDelayed
                         );

   sUI handleNextBlock(void);

   void startADSRAndLFO(sUI _mask, sBool _bNoteOn);

   void calcFragmentInterpolPhase(sF32 _phase, sF32 _width);
   sF32 getEffectiveMod(void); // voice+sampleplayer, clipped to 0..1 range
   void applyFragmentInterpol(void);

   YAC_IntArray *getCurrentSampleLoops(void);


  public:
   StSampleVoice(void);
   ~StSampleVoice();

   YAC(StSampleVoice);

   sBool isAllocated (void);
   sBool canAllocate (void);
   sBool isSoftStopped (void);

   void initStartedVoice (void);

   void prepareToPlay (StSamplePlayer *_samplePlayer, StSample *_sample, sSI _voiceKey); // called after voicealloc

   sUI renderBlock (sF32 *buf, sUI blkSz, sF32 a, sF32 b, sF32 _volScale);

   sBool handleEndOfLoop (sF64 *cOff, sF32**smpDat);

   void calcTSWindowSin (sUI _len);
   void calcTSWindowTri (sUI _len);

   void readWindowedCycleSample (const sF32 *smpDat, sUI _off, sF32 _tsOff, sF32 *_l, sF32 *_r, const sUI _curSampleLen);
   void readWindowedSample (const sF32 *smpDat, sF32 _off, sF32 *_l, sF32 *_r, sBool _bAllowInterpol, const sUI _curSampleLen);

   // Immediately stop playing
   YM void _resetVoice (void);

   void resetBiquad (void);


   /* @method getVoiceIndex:int
    Query index of sample voice in sample player voice array
   */
   YM sUI getVoiceIndex (void);


   /* @method init,StSample sample,FloatArray freqTable,float mixRate,int note
      Initialize sample voice.

      @arg sample - Which sample to play
      @arg freqTable - Used to convert MIDI note number to frequency (Hz). At least 128 entries.
      @arg note - Which MIDI note to play (fraction is used for detune)
      @arg mod - Initial modulation
      @arg vol - Initial volume modulation
      @arg pan - Initial pan modulation
      @arg freq - Initial frequency modulation
   */
   YM void _startVoice (YAC_Object *_sample, YAC_Object *_freqTable, sF32 _mixRate, sSI _note, sF32 _vel, sF32 _mod, sF32 _vol, sF32 _pan, sF32 _freq);

   /* @method restartVoice
      Reset currently playing voice
   */
   YM void _restartVoice (void);


   /* @method stopVoice
      Mark voice inactive (no fadeout)
   */
   YM void stopVoice (void);


   /* @method softStopVoice
      Fadeout voice and stop it
   */
   YM void softStopVoice (void);

   
   /* @method noteOff,float velocity
      Start release phase

      If there is no release envelope, the voice is stopped immediately

      @arg velocity Note-off velocity (0..1)
   */
   YM void noteOff (sF32 _velocity);


   /* @method noteToFreq,float note:float
      Map MIDI note to frequency (Hz) using currently set freq_table.

      @arg note MIDI note number (fraction for detune)
      @return Frequency (Hz)
   */
   YM sF32 noteToFreq (sF32 _note);

   sF32 noteToSampleRate (sF32 _note);

   // convert normalized cutoff note (0..1) to linear freq (0..1 => 0..max_note_hz) via freq_table
   sF32 normToCutOffFreq (sF32 _norm);

   void calcNextBlockState (void);


   /* @method setVol,float vol
      Set volume modulation
   */
   YM void _setVol (sF32 _vol);


   /* @method getVol:float
      Return volume modulation
   */
   YM sF32 _getVol (void);


   /* @method setVol2,float vol
      Set volume modulation 2 (randomization)
   */
   YM void _setVol2 (sF32 _vol);


   /* @method getVol2:float
      Return volume modulation (randomization)
   */
   YM sF32 _getVol2 (void);


   /* @method setPan,float pan
      Set panning modulation
   */
   YM void _setPan (sF32 _pan);


   /* @method getPan:float
      Return panning modulation
   */
   YM sF32 _getPan (void);


   /* @method setPan2,float pan
      Set panning modulation (randomization)
   */
   YM void _setPan2 (sF32 _pan);


   /* @method getPan2:float
      Return panning modulation (randomization)
   */
   YM sF32 _getPan2 (void);


   /* @method setFreq,float freq
      Set frequency modulation
   */
   YM void _setFreq (sF32 _freq);


   /* @method getFreq:float
      Return frequency modulation
   */
   YM sF32 _getFreq (void);


   /* @method setFreq2,float freq
      Set frequency modulation 2 (additive, randomization)
   */
   YM void _setFreq2 (sF32 _freq);


   /* @method getFreq2:float
      Return frequency modulation
   */
   YM sF32 _getFreq2 (void);


   /* @method setMod,float mod
      Set modulation

      This only has an effect when there is a mod range defined
   */
   YM void _setMod (sF32 _mod);


   /* @method getMod:float
      Return modulation
   */
   YM sF32 _getMod (void);


   /* @method setTimestretch,float ts
      Set timestretch modulation

      This only has an effect when timestretching is enabled
   */
   YM void _setTimestretch (sF32 _ts);


   /* @method getTimestretch:float
      Return timestretch modulation
   */
   YM sF32 _getTimestretch (void);


   /* @method setTimestretchBend,float ts
      Set timestretch phase bend modulation

      This only has an effect when timestretching is enabled
   */
   YM void _setTimestretchBend (sF32 _ts);


   /* @method getTimestretchBend:float
      Return timestretch phase bend modulation
   */
   YM sF32 _getTimestretchBend (void);


   /* @method setSampleOff,float so
      Set sample offset modulation

   */
   YM void _setSampleOff (sF32 _so);


   /* @method getSampleOff:float
      Return sample offset modulation
   */
   YM sF32 _getSampleOff (void);


   /* @method setSampleOff2,float so
      Set sample offset modulation (randomization)

   */
   YM void _setSampleOff2 (sF32 _so);


   /* @method getSampleOff2:float
      Return sample offset modulation (randomization)
   */
   YM sF32 _getSampleOff2 (void);


   /* @method setCycleLen,float so
      Set granular cycle length modulation

   */
   YM void _setCycleLen (sF32 _cl);


   /* @method getCycleLen:float
      Return sample cycle length modulation
   */
   YM sF32 _getCycleLen (void);


   /* @method setSampleShift,float sh
      Set sample shift modulation

   */
   YM void _setSampleShift (sF32 _sh);


   /* @method getSampleShift:float
      Return sample shift modulation
   */
   YM sF32 _getSampleShift (void);


   /* @method setSampleShiftEndOfLoop,float sh
      Set sample shift modulation (queued until end of loop)

   */
   YM void _setSampleShiftEndOfLoop (sF32 _sh);


   /* @method getSampleShiftEndOfLoop:float
      Return sample shift modulation (queued until end of loop)
   */
   YM sF32 _getSampleShiftEndOfLoop (void);


   /* @method setAltVolume,float av
      Set alternative volume modulation

   */
   YM void _setAltVolume (sF32 _av);


   /* @method getAltVolume:float
      Return alternative volume modulation
   */
   YM sF32 _getAltVolume (void);


   /* @method retrigEnvLFO,int mask
      Retrigger envelopes/LFOs

   */
   YM void _setRetrigMask (sUI _mask);


   /* @method setTimedLoopBase,float base
      Set timedloop repeat frequency (1sec/base)

      This only has an effect when the sample uses timed loops.
    */
   YM void _setTimedLoopBase (sF32 _base);


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


   /* @method setLFOFreqSpd,float spd
      Set frequency LFO speed modifier
    */
   YM void _setLFOFreqSpd (sF32 _spd);


   /* @method setLFOFreqLvl,float lvl
      Set frequency LFO level modifier
    */
   YM void _setLFOFreqLvl (sF32 _lvl);


   /* @method setLFOVolSpd,float spd
      Set volume LFO speed modifier
    */
   YM void _setLFOVolSpd (sF32 _spd);


   /* @method setLFOVolLvl,float lvl
      Set volume LFO level modifier
    */
   YM void _setLFOVolLvl (sF32 _lvl);


   /* @method setLFOPanSpd,float spd
      Set pan LFO speed modifier
    */
   YM void _setLFOPanSpd (sF32 _spd);


   /* @method setLFOPanLvl,float lvl
      Set pan LFO level modifier
    */
   YM void _setLFOPanLvl (sF32 _lvl);


   /* @method setLFOAuxSpd,float spd
      Set aux LFO speed modifier
    */
   YM void _setLFOAuxSpd (sF32 _spd);


   /* @method setLFOAuxLvl,float lvl
      Set aux LFO level modifier
    */
   YM void _setLFOAuxLvl (sF32 _lvl);


   /* @method setADSRFreqSpd,float spd
      Set frequency ADSR speed modifier
    */
   YM void _setADSRFreqSpd (sF32 _spd);


   /* @method setADSRFreqLvl,float lvl
      Set frequency ADSR level modifier
    */
   YM void _setADSRFreqLvl (sF32 _lvl);


   /* @method setADSRFreqInt,float int
      Set frequency ADSR intensity modifier
    */
   YM void _setADSRFreqInt (sF32 _int);


   /* @method setADSRVolSpd,float spd
      Set volume ADSR speed modifier
    */
   YM void _setADSRVolSpd (sF32 _spd);


   /* @method setADSRVolLvl,float lvl
      Set volume ADSR level modifier
    */
   YM void _setADSRVolLvl (sF32 _lvl);


   /* @method setADSRVolInt,float int
      Set volume ADSR intensity modifier
    */
   YM void _setADSRVolInt (sF32 _int);


   /* @method setADSRPanSpd,float spd
      Set pan ADSR speed modifier
    */
   YM void _setADSRPanSpd (sF32 _spd);


   /* @method setADSRPanLvl,float lvl
      Set pan ADSR level modifier
    */
   YM void _setADSRPanLvl (sF32 _lvl);


   /* @method setADSRPanInt,float int
      Set pan ADSR intensity modifier
    */
   YM void _setADSRPanInt (sF32 _int);


   /* @method setADSRAuxSpd,float spd
      Set aux ADSR speed modifier
    */
   YM void _setADSRAuxSpd (sF32 _spd);


   /* @method setADSRAuxLvl,float lvl
      Set aux ADSR level modifier
    */
   YM void _setADSRAuxLvl (sF32 _lvl);


   /* @method setADSRAuxInt,float int
      Set aux ADSR intensity modifier
    */
   YM void _setADSRAuxInt (sF32 _int);


   /* @method setAFreqSpd,float int
      Set freq attack speed modifier
    */
   YM void _setAFreqSpd (sF32 _spd);


   /* @method setSFreqSpd,float int
      Set freq sustain speed modifier
    */
   YM void _setSFreqSpd (sF32 _spd);


   /* @method setRFreqSpd,float int
      Set freq release speed modifier
    */
   YM void _setRFreqSpd (sF32 _spd);


   /* @method setAVolSpd,float int
      Set volume attack speed modifier
    */
   YM void _setAVolSpd (sF32 _spd);


   /* @method setSVolSpd,float int
      Set volume sustain speed modifier
    */
   YM void _setSVolSpd (sF32 _spd);


   /* @method setRVolSpd,float int
      Set volume release speed modifier
    */
   YM void _setRVolSpd (sF32 _spd);


   /* @method setAPanSpd,float int
      Set pan attack speed modifier
    */
   YM void _setAPanSpd (sF32 _spd);


   /* @method setSPanSpd,float int
      Set pan sustain speed modifier
    */
   YM void _setSPanSpd (sF32 _spd);


   /* @method setRPanSpd,float int
      Set pan release speed modifier
    */
   YM void _setRPanSpd (sF32 _spd);


   /* @method setAAuxSpd,float int
      Set aux attack speed modifier
    */
   YM void _setAAuxSpd (sF32 _spd);


   /* @method setSAuxSpd,float int
      Set aux sustain speed modifier
    */
   YM void _setSAuxSpd (sF32 _spd);


   /* @method setRAuxSpd,float int
      Set aux release speed modifier
    */
   YM void _setRAuxSpd (sF32 _spd);


   /* @method setJumpToLoop,int idx
      Queue jump to loop
    */
   YM void _setJumpToLoop (sSI _int);


   /* @method setJumpToLoopRel,float relIdx
      Queue jump to loop (0..1 => 0..num_loops-1)
    */
   YM void _setJumpToLoopRel (sF32 _relIdx);


   /* @method setWaveguideIndex,int idx
      Queue next cycle
    */
   YM void _setWaveguideIndex (sSI _int);


   /* @method render,FloatArray buf
    */
   YM void _render (YAC_Object *_fa, sF32 _volScale); // "add" to stereo buffer


   /* @method PrintDebugStats
      Pseudo-static method that prints mixer codepath statistics (for debugging/optimization)
   */
   YM void _PrintDebugStats (void);


   YM sF32 FreqToNote (sF32 _freq, YAC_Object *_freqTable);

   YM void        _allocOverrideSampleLoops       (void);
   YM YAC_Object *_getOrCreateOverrideSampleLoops (void);
   YM YAC_Object *_getOverrideSampleLoops         (void);
   YM void        _freeOverrideSampleLoops        (void);
   YM sBool       _verifyOverrideSampleAreas      (void);


   /* @method setFilterCutOff,float f
      Set filter cutoff modifier
    */
   YM void _setFilterCutOff (sF32 _f);


   /* @method setFilterPan,float f
      Set filter stereo pan modifier
    */
   YM void _setFilterPan (sF32 _f);


   /* @method setFilterOffset,float f
      Set filter offset modifier
    */
   YM void _setFilterOffset (sF32 _f);


   /* @method setFilterResonance,float f
      Set filter resonance modifier
    */
   YM void _setFilterResonance (sF32 _f);


   /* @method setGlideSwitch,boolean bEnable
      Set glide switch
    */
   YM void _setGlideSwitch (sBool _bEnable);


   /* @method setGlideSpeed,float speed
      Set glide speed
    */
   YM void _setGlideSpeed (sF32 _speed);


   /* @method updatePerfCtlPolyPressure,float value
      Update poly pressure (0..127)
    */
   YM void _updatePerfCtlPolyPressure (sF32 _value);

};

#endif // __ST_SAMPLEVOICE_H__
