#include <dos.h>
#include "iwdefs.h"
#include "iwprotos.h"
extern IWAVE iw;
//########################################################################
// FILE: iwvoice.c
//
// REMARKS: This file contains the definitions of all DDK drivers for the
//          Synthesizer section of the InterWave IC.
//
// UPDATE: 4/04/95
//########################################################################
//
// FUNCTION: IwaveStopVoice
//
// PROFILE: This function stops a voice's output. Note that this function
//          deactivates the voice completely to stop it from being
//          processed. This only works in enhanced mode and if your app
//          is a GF1-compatible app you could reduce code size by removing
//          the deactivate portion of this function.
//
//########################################################################
void IwaveStopVoice(BYTE voice)
{
 BYTE sxci;

   ENTER_CRITICAL;
	_poke(iw.svsr,voice);                /* select voice */
	_poke(iw.igidxr,_SVCI_RD);           /* select SVCI to read from */
	sxci=_peek(iw.i8dp);                 /* read SVCI */
	sxci&=~VC_ROLLOVER;                  /* turn rollover off */
	_poke(iw.igidxr,_SVCI_WR);           /* select SVCI to write to*/
	_poke(iw.i8dp,sxci);                 /* write SVCI */
	_poke(iw.igidxr,_SACI_RD);           /* select SACI to read from */
	sxci=_peek(iw.i8dp);                 /* read SACI */
	sxci&=~VC_IRQ_ENABLE;                /* disable IRQ: SACI[5]=0 */
	sxci|=(VOICE_STOPPED|VOICE_STOP);    /* stop voice: SACI[1:0]=(1,1)*/
	_poke(iw.igidxr,_SACI_WR);           /* select SACI to write to */
	_poke(iw.i8dp,sxci);                 /* write SACI */
	if (iw.smode==ENH_MODE)              /* If enhanced mode, de-activate voice */
	 {
		_poke(iw.igidxr,_SMSI_RD);
		sxci=_peek(iw.i8dp)|0x02;
		_poke(iw.igidxr,_SMSI_WR);
		_poke(iw.i8dp,sxci);
	 }
	LEAVE_CRITICAL;
}
//########################################################################
//
// FUNCTION: IwaveStopVolume
//
// PROFILE: This function stops a voice's volume looping.
//
//########################################################################
void IwaveStopVolume(BYTE voice)
{
 BYTE svci;

 ENTER_CRITICAL;
 _poke(iw.svsr,voice);                  /* select  voice */
 _poke(iw.igidxr,_SVCI_RD);             /* select SVCI to read */
 svci = _peek(iw.i8dp);
 svci|=(VOLUME_STOPPED|VOLUME_STOP);
 _poke(iw.igidxr,_SVCI_WR);             /* select SVCI to write */
 _poke(iw.i8dp,svci);                   /* turn it off */
 LEAVE_CRITICAL;
}
//########################################################################
//
//  FUNCTION: IwaveReadyVoice
//
//  PROFILE: This function programs a selected voice on the InterWave for
//           playback. It does NOT start the voice, it just gets it ready.
//           Set the looping characteristics of the voice via function
//           IwaveSetLoopMode. Note that this function will set up the
//           voice to move in the direction of decreasing address if
//           "begin" is greater than "end".
//
//########################################################################
BYTE
IwaveReadyVoice(BYTE voice, ADDRESS begin, ADDRESS end,
					 ADDRESS fetch, BYTE mode)
{
  BYTE saci;
//########################################################################
//  If the Start of the sample buffer is greater than the End
//  then swap them around and turn address decrementing on
//########################################################################

  if (begin>end)
	{
	  BYTE temp = begin;
	  begin = end;
	  end = temp;
	  mode|= VC_DIRECT;
	}
//########################################################################
// 16-bit data requires an address translation in GUS
// compatible mode.
//########################################################################
  if (mode&VC_DATA_WIDTH)
	{
	  fetch=IwaveAddrTrans(fetch);
	  begin=IwaveAddrTrans(begin);
	  end  =IwaveAddrTrans(end);
	}

  ENTER_CRITICAL;

  _poke(iw.svsr,voice);                     /* select voice */
  _poke(iw.igidxr,_SUAI_WR);
  _poke(iw.i8dp,(BYTE)((begin>>22)&0x03L)); /* select local mem bank */
  _poke(iw.igidxr,_SACI_RD);                /* select SACI for reading */
  saci = _peek(iw.i8dp)&0xBF;               /* read SACI */
  saci|=(mode&VC_DIRECT);
  _poke(iw.igidxr,_SACI_WR);                /* select SACI for writing */
  _poke(iw.i8dp,saci);

//##############################################################
// Set up Synth Address registers which point to the
// location in Local Memory where the Synth fetches data
// from. These registers are SAHI and SALI.
//##############################################################
  _poke(iw.igidxr,_SALI_WR);          /* select SALI (write) */
  _pokew(iw.i16dp,ADDR_LOW(fetch));
  _poke(iw.igidxr,_SAHI_WR);          /* select SAHI (write) */
  _pokew(iw.i16dp,ADDR_HIGH(fetch));

//##############################################################
// Set up Synth Address Start registers which point to a
// boundary address or start address of a sample buffer
// These registers are SASHI and SASLI.
//##############################################################

  _poke(iw.igidxr,_SASLI_WR);         /* select SASLI (write) */
  _pokew(iw.i16dp,ADDR_LOW(begin));
  _poke(iw.igidxr,_SASHI_WR);         /* select SASHI (write) */
  _pokew(iw.i16dp,ADDR_HIGH(begin));

//##############################################################
// Set up Synth Address End registers which point to a
// boundary address or end address of a sample buffer
// These registers are SAEHI and SAELI.
//##############################################################
  _poke(iw.igidxr,_SAELI_WR);         /* select SAELI (write) */
  _pokew(iw.i16dp,ADDR_LOW(end));
  _poke(iw.igidxr,_SAEHI_WR);         /* select SAEHI (write) */
  _pokew(iw.i16dp,ADDR_HIGH(end));

  LEAVE_CRITICAL;

  return(mode);
}
//#######################################################################
//
//  FUNCTION: IwaveStartVoice
//
//  PROFILE: This function actually sets the voice playing. It is assumed
//           that the characteristics of the voice have already been
//           programmed. SACI[1:0]=(0,0) for the voice to work. Note that
//           in the case of an enhanced mode application, you must activate
//           the voice at SMSI so it can be processed. If your app is
//           a GF1 mode app, you may drop this portion of the driver to
//           help make your code smaller.
//
//#######################################################################
void IwaveStartVoice(BYTE voice)
{
	BYTE reg;

	ENTER_CRITICAL;

	_poke(iw.svsr,voice);            /* select voice */
	_poke(iw.igidxr,_SACI_RD);       /* select SACI to read from */
	reg=_peek(iw.i8dp)&~(VOICE_STOPPED|VOICE_STOP);
	_poke(iw.igidxr,_SACI_WR);       /* select SACI to write to */
	_poke(iw.i8dp,reg);              /* this also clears IRQs */
	if (iw.smode==ENH_MODE)          /* If enhanced mode, activate voice */
	 {
		_poke(iw.igidxr,_SMSI_RD);
		reg=_peek(iw.i8dp)&~0x02;
		_poke(iw.igidxr,_SMSI_WR);
		_poke(iw.i8dp,reg);
	 }
	LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveReadVoice
//
//  PROFILE: This function actually returns the address from which a
//           playing voice is currently fetching data.
//
//#######################################################################
ADDRESS IwaveReadVoice(BYTE voice)
{
  DWORD logical=0L;
  BYTE mode;

  ENTER_CRITICAL;
  _poke(iw.svsr,voice);                        /* select voice */
  _poke(iw.igidxr,_SAHI_RD);                   /* select SAHI to read */
  logical=((DWORD)_peekw(iw.i16dp))<<7;        /* get high portion of addr. */
  _poke(iw.igidxr,_SALI_RD);                   /* select SALI to read */
  logical|=((DWORD)_peekw(iw.i16dp))>>9;       /* get low portion of addr. */
  _poke(iw.igidxr,_SACI_RD);                   /* select SACI to read */
  mode = _peek(iw.i8dp);
  _poke(iw.igidxr,_SUAI_RD);
  logical|=((DWORD)(_peek(iw.i8dp)&0x03))<<22; /* bits 23:22 */
  LEAVE_CRITICAL;
  return(IwaveRealAddr(logical,mode));
}
//#######################################################################
//
//  FUNCTION: IwaveSetVoicePlace
//
//  PROFILE: This function sets the address location in local memory
//           where a voice fetches data from to a new position. This can
//           be useful when one wishes to eliminate the contribution from
//           a voice into the output. All voices fetching locations are
//           summed into the output even if the voice is not running.
//           'Pops' in the audio may result if a voice's fetching position
//           is set to a location with a significant value.
//
//#######################################################################
void IwaveSetVoicePlace(BYTE voice, ADDRESS local)
{
	BYTE saci;

	ENTER_CRITICAL;

	_poke(iw.svsr,voice);          /* select voice */

	_poke(iw.igidxr,_SACI_RD);     /* select SACI to read */
	saci=_peek(iw.i8dp);

	if (saci&VC_DATA_WIDTH)
		 local=IwaveAddrTrans(local);

//#######################################################################
// Set up Synth Address registers which point to the
// location in Local Memory where the Synth fetches data
// from. These registers are SAHI and SALI.
//#######################################################################

  _poke(iw.igidxr,_SALI_WR);          /* select SALI (write) */
  _pokew(iw.i16dp,ADDR_LOW(local));
  _poke(iw.igidxr,_SAHI_WR);          /* select SAHI (write) */
  _pokew(iw.i16dp,ADDR_HIGH(local));

  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveSetVoiceEnd
//
//  PROFILE: This function sets a new end point in Local Memory for the
//           specified voice. When used in conjunction with
//           IwaveSetLoopMode to turn looping off, a sampled decay
//           can be implemented.
//
//#######################################################################
void IwaveSetVoiceEnd(BYTE voice, ADDRESS local)
{
	BYTE saci;

	ENTER_CRITICAL;
	_poke(iw.svsr,voice);          /* select voice */

	_poke(iw.igidxr,_SACI_RD);     /* select SACI to read */
	saci=_peek(iw.i8dp);

	if (saci&VC_DATA_WIDTH)
		 local=IwaveAddrTrans(local);

	saci&=~(VC_IRQ_PENDING|VOICE_STOPPED|VOICE_STOP);

//#######################################################################
//  Synth Address End registers indicate a boundary address.
//  The value must be greater than the value in the Address
//  Start registers.The Address End registers are SAEHI and
//  SAELI.
//#######################################################################

  _poke(iw.igidxr,_SAELI_WR);          /* select SAELI (write) */
  _pokew(iw.i16dp,ADDR_LOW(local));
  _poke(iw.igidxr,_SAEHI_WR);          /* select SAEHI (write) */
  _pokew(iw.i16dp,ADDR_HIGH(local));

  _poke(iw.igidxr,_SACI_WR);           /* select SACI to write to */
  _poke(iw.i8dp,saci);
  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveReadVolume
//
//  PROFILE: This function returns the current looping volume value from
//           register SVLI[15:4].
//
//#######################################################################
WORD IwaveReadVolume(BYTE voice)
{
  WORD volume;

  ENTER_CRITICAL;
  _poke(iw.svsr,voice);         /* select voice */
  _poke(iw.igidxr,_SVLI_RD);    /* select SVLI to read */
  volume = _peekw(iw.i16dp)>>4;  /* extract SVLI[15:4] */
  LEAVE_CRITICAL;
  return(volume);
}
//#######################################################################
//
//  FUNCTION: IwaveSetVolume
//
//  PROFILE: This function sets the current looping volume value in
//           register SVLI[15:4].
//
//#######################################################################
void  IwaveSetVolume(BYTE voice, WORD volume)
{
  ENTER_CRITICAL;
  _poke(iw.svsr,voice);       /* select voice */
  _poke(iw.igidxr,_SVLI_WR);  /* select SVLI to write to */
  _pokew(iw.i16dp,volume<<4); /* write SVLI[15:4] */
  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveRampVolume
//
//  PROFILE: This function ramps the volume on a specified voice.
//
//#######################################################################
void
IwaveRampVolume(BYTE voice,                 /* voice to use */
					 WORD start,                 /* start value (0-4095) */
					 WORD end,                   /* end value (0-4095) */
					 BYTE rate,                  /* ramp rate */
					 BYTE mode)                  /* volume mode (rollover)*/
{
	BYTE vmode;
	WORD begin;

	if (start==end)
		 return;

	mode&=~(VC_IRQ_PENDING|VOLUME_STOP|VOLUME_STOPPED);
	mode&=(VC_ROLLOVER|VC_DIRECT);
	begin = start;

	if (start>end)             /* end must be greater then start */
	  {
		 start=end;             /* swap values around */
		 end=begin;
		 mode|=VC_DIRECT;       /* decreasing volume */
	  }

	if (start<64)
		 start=64;

	if (end>4032)
		 end=4032;

	ENTER_CRITICAL;

	_poke(iw.svsr,voice);             /* select voice */
	_poke(iw.igidxr,_SVRI_WR);        /* select SVRI to write to */
	_poke(iw.i8dp,rate);              /* set volume rate */
	_poke(iw.igidxr,_SVSI_WR);        /* select SVSI to write to */
	_poke(iw.i8dp,(BYTE)(start>>4));
	_poke(iw.igidxr,_SVEI_WR);        /* select SVEI to write to */
	_poke(iw.i8dp,(BYTE)(end>>4));

	IwaveSetVolume(voice,begin);      /* set volume to the start volume */

	_poke(iw.igidxr,_SVCI_WR);        /* select SVCI to write to */
	vmode=_peek(iw.i8dp)|mode;
	_poke(iw.igidxr,_SVCI_WR);        /* select SVCI to write to */
	_poke(iw.i8dp,vmode);             /* start the ramp */

	LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveVoicePan
//
//  PROFILE: This function sets the stereo position of a voice. When
//           SMSI[5]=0 a GF1 compatible mode is in effect and SROI[11:8]
//           control the left and right stereo offsets. When SMSI[5]=1
//           SROI[15:4] and SLOI[15:4] will control the right and left
//           offsets respectively.
//
//#######################################################################
void IwaveVoicePan(BYTE voice,WORD right,WORD left)
{
  BYTE mode;

  ENTER_CRITICAL;
  _poke(iw.svsr,voice);           /* select voice */

  _poke(iw.igidxr,_SMSI_RD);      /* select SMSI to read from */
  mode=_peek(iw.i8dp);
  _poke(iw.igidxr,_SROI_WR);      /* select SROI to write to */
  if (mode&VOICE_OFFSET)          /* separate control for left and right */
  {
		_pokew(iw.i16dp,right<<4);  /* set right balance (SROI)*/
		_poke(iw.igidxr,_SLOI_WR);  /* select SLOI to write to */
		_pokew(iw.i16dp,left<<4);   /* set left balance */
  }
  else                            /* GF1 compatible mode */
		_poke(iw.i8dp,(BYTE)right); /* set right & left balance (SROI) */
  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveSynthGlobal
//
//  PROFILE: This function can be used to control the global mode of
//           operation of the Synth which affects all voices. You can
//           enable or disable one or more modes at the same time, but
//           you can not both enable and disable modes at the same time.
//           The following calls illustrate the usage of this function:
//
//           IwaveSynthGlobal(ENH_MODE,ON)   turns on enhanced mode
//           IwaveSynthGlobal(ENH_MODE|ENABLE_LFOS,ON) enables enhance mode
//           and all LFOS.
//           IwaveSynthGlobal(ENH_MODE,OFF) disables enhanced mode (enables
//           GF1 mode).
//
//#######################################################################
void IwaveSynthGlobal(BYTE mode,BOOL state)
{
  BYTE sgmi;

  ENTER_CRITICAL;

  _poke(iw.igidxr,_SGMI_RD);    /* select Synth Global Mode Reg. to read */
  sgmi=_peek(iw.i8dp);          /* read SGMI */
  _poke(iw.igidxr,_SGMI_WR);    /* select SGMI to write to */
  if (state==ON)                 /* enable bit(s) */
	  _poke(iw.i8dp,(BYTE)(sgmi|mode));
  else                           /* disable bit(s) */
	  _poke(iw.i8dp,(BYTE)(sgmi&~mode));

  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveSynthMode
//
//  PROFILE: This function can be used to switch the InterWave between GUS
//           compatible mode and enhanced mode. The DDK functions can detect
//           the mode of operation via iw.smode. The argument to this
//           function should be set to either GUS_MODE or ENH_MODE. Note
//           that if your application manages local memory via the DDK
//           memory management functions, switching modes invalidates the
//           current memory structure. You must initialize DRAM by calling
//           IwaveMemInit again.
//
//#######################################################################
void IwaveSynthMode(BYTE mode)
{
  BYTE sgmi;

  ENTER_CRITICAL;
  _poke(iw.igidxr,0x99);
  sgmi=_peek(iw.i8dp);
  _poke(iw.igidxr,0x19);
  if (mode==GUS_MODE)
		_poke(iw.i8dp,(BYTE)(sgmi&~ENH_MODE));
  else
		_poke(iw.i8dp,(BYTE)(sgmi|ENH_MODE));
  LEAVE_CRITICAL;
  iw.smode=mode;
}
//#######################################################################
//
//  FUNCTION: IwaveVoicePitch
//
//  PROFILE: This function sets the frequency of a the specified voice
//           into register SFCI. The contents of this register are
//           set based on the desired input frequency. SFCI is made up
//           of two parts: SFCI[15:10] is an integer part indicating
//           how many times faster than it was recorded we wish to play
//           the sample data. SFCI[9:0] is a fractional part that allows
//           more resolution for the frequency. SFCI[0] is not available
//           in GUS compatible mode.
//
//           Use this function if you intend to write your application to
//           to be an enhanced mode application. Otherwise, use function
//           IwaveVoiceFreq.
//#######################################################################
void IwaveVoicePitch(BYTE voice, DWORD freq)
{
  WORD fc;

  fc= (((freq<<10) + 22050)/44100)<<1;   /* 10 fractional bits in SFCI */

  ENTER_CRITICAL;
  _poke(iw.svsr,voice);         /* select voice */
  _poke(iw.igidxr,_SFCI_WR);    /* select Synth Freq Control register*/
  _pokew(iw.i16dp,fc);
  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveVoiceFreq
//
//  PROFILE: This function sets the frequency of a the specified voice
//           into register SFCI. The contents of this register are
//           set based on the desired input frequency. SFCI is made up
//           of two parts: SFCI[15:10] is an integer part indicating
//           how many times faster than it was recorded we wish to play
//           the sample data. SFCI[9:0] is a fractional part that allows
//           more resolution for the frequency. SFCI[0] is not available
//           in GUS compatible mode.
//
//           Use this function if you intend to write your application to
//           to be an GUS mode application. Otherwise, use function
//           IwaveVoicePitch. This will make your code smaller.
//#######################################################################
void IwaveVoiceFreq(BYTE voice, DWORD freq)
{
  WORD fc, fs;
  WORD freq_divisor[19] = {
	44100,		/* 14 active voices */
	41160,		/* 15 active voices */
	38587,		/* 16 active voices */
	36317,		/* 17 active voices */
	34300,		/* 18 active voices */
	32494,		/* 19 active voices */
	30870,		/* 20 active voices */
	29400,		/* 21 active voices */
	28063,		/* 22 active voices */
	26843,		/* 23 active voices */
	25725,		/* 24 active voices */
	24696,		/* 25 active voices */
	23746,		/* 26 active voices */
	22866,		/* 27 active voices */
	22050,		/* 28 active voices */
	21289,		/* 29 active voices */
	20580,		/* 30 active voices */
	19916,		/* 31 active voices */
	19293};		/* 32 active voices */

  fs = freq_divisor[iw.voices-14];
  fc= ((freq<<9 + (DWORD)(fs>>1))/fs)<<1; /* 9 fractional bits in SFCI */

	ENTER_CRITICAL;
  _poke(iw.svsr,voice);         			 /* select voice */
  _poke(iw.igidxr,_SFCI_WR);            /* select Synth Freq Control register*/
  _pokew(iw.i16dp,fc);
  LEAVE_CRITICAL;
}
//#######################################################################
//
//  FUNCTION: IwaveSetLoopMode
//
//  PROFILE: This function allows the caller to set up the looping mode
//           for both addressing and volume of a voice. You can also enable
//           address and volume boundary interrupts. The set up for a
//           voice is accomplished at registers SACI and SVCI. If you do
//           not enable a feature it will certainly be disabled by the call
//           to this function.
//
//#######################################################################
void IwaveSetLoopMode(BYTE voice, BYTE amode, BYTE vmode)
{
  BYTE reg;
  /* screen out bits that do not apply from inputs */
  amode&=(VC_BI_LOOP|VC_LOOP_ENABLE|VC_DIRECT|VC_IRQ_ENABLE|VC_DATA_WIDTH);
  vmode&=(VC_BI_LOOP|VC_LOOP_ENABLE|VC_DIRECT|VC_IRQ_ENABLE|VC_ROLLOVER);
  ENTER_CRITICAL;
  _poke(iw.svsr,voice);         /* select voice */
  _poke(iw.igidxr,_SACI_RD);    /* select Synth Addr Control register*/
  reg=_peek(iw.i8dp);
  reg&=~(VC_BI_LOOP|VC_LOOP_ENABLE|VC_DIRECT|VC_IRQ_ENABLE|VC_DATA_WIDTH);
  _poke(iw.igidxr,_SACI_WR);    /* select Synth Addr Control register*/
  _poke(iw.i8dp,(BYTE)(reg|amode));
  _poke(iw.igidxr,_SVCI_RD);    /* select Synth Volume Control register*/
  reg=_peek(iw.i8dp);
  reg&=~(VC_BI_LOOP|VC_LOOP_ENABLE|VC_DIRECT|VC_IRQ_ENABLE|VC_ROLLOVER);
  _poke(iw.igidxr,_SVCI_WR);    /* select Synth Volume Control register*/
  _poke(iw.i8dp,(BYTE)(reg|vmode));
  LEAVE_CRITICAL;
}
