// -*-C++-*-
// This file is part of the gmod package
// Copyright (C) 1997 by Andrew J. Robinson

#ifndef VOICE_H
#define VOICE_H

#include <limits.h>

#ifdef USE_LOCAL
#include "soundcard.h"
#else
#include <sys/soundcard.h>
#endif

#include "defines.h"

#define VOL_CHANGED 0x01
#define NOTE_CHANGED 0x02
#define PAN_CHANGED 0x04
#define KEY_CHANGED 0x10
#define BEND_CHANGED 0x20
#define OFFSET_CHANGED 0x40
#define SAMPLE_CHANGED 0x80

#define TREMOR_VOL 0x01
#define SAMPLE_VOL 0x02
#define SLIDE_VOL 0x04

class Sample;
struct songInfo;

class Voice
{
public:
  Voice() : 
    arpegBend_(0),
    arpegNum_(0),
    channel_(0),
    cutCount_(0),
    delayCount_(0),
    envelopePan_(0),
    envelopeVol_(0),
    finetune_(0),
    fineVol_(0),
    flagVol_(SAMPLE_VOL),
    glissando_(0),
    globalVolSlide_(0),
    inPanSustain_(0),
    inVolumeSustain_(0),
    lastBend_(INT_MAX),
    lastNote_(0),
    lastRate_(0),
    lastVol_(INT_MAX),
    mainVolume_(255),
    noteDelayed_(0),
    offset_(0),
    pan_(0),
    panEnvelopePos_(0),
    panFactor_(100),
    panSlide_(0),
    pitchbender_(0),
    retrigger_(0),
    //retrigVol_(0),
    sample_(0),
    slideDir_(0),
    slideGoal_(0),
    slidePeriod_(0),
    slidePeriodGoal_(0),
    slidePitch_(0),
    slideRate_(0),
    tremolo_(0),
    tremoloDepth_(0),
    tremoloOld_(0),
    tremoloPosition_(0),
    tremoloVol_(0),
    tremoloWave_(0),
    tremor_(0),
    vibraBend_(0),
    vibraDepth_(0),
    vibraOldRate_(0),
    vibraPosition_(0),
    vibraRate_(0),
    vibraWave_(0),
    volSlide_(0),
    volType_(0),
    volume_(0),
    volumeEnvelopePos_(0),
    whatChanged_(VOL_CHANGED | PAN_CHANGED | BEND_CHANGED)
  { channel_ = numVoices_++; };
  virtual ~Voice() { numVoices_--; };

  virtual void doUpdates(int) = 0;

  void arpeg(int);
  void cutNote(int parm) { cutCount_ = parm; }
  void delayNote(int amt) { delayCount_ = amt; }
  int doTick(int, int, songInfo *);
  void finetune(int);
  void fineVol(int);
  void glissando(int parm) { glissando_ = parm; }
  void globalVolSlide(int amt) { globalVolSlide_ = amt; }
  void init(int, const Sample *);
  void keyOff();
  void mainVolume(int);
  void note(int, int);
  void offset(int pos);
  void pan(int);
  void panFactor(int factor) { panFactor_ = factor; }
  void panSlide(int amt) { panSlide_ = amt; }
  void portAndVol(int parm);
  void resetEffects(int);
  void retrigger(int p, int v) { retrigger_ = p; retrigVol_ = v; }
  void sample(const Sample *);
  void setEnvelopePos(int);
  void slideTo(int, int, int, int, int);
  void tremolo(int);
  void tremoloWave(int);
  void tremor(int);
  void vibrato(int);
  void vibratoSpeed(int speed) { vibraOldRate_ = speed; }
  void vibratoWave(int wave);
  void volType(int type) { volType_ = type; }
  void volSlide(int);
  void volume(int);

protected:
  virtual int doPreUpdates(int);
  virtual void startNote(int, int);

  void envelopePan(int);
  void envelopeVol(int vol);

  static unsigned char numVoices_;
  static short vibraTable[][NUM_VIBRA];

