Metropoli BBS
VIEWER: adlib.c MODE: TEXT (ASCII)
/**
	ADLIB.C, low level sound driver V1.03
	===== To be used with OUTCHIP.ASM file. =====

	Copyright Ad Lib Inc, 1988, 1989

	1988/06/22, Dale Glowinski, Marc Savary, Ad Lib Inc.
	1989/04/20, Marc savary.




	The following routines are public (see each routine for further	
	documentation):

		SoundColdInit( port)
		SoundWarmInit()
		SetMode( mode)
		Set3812( state)
		SetPitchRange( pR)
		SetGParam( amD, vibD, nSel)
		SetVoiceTimbre( voice, paramArray)
		SetVoiceVolume( voice, volume)
		SetVoicePitch( voice, pitchBend)
		NoteOn( voice, pitch)
		NoteOff( voice)

**/

#include "adlib.h"

/*
	In percussive mode, the last 4 voices ( SD TOM HH CYMB) are created
	using melodic voices 7 & 8. A noise generator use channels 7 & 8
	frequency information for creating rhythm instruments. Best result
	are obtained by setting TOM two octaves below mid C and SD 7 half-tones
	above TOM.
	In this implementation, only the TOM pitch may vary, with the SD always
	set 7 half-tones above.
*/

#define TOM_PITCH	24				/* best frequency, in range of 0 to 95 */
#define TOM_TO_SD	7				/* 7 half-tones between voice 7 & 8 */
#define SD_PITCH	(TOM_PITCH + TOM_TO_SD)

#define NR_STEP_PITCH 25			/* 25 steps within a half-tone for pitch bend */

#define GetLocPrm( slot, prm)( (unsigned)paramSlot[ slot][ prm])
#define HighByte( word) ( ((char *)(&word))[ 1])	/* 80x86-8 only .. */
#define RhythmMode() 	(percussion)

typedef char	SLOT_PARAM;


/*
-----------------------------------------------------------------
*/

unsigned genAddr;						/* address of sound chip */

static char percBits;					/* control bits of percussive voices */
static char percMasks[] = {
	0x10, 0x08, 0x04, 0x02, 0x01
	};

static char notePitch[ 11];			/* pitch of last note-on of each voice */
static char voiceKeyOn[ 11];			/* state of keyOn bit of each voice */

static char noteDIV12[ 96];			/* table of (0..95) DIV 12 */
static char noteMOD12[ 96];			/* table of (0..95) MOD 12 */

static char slotRelVolume[ 18];		/* relative volume of slots */


/* definition of the ELECTRIC-PIANO voice (opr0 & opr1) */
static char pianoParamsOp0[ nbLocParam] = {
	1, 1, 3, 15, 5, 0, 1, 3, 15, 0, 0, 0, 1, 0 };
static char pianoParamsOp1[ nbLocParam] = {
	0, 1, 1, 15, 7, 0, 2, 4, 0, 0, 0, 1, 0, 0 };

	/* definition of default percussive voices: */
static char bdOpr0[] = { 0,  0, 0, 10,  4, 0, 8, 12, 11, 0, 0, 0, 1, 0 };
static char bdOpr1[] = { 0,  0, 0, 13,  4, 0, 6, 15,  0, 0, 0, 0, 1, 0 };
static char sdOpr[] =  { 0, 12, 0, 15, 11, 0, 8,  5,  0, 0, 0, 0, 0, 0 };
static char tomOpr[] = { 0,  4, 0, 15, 11, 0, 7,  5,  0, 0, 0, 0, 0, 0 };
static char cymbOpr[] ={ 0,  1, 0, 15, 11, 0, 5,  5,  0, 0, 0, 0, 0, 0 };
static char hhOpr[] =  { 0,  1, 0, 15, 11, 0, 7,  5,  0, 0, 0, 0, 0, 0 };

static SLOT_PARAM paramSlot[ 18][ nbLocParam];	/* all the parameters of slots...  */

static char	amDepth;			/* chip global parameters .. */
static char	vibDepth;			/* ... */
static char	noteSel;			/* ... */
static char	percussion;			/* percussion mode parameter */

/* Slot numbers as a function of the voice and the operator.
	( melodic only)
*/
char slotVoice[] [ 2] = {
	{0, 3},		/* voix 0 */
	{1, 4},		/* 1 */
	{2, 5},		/* 2 */
	{6, 9},		/* 3 */
	{7, 10},	/* 4 */
	{8, 11},	/* 5 */
	{12, 15},	/* 6 */
	{13, 16},	/* 7 */
	{14, 17}	/* 8 */
};


/* Slot numbers for the percussive voices.
	0 indicates that there is only one slot.
*/
char slotPerc[] [ 2] = {
	{12, 15},		/* Bass Drum: slot 12 et 15 */
	{16, 0},		/* SD: slot 16 */
	{14, 0},		/* TOM: slot 14 */
	{17, 0},		/* TOP-CYM: slot 17 */
	{13, 0}			/* HH: slot 13 */
};

/* 
	This table gives the offset of each slot within the chip.
	offset = fn( slot)
*/
static char offsetSlot[] = {
	 0,  1,  2,  3,  4,  5,
	 8,  9, 10, 11, 12, 13,
	16, 17, 18, 19, 20, 21
};


/* This table indicates if the slot is a modulator (0) or a carrier (1).
	opr = fn( slot)
*/
static char operSlot[] = {
	0, 0, 0,		/* 1 2 3 */
	1, 1, 1,		/* 4 5 6 */
	0, 0, 0, 		/* 7 8 9 */
	1, 1, 1, 		/* 10 11 12 */
	0, 0, 0, 		/* 13 14 15 */
	1, 1, 1,		/* 16 17 18 */
};

/* This table gives the voice number associated with each slot.
	(melodic mode only)
	voice = fn( slot)
*/
static char voiceSlot[] = {
	0, 1, 2,
	0, 1, 2,
	3, 4, 5,
	3, 4, 5,
	6, 7, 8,
	6, 7, 8,
};


static unsigned fNumNotes[ NR_STEP_PITCH] [ 12];
static int 	halfToneOffset[ 11];
static unsigned * fNumFreqPtr[ 11];
static int 	pitchRange;			/* pitch variation, half-tone [+1,+12] */
static int 	pitchRangeStep;		/* == pitchRange * NR_STEP_PITCH */
static int		modeWaveSel;		/* != 0 if used with the 'wave-select' parameters */


extern SndOutput();			/* in file OUTCHIP.ASM */



/*
----------------------------------------------------------
*/




/*
	Must be called for start-up initialisation.

	Return 0 if harware not found.
*/
int SoundColdInit( port)
	unsigned port;			/* io port address of sound board (0x388) */
	{
	int hardware;

	genAddr = port;
	hardware =  BoardInstalled();
	SoundWarmInit();
	return hardware;
	}	/* SoundColdInit() */


/*
-----------------------------------------------
	Initialize the chip in melodic mode (mode == 0),
	set all 9 voices to electric-piano timbres,
	set the 3 global parameters to zero,
	set the pitch bend range to 1 half-tone,
	set the pitch bend of each voice to 0x2000 (no detune),
	set the volume of each voice to maximum level,
	and enable the wave-select parameter.
-----------------------------------------------
*/
SoundWarmInit()
	{
	int i;

	InitSlotVolume();
	InitFNums();
	SetMode( 0);				/* melodic mode */
	SetGParam( 0, 0, 0);		/* init global parameters */
	for( i = 0 ; i < 9; i++)
		SoundChut( i);
	SetPitchRange( 1);			/* default pitch range is 1 half-tone */
	Set3812( 1);
	}	/* SoundWarmInit() */



/*
---------------------------------------------
	Put the chip in melodic mode (mode == 0),
	or in percussive mode ( mode != 0).

	If the melodic mode is chosen, all voices are
	set to electric-piano, else the first 5 are set
	to electric-piano, and the percussion voices
	to their default timbres.
---------------------------------------------
*/
SetMode( mode)
	int mode;
	{

	if( mode){
		SoundChut( BD);
		SoundChut( SD);
		SoundChut( TOM);
		
		/* set the frequency for the last 4 percussion voices: */
		SetFreq( TOM, TOM_PITCH, 0);
		SetFreq( SD, SD_PITCH, 0);
		}
	percussion = mode;
	percBits = 0;

	InitSlotParams();
	SndSAmVibRhythm();
	}	/* SetMode() */