  unsigned char arpegCurr_;
  short arpegBend_;
  short arpegNote_[3];
  unsigned char arpegNum_;
  unsigned char channel_;
  unsigned char cutCount_;
  unsigned char delayCount_;
  unsigned char envelopePan_; // 0..64
  unsigned char envelopeVol_; // 0..64
  unsigned short fadeVol_;
  short finetune_;
  unsigned char fineVol_;
  unsigned char flagVol_;
  unsigned char glissando_;
  signed char globalVolSlide_;
  char inPanSustain_;
  char inVolumeSustain_;
  int lastBend_;
  unsigned char lastNote_;
  short lastRate_;
  int lastVol_;
  unsigned char mainVolume_; // 0..255
  unsigned char note_;
  unsigned char noteDelayed_;
  int offset_;
  unsigned char pan_;  // 0..255
  int panEnvelopePos_;
  signed char panFactor_;
  signed char panSlide_;
  short pitchbender_;
  unsigned char retrigger_;
  unsigned char retrigVol_;
  const Sample *sample_;
  unsigned char slideDir_;
  short slideGoal_;
  int slidePeriod_;
  int slidePeriodGoal_;
  unsigned char slidePitch_;
  int slideRate_;
  unsigned char tremolo_;
  unsigned char tremoloDepth_;
  unsigned char tremoloOld_;
  unsigned char tremoloPosition_;
  signed char tremoloVol_; // -128..127 (adjustment)
  unsigned char tremoloWave_;
  unsigned char tremor_;
  unsigned char tremTotal_;
  short vibraBend_;
  unsigned char vibraDepth_;
  unsigned char vibraOldRate_;
  unsigned char vibraPosition_;
  unsigned char vibraRate_;
  unsigned char vibraWave_;
  signed char volSlide_;
  unsigned char volType_;
  unsigned char volume_; // 0..255
  int volumeEnvelopePos_;
  unsigned char whatChanged_;
};

inline void
Voice::arpeg(int amount)
{
  arpegNum_ = 3;
  arpegCurr_ = 0;
  arpegNote_[0] = 0;
  arpegNote_[1] = ((amount >> 4) & 0x0f) * 100;
  arpegNote_[2] = (amount & 0x0f) * 100;
}

inline void
Voice::envelopePan(int pan)
{
  if (envelopePan_ != pan)
    {
      envelopePan_ = pan;
      whatChanged_ |= PAN_CHANGED;
    }
}

inline void
Voice::envelopeVol(int vol)
{
  if (envelopeVol_ != vol)
    {
      envelopeVol_ = vol;
      whatChanged_ |= VOL_CHANGED;
    }
}

inline void
Voice::finetune(int parm)
{
  if (finetune_ != parm)
    {
      finetune_ = parm;
      whatChanged_ |= BEND_CHANGED;
    }
}

inline void
Voice::fineVol(int parm)
{
  volSlide_ = VOL_SLIDE_RATE * parm;
  fineVol_ = 1;
}

inline void
Voice::mainVolume(int vol)
{
  if (mainVolume_ != vol)
    {
      mainVolume_ = vol;
      whatChanged_ |= VOL_CHANGED;
    }
}

inline void
Voice::pan(int panVal)
{
  if (pan_ != panVal)
    {
      pan_ = panVal;
      whatChanged_ |= PAN_CHANGED;
    }
}

inline void
Voice::tremolo(int amount)
{
  tremolo_ = (amount >> 4) & 0x0f;
  
  if (!tremolo_)
    tremolo_ = tremoloOld_;
  else
    tremoloOld_ = tremolo_;

  if (amount &= 0x0f)
    tremoloDepth_ = amount;
}

inline void
Voice::tremoloWave(int parm)
{
  if ((parm & 0x03) == 0x03)
    parm -= 1;

  tremoloWave_ = parm;
}

inline void
Voice::tremor(int amount)
{
  tremor_ = (amount >> 4) & 0x0f;
  tremTotal_ = tremor_ + (amount & 0x0f);

  if (!tremTotal_)
    tremor_ = 0;
}

inline void
Voice::vibratoWave(int wave)
{
  if ((wave & 0x03) == 0x03)
    wave--;

  vibraWave_ = wave;
}

inline void
Voice::volSlide(int parm)
{
  if (parm & 0xf0)
    volSlide_ = VOL_SLIDE_RATE * ((parm >> 4) & 0x0f);
  else
    volSlide_ = -VOL_SLIDE_RATE * (parm & 0x0f);

  fineVol_ = 0;
}

inline void
Voice::portAndVol(int parm)
{
  volSlide(parm);
  slidePitch_ = SLIDE_PORT;  // this was 1, but I think SLIDE_PORT is right
}

inline void
Voice::volume(int vol)
{
  if (volume_ != vol)
    {
      volume_ = vol;
      whatChanged_ |= VOL_CHANGED;
    }
};

#endif