/*
	Enable (state != 0) / disable (state == 0)
	the wave-select parameters.

	If you do not want to use the wave-select parameters, call
	this function with a value of 0 AFTER calling SoundColdInit()
	or SoundWarmInit().
*/
Set3812( state)
	{
	int i;

	modeWaveSel = state ? 0x20 : 0;
	for( i = 0; i < 18; i++)
		SndOutput( 0xE0 + offsetSlot[ i], 0);
	SndOutput( 1, modeWaveSel);
	}	/* Set3812() */



/*
	Routine to change the pitch bend range. The value can be from
	1 to 12 (in half-tones).

	For example, the value 12 means that the pitch bend will 
	range from -12 (pitchBend == 0, see function 'SetVoicePitch()')
	to +12 (pitchBend == 0x3fff) half-tones.

	The change will be effective as of the next call to
	'SetVoicePitch()'.
*/
SetPitchRange( pR)
	unsigned pR;
	{
	if( pR > 12)
		pR = 12;
	if( pR < 1)
		pR = 1;
	pitchRange = pR;
	pitchRangeStep = pitchRange * NR_STEP_PITCH;
	}	/* SetPitchRange() */


/*
----------------------------------------------
	Set the 3 global parameters AmDepth,
	VibDepth & NoteSel

	The change takes place immediately.
----------------------------------------------
*/
SetGParam( amD, vibD, nSel)
	int amD, vibD, nSel;
	{
	amDepth = amD;
	vibDepth = vibD;
	noteSel = nSel;

	SndSAmVibRhythm();
	SndSNoteSel();
	}	/* SetGParam() */





/*
-------------------------------------------------
	Set the parameters of the voice 'voice'.

	In melodic mode, 'voice' varies from 0 to 8.
	In percussive mode, voices 0 to 5 are melodic
	and 6 to 10 are percussive.

	A timbre (melodic or percussive) is defined as follows:
	the 13 first parameters of operator 0 ( ksl, multi, feedBack,
	attack, sustain, eg-typem decay, release, level, am, vib, ksr, fm)
	followed by the 13 parameters of operator 1 (if a percussive voice, all
	the parameters are zero), followed by the wave-select parameter for
	the operators 0 and 1.

	'paramArray' is structured as follows:
		struct {
			int opr0Prm[ 13];		first 13 parameters
			int opr1Prm[ 13];		must be 0 if percussive timbre
			int	opr0WaveSel;		last parameter
			int opr1WaveSel;		must be 0 if percussive timbre
		} TimbreDef;

	The old timbre files (*.INS) do not contain the parameters
	'opr0WaveSel' and 'opr1WaveSel'. 
	Set these two parameters to zero if you are using the old file
	format.
-------------------------------------------------
*/
SetVoiceTimbre( voice, paramArray)
	int voice, * paramArray;
	{
	int wave0, wave1;
	int * prm1, * wavePtr;

	wavePtr = paramArray + 2 * ( nbLocParam -1);
	wave0 = * wavePtr++;
	wave1 = * wavePtr;
	prm1 = paramArray + nbLocParam -1;

	if( !RhythmMode() || voice < BD) {	/* melodic only */
		SetSlotParam( (int)slotVoice[ voice][ 0], paramArray, wave0);
		SetSlotParam( (int)slotVoice[ voice][ 1], prm1, wave1);
		}
	else if( voice == BD) {	/* Bass Drum */
		SetSlotParam( (int)slotPerc[ 0][ 0], paramArray, wave0);
		SetSlotParam( (int)slotPerc[ 0][ 1], prm1, wave1);
		}
	else	/* percussion, 1 slot */
		SetSlotParam( (int)slotPerc[ voice -BD][ 0], paramArray, wave0);
	}   /* SetVoiceTimbre() */




/*
--------------------------------------------------
	Set the volume of the voice 'voice' to 'volume'.

	The resulting output level is (timbreVolume * volume / 127).
	The change takes place immediately.

	0 <= volume <= 127
--------------------------------------------------
*/
SetVoiceVolume( voice, volume)
	unsigned volume;			/* 0 - 0x7f */
{
	int slot;
	char * slots;

#if 1
	if( !RhythmMode() || voice < BD)
		slot = slotVoice[ voice][ 1];
	else
		slot = slotPerc[ voice -BD][ voice == BD ? 1 : 0];

	if( volume > MAX_VOLUME)
		volume = MAX_VOLUME;
	slotRelVolume[ slot] = volume;
	SndSKslLevel( slot);
#else	/* code that modify the two oper. volume of an additive sound: */
	if( volume > MAX_VOLUME)
		volume = MAX_VOLUME;

	if( !RhythmMode() || voice <= BD) {
		slots = slotVoice[ voice];
		slotRelVolume[ slots[ 1]] = volume;
		SndSKslLevel( slots[ 1]);
		if( !GetLocPrm( slots[ 0], prmFm)) {
			/* additive syntesis: set volume of first slot too */
			slotRelVolume[ slots[ 0]] = volume;
			SndSKslLevel( slots[ 0]);
			}
		}
	else {
		slot = slotPerc[ voice -BD][ 0];
		slotRelVolume[ slot] = volume;
		SndSKslLevel( slot);
		}
#endif
}	/* SetVoiceVolume() */


/*
-------------------------------------------------
	Change the pitch value of a voice.

	The variation in pitch is a function of the previous call
	to 'SetPitchRange()' and the value of 'pitchBend'.
	A value of 0 means -half-tone * pitchRange,
	0x2000 means no variation (exact pitch) and
	0x3fff means +half-tone * pitchRange.

	Does not affect the percussive voices, except for the bass drum.

	The change takes place immediately.

	0 <= pitchBend <= 0x3fff, 0x2000 == exact tuning
-------------------------------------------------
*/
SetVoicePitch( voice, pitchBend)
	unsigned voice;
	unsigned pitchBend;
{
	if( ! RhythmMode() || voice <= BD) {
		/* melodic + bass-drum */
		if( pitchBend > MAX_PITCH)
			pitchBend = MAX_PITCH;
		ChangePitch( voice, pitchBend);
		SetFreq( voice, notePitch[ voice], voiceKeyOn[ voice]);
		}
}	/* SetVoicePitch() */



/*
-----------------------------------------------------------
	Routine to start a note playing.

	0 <= voice <= 8	in melodic mode,
	0 <= voice <= 10 in percussive mode;
	0 <= pitch <= 127, 60 == MID_C ( the card can play between 12 and 107 )
-----------------------------------------------------------
*/
NoteOn( voice, pitch)
	unsigned voice;
	int pitch;			/* 0 - 127 */
	{
	pitch -= ( MID_C - CHIP_MID_C);
	if( pitch < 0)
		pitch = 0;
	
	if( voice < BD || ! RhythmMode())
		/* this is a melodic voice */
		SetFreq( voice, pitch, 1);
	else {
		/* this is a percussive voice */
		if( voice == BD)
			SetFreq( BD, pitch, 0);
		else if( voice == TOM) {
			/* for the last 4 percussions, only the TOM may change in frequency,
				modifying the three others: */
			SetFreq( TOM, pitch, 0);
			SetFreq( SD, pitch + TOM_TO_SD, 0);	/* f7 = 3 * f8 */
			}
		
		percBits |= percMasks[ voice - BD];
		SndSAmVibRhythm();
		}
	}	/* NoteOn() */


/*
	Routine to stop playing the note which was started in 'NoteOn()'.

	0 <= voice <= 8	in melodic mode,
	0 <= voice <= 10 in percussive mode;
*/
NoteOff( voice)
	unsigned voice;	
	{
	if( !RhythmMode() || voice < BD)
		SetFreq( voice, notePitch[ voice], 0); /* shut off */
	else {
		percBits &= ~percMasks[ voice - BD];
		SndSAmVibRhythm();
		}
	}	/* NoteOff() */





/*
------------------------------------------------------------------------
	static functions ...
------------------------------------------------------------------------
*/


/*
	In melodic mode, initialize all voices to electric-pianos.

	In percussive mode, initialize the first 6 voices to electric-pianos
	and the percussive voices to their default timbres.
*/
static InitSlotParams()
{
	int i;
	
	for( i = 0; i < 18; i++)
		if( operSlot[ i])
			SetCharSlotParam( i, pianoParamsOp1, 0);
		else
			SetCharSlotParam( i, pianoParamsOp0, 0);
	if( RhythmMode()) {
		SetCharSlotParam( 12, bdOpr0, 0);
		SetCharSlotParam( 15, bdOpr1, 0);
		SetCharSlotParam( 16, sdOpr, 0);
		SetCharSlotParam( 14, tomOpr, 0);
		SetCharSlotParam( 17, cymbOpr, 0);
		SetCharSlotParam( 13, hhOpr, 0);
		}
}	/* InitSlotParams() */




/*
	Set the volume of all slots.
*/
static InitSlotVolume()
{
	int i;

	for( i = 0; i < 18; i++)
		slotRelVolume[ i] = MAX_VOLUME;
}	/* InitSlotVolume() */




/*
	Return binary value of the frequency 260.44 ( C)
	shifted by +/- numdeltaDemiTon/denDeltaDemiTon multiplied by 8.

	If the numerator (numDeltaDemiTon) is positive, the frequency is
	increased; if negative, it is decreased.

	Fo = Fb( 1 + 0.06 num /den)
	Fnum8 = Fo * 65536 * 72 / 3.58e6
	
	-100 <= numDeltaDemiTon <= +100
	1 <= denDeltaDemiTon <= 100
*/
static long CalcPremFNum( numDeltaDemiTon, denDeltaDemiTon)
{
	long	f8, fNum8, d100;

	d100 = denDeltaDemiTon * 100;
	f8 = ( d100 + 6 * numDeltaDemiTon) * (26044L * 2L);	/* 260.44 * 100 * 2 */
	f8 /= d100 * 25;
	fNum8 = f8 * 16384;		/*( 16384L * 9L);	*/
	fNum8 *= 9L;
	fNum8 /= 179L * 625L;
	return fNum8;
}	/* CalcPremFNum() */


/*
	Initialize a line in the frequency table with shifted frequency values.
	The values are shifted a fraction (num/den) of a half-tone.
	See following routine.
*/
static SetFNum( fNumVec, num, den)
	unsigned * fNumVec;
{
	int i;
	long val;

	*fNumVec++ = (unsigned)(4 +(val = CalcPremFNum( num, den))) >> 3;
	for ( i = 1; i < 12; i++) {
		val *= 106;
		*fNumVec++ = (unsigned)(4 +(val /= 100)) >> 3;
		}
}	/* SetFNum() */


/*
	Initialize all lines of the frequency table. Each line represents
	12 half-tones shifted by (n/NR_STEP_PITCH), where 'n' is the line number
	and ranges from 1 to NR_STEP_PITCH.
*/
static InitFNums()
{
	unsigned i, j, k, num, numStep, pas;

	numStep = 100 / NR_STEP_PITCH;
	for( num = pas = 0; pas < NR_STEP_PITCH; pas++, num += numStep)
		SetFNum( fNumNotes[ pas], num, 100);
	for( i = 0; i < 11; i++) {
		fNumFreqPtr[ i] = (unsigned *) fNumNotes[ 0];
		halfToneOffset[ i] = 0;
		}

	k = 0;
	for( i = 0; i < 8; i++)
		for( j = 0; j < 12; j++, k++) {
			noteDIV12[ k] = i;
			noteMOD12[ k] = j;
			}
}	/* InitFNums() */


/*
	Routine to set 'halfToneOffset[]' & 'fNumFreqPtr[]'.
	These two global variables are used to determine the frequency
	variation to use when a note is played.

	0 <= pitchBend <= 3fffH
*/
static ChangePitch( voice, pitchBend)
	int voice;
	int pitchBend;		/* 0 - 3fffH, 2000H == exact tuning */
	{
	int t1, t2, delta;
	long l;
	static long oldL = ~0;
	static int oldHt;
	static unsigned * oldPtr;

	l = (long)(pitchBend - MID_PITCH) * pitchRangeStep;
	if( oldL == l) {	/* optimisation ... */
		fNumFreqPtr[ voice] = oldPtr;
		halfToneOffset[ voice] = oldHt;
		}
	else {
		t1 = l / MID_PITCH;
		if( t1 < 0) {
			t2 = NR_STEP_PITCH -1 -t1;
			oldHt = halfToneOffset[ voice] = -(t2 / NR_STEP_PITCH);
			delta = (t2 - NR_STEP_PITCH +1) % NR_STEP_PITCH;
			if( delta)
				delta = NR_STEP_PITCH - delta;
			}
		else {
			oldHt = halfToneOffset[ voice] = t1 / NR_STEP_PITCH;
			delta = t1 % NR_STEP_PITCH;
			}
		oldPtr = fNumFreqPtr[ voice] = (unsigned *) fNumNotes[ delta];
		oldL = l;
		}
	}	/* ChangePitch() */



/*
	Used to change the parameter 'param' of the slot 'slot'
	with the value 'val'. The chip registers are updated.
*/
SetASlotParam( slot, param, val)
	int slot, val;
	int param;		/* parameter number */
{
	paramSlot[ slot][ param] = val;
	SndSetPrm( slot, param);
}	/* SetASlotParam() */

	





/*
------------------------------------------------------
	Set the 14 parameters ( 13 in 'param', 1 in 'waveSel')
	of slot 'slot'. Update the parameter array and the chip.
------------------------------------------------------
*/
static SetSlotParam( slot, param, waveSel)
	unsigned slot, * param, waveSel;
{
	int i, k;
	SLOT_PARAM * ptr;

	for( i = 0, ptr = &paramSlot[ slot][ 0]; i < nbLocParam -1; i++)
		*ptr++ = *param++;
	*ptr = waveSel &= 0x3;
	SndSetAllPrm( slot);
}	/* SetSlotParam() */

SetCharSlotParam( slot, cParam, waveSel)
	unsigned slot, waveSel;
	char * cParam;
	{
	int param[ nbLocParam];
	int i;

	for( i = 0; i < nbLocParam -1; i++)
		param[ i] = *cParam++;
	SetSlotParam( slot, param, waveSel);
	}	/* SetCharSlotParam() */


/*
-----------------------------------------------
	Update the parameter 'prm' for the slot 'slot'.
	Update the chip registers.
-----------------------------------------------
*/
static SndSetPrm( slot, prm)
	int slot, prm;
{

	switch( prm) {
		case prmPercussion:
		case prmAmDepth:
		case prmVibDepth:
			SndSAmVibRhythm();
			break;

		case prmNoteSel:
			SndSNoteSel();
			break;

		case prmKsl:
		case prmLevel:
			SndSKslLevel( slot);
			break;

		case prmFm:
		case prmFeedBack:
			SndSFeedFm( slot);
			break;

		case prmAttack:
		case prmDecay:
			SndSAttDecay( slot);
			break;

		case prmRelease:
		case prmSustain:
			SndSSusRelease( slot);
			break;

		case prmMulti:
		case prmVib:
		case prmStaining:
		case prmKsr:
		case prmAm:
			SndSAVEK( slot);
			break;

		case prmWaveSel:
			SndWaveSelect( slot);
			break;
		}
}	/* SndSetPrm() */



/*-------------------------------------------------
	Transfer all the parameters from slot 'slot' to
	the chip.
*/
static SndSetAllPrm( slot)
{
	SndSAmVibRhythm();
	SndSNoteSel();
	SndSKslLevel( slot);
	SndSFeedFm( slot);
	SndSAttDecay( slot);
	SndSSusRelease( slot);
	SndSAVEK( slot);
	SndWaveSelect( slot);
}	/* SndSetAllPrm() */
	

/*
	KSL, LEVEL
*/
static SndSKslLevel( slot)
{
	unsigned t1;

	t1 = 63 - (GetLocPrm( slot, prmLevel) & 0x3f);	/* amplitude */
	t1 = slotRelVolume[ slot] * t1;
	t1 += t1 + MAX_VOLUME;	/* round off to 0.5 */
	t1 = 63 - t1 / ( 2 * MAX_VOLUME);

	t1 |= GetLocPrm( slot, prmKsl) << 6;
	SndOutput( 0x40 + (int)offsetSlot[ slot], t1);
}


/* --------------------------------------------
	Note sel
*/
static SndSNoteSel()
{
	SndOutput( 0x08, noteSel ? 64 : 0);
}	/* SndSNoteSel() */



/* --------------------------------------------
	FEED-BACK and FM (connection).
	Applicable only to operator 0 in melodic mode.
*/
static SndSFeedFm( slot)
{
	unsigned t1;

	if( operSlot[ slot])
		return;
	t1 = GetLocPrm( slot, prmFeedBack) << 1;
	t1 |= GetLocPrm( slot, prmFm) ? 0 : 1;
	SndOutput( 0xC0 + (int)voiceSlot[ slot], t1);
}


/*
	ATTACK, DECAY
*/
static SndSAttDecay( slot)
{
	unsigned t1;

	t1 = GetLocPrm( slot, prmAttack) << 4;
	t1 |= GetLocPrm( slot, prmDecay) & 0xf;
	SndOutput( 0x60 + (int)offsetSlot[ slot], t1);
}


/*
	SUSTAIN, RELEASE
*/
static SndSSusRelease( slot)
{
	unsigned t1;

	t1 = GetLocPrm( slot, prmSustain) << 4;
	t1 |= GetLocPrm( slot, prmRelease) & 0xf;
	SndOutput( 0x80 + (int)offsetSlot[ slot], t1);
}



/*
	AM, VIB, EG-TYP( Sustaining), KSR, MULTI
*/
static SndSAVEK( slot)
{
	unsigned t1;

	t1 = GetLocPrm( slot, prmAm) ? 0x80 : 0;
	t1 += GetLocPrm( slot, prmVib) ? 0x40 : 0;
	t1 += GetLocPrm( slot, prmStaining) ? 0x20 : 0;
	t1 += GetLocPrm( slot, prmKsr) ? 0x10 : 0;
	t1 += GetLocPrm( slot, prmMulti) & 0xf;
	SndOutput( 0x20 + (int)offsetSlot[ slot], t1);
}	/* SndSAVEK() */


/*
    Set the values: AM Depth, VIB depth & Rhythm
*/
static SndSAmVibRhythm()
{
	unsigned t1;

	t1 = amDepth ? 0x80 : 0;
	t1 |= vibDepth ? 0x40 : 0;
	t1 |= RhythmMode() ? 0x20 : 0;
	t1 |= percBits;
	SndOutput( 0xBD, t1);
}


/*
	Set the wave-select parameter.
*/
static SndWaveSelect( slot)
	{
	unsigned wave;

	if( modeWaveSel)
		wave = GetLocPrm( slot, prmWaveSel) & 0x03;
	else
		wave = 0;
	SndOutput( 0xE0 + offsetSlot[ slot], wave);
	}	/* SndWaveSelect() */


/*
	Change pitch of voices 0 to 8, for melodic or percussive mode.
*/
SetFreq( voice, pitch, keyOn)
	unsigned voice;				/* voice number */
	int		pitch;				/* 0 - 95 */
	unsigned	keyOn;			/* Set key-on/off */
	{
	unsigned int fNbr, t1;

	voiceKeyOn[ voice] = keyOn;
	notePitch[ voice] = pitch;
	pitch += halfToneOffset[ voice];
	if( pitch > 95)
		pitch = 95;
	if( pitch < 0)
		pitch = 0;
	fNbr = * ( fNumFreqPtr[ voice] + noteMOD12[ pitch]);
	SndOutput( 0xA0 +voice, fNbr);
	t1 = keyOn ? 32 : 0;
	t1 += ( (unsigned)noteDIV12[ pitch] << 2) + ( 0x3 & HighByte( fNbr) );
	SndOutput( 0xB0 +voice, t1);
	}



/*
	Set the frequency of voice 'voice' to 0 hertz.
*/
static SoundChut( voice)
	int voice;
{
	SndOutput( 0xA0 +voice, 0);
	SndOutput( 0xB0 +voice, 0);
}	/* SoundChut() */



/*
	Return 0 if board is not installed
*/
int BoardInstalled()
	{
	unsigned t1, t2, i;

	SndOutput( 4, 0x60);	/* mask T1 & T2 */
	SndOutput( 4, 0x80);	/* reset IRQ */
	t1 = inp( genAddr);		/* read status register */
	SndOutput( 2, 0xff);	/* set timer-1 latch */
	SndOutput( 4, 0x21);	/* unmask & start T1 */
	for( i = 0; i < 200; i++)	/* 100 uSec delay for timer-1 overflow */
		inp( genAddr);
	t2 = inp( genAddr);		/* read status register */
	SndOutput( 4, 0x60);
	SndOutput( 4, 0x80);
	return (t1 & 0xE0) == 0 && (t2 & 0xE0) == 0xC0;
	}



[ RETURN TO DIRECTORY ]