**
* music.c.  make funny noises.											*
*																		*
* Author : A.J. Savige, Convergent Solutions Pty Ltd, Christmas 1989.	*
**

include std.h
include ctos.h
include music.h

define DIM(a) (sizeof(a)sizeof(*(a)))  * no. of elts in array a *

define ERC_NO_MESSAGE_AVAILABLE		14
define ERC_YOURE_DOG_MEAT				22224	* invalid input to server *

define PERIOD_RQ_CODE					1	* unique  can be anything *
define FREQ_RQ_CODE					2	* unique  can be anything *
											* not currently used *
define DELAY_RQ_CODE					3	* unique  can be anything *

* the following three define the byte output to the NGen beeper port *

define TONE_OFF						1
define TONE_CTS_ON						3		* continuous *
define TONE_ON							5

* timer request block *

typedef struct {
	word_t	counter;
	word_t	ctrReload;
	word_t	cEvents;
	word_t	exchResp;
	word_t	ercRet;
	word_t	rqCode;
} trb_t;

* death march *

note_t deathMarch[] = {
	1000, 4, 1,
	1000, 4, 1,
	1000, 1, 1,
	1000, 4, 2,
	880,  3, 1,
	940,  1, 1,
	940,  2, 1,
	1000, 2, 1,
	1000, 2, 1,
	1100, 2, 1,
	1000, 4, 1
};

portinfo_t	*pPort;			* get NGen beeper port from this *
trb_t		trbPeriod;		* period of note *
trb_t		trbDelay;		* delay between note *
exchange_t	timerExch;		* used by trb *
trb_t		*pMsg;			* used by timer stuff *

void InitMusic()
{
	register erc_t erc;

	* set up stuff for noise *

	erc = GetpStructure(14, 0, pPort);
	if (erc = ERC_OK) BadErc(erc, GetpStructure);

	* initialize timer request blocks *

	CheckErc(AllocExch(timerExch));

	trbPeriod.rqCode	= PERIOD_RQ_CODE;
	trbPeriod.exchResp	= timerExch;
	trbPeriod.ercRet	= ERC_OK;

	trbDelay.rqCode		= DELAY_RQ_CODE;
	trbDelay.exchResp	= timerExch;
	trbDelay.ercRet		= ERC_OK;

	trbPeriod.counter	= 0;
	erc = OpenRTClock(trbPeriod);
	if (erc = ERC_OK) BadErc(erc, OpenRTClock  Period);
	trbPeriod.cEvents	= 0;
	trbPeriod.ctrReload	= 0;
	trbPeriod.counter	= 1;
	erc = Wait(timerExch, pMsg);
	if (erc = ERC_OK) BadErc(erc, wait period);

	trbDelay.counter	= 0;
	erc = OpenRTClock(trbDelay);
	if (erc = ERC_OK) BadErc(erc, OpenRTClock  Delay);
	trbDelay.cEvents	= 0;
	trbDelay.ctrReload	= 0;
	trbDelay.counter	= 1;
	erc = Wait(timerExch, pMsg);
	if (erc = ERC_OK) BadErc(erc, wait delay);
}

**

void DoSiren(port, lo, hi, inc, cfreq, nrep)
word_t	port;	* in:  put tone to this port *
int		lo;		* in:  lowest freq. of siren *
int		hi;		* in:  highest freq. of siren *
int		inc;	* in:  freq. increment of siren *
int		cfreq;	* in:  time between changing freqencies *
int		nrep;	* in:  no. of repititions *
{
	register int i, j, k, note;

	* allow for different processor speeds *

	if (pPort>processorType == PROC_I_SERIES) {
		lo *= 2;
		hi *= 2;
		cfreq *= 2;
	}

	note = hi;
	for (k = 0; k < nrep; k++) {
		* sweep up *
		for (; note > lo; note = inc) {
			for (j = 0; j < cfreq(note*2); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < note; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < note; i++) ;   * freq. delay *
			}
		}
		* sweep down *
		for (; note < hi; note += inc) {
			for (j = 0; j < cfreq(2*note); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < note; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < note; i++) ;   * freq. delay *
			}
		}
	}
}

void DoSweepUp(port, lo, hi, inc, cfreq, nrep)
word_t	port;	* in:  put tone to this port *
int		lo;		* in:  lowest freq. of sweep *
int		hi;		* in:  highest freq. of sweep *
int		inc;	* in:  freq. increment of sweep *
int		cfreq;	* in:  time between changing freqencies *
int		nrep;	* in:  no. of repititions *
{
	register int i, j, k, note;


	* allow for different processor speeds *

	if (pPort>processorType == PROC_I_SERIES) {
		lo *= 2;
		hi *= 2;
		cfreq *= 2;
	}

	for (k = 0; k < nrep; k++) {
		* sweep up *
		for (note = hi; note > lo; note = inc) {
			for (j = 0; j < cfreq(note*2); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < note; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < note; i++) ;   * freq. delay *
			}
		}
	}
}

void DoSweepDown(port, lo, hi, inc, cfreq, nrep)
word_t	port;	* in:  put tone to this port *
int		lo;		* in:  lowest freq. of siren *
int		hi;		* in:  highest freq. of siren *
int		inc;	* in:  freq. increment of siren *
int		cfreq;	* in:  time between changing freqencies *
int		nrep;	* in:  no. of repititions *
{
	register int i, j, k, note;


	* allow for different processor speeds *

	if (pPort>processorType == PROC_I_SERIES) {
		lo *= 2;
		hi *= 2;
		cfreq *= 2;
	}

	for (k = 0; k < nrep; k++) {
		* sweep down *
		for (note = lo; note < hi; note += inc) {
			for (j = 0; j < cfreq(2*note); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < note; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < note; i++) ;   * freq. delay *
			}
		}
	}
}

define WARB_V_SLOW_LO		545
define WARB_V_SLOW_HI		112
define WARB_V_SLOW_FREQ	10000
define WARB_V_SLOW_N		12

define WARB_SLOW_LO		327
define WARB_SLOW_HI		67
define WARB_SLOW_FREQ		5000
define WARB_SLOW_N			12

define WARB_MEDIUM_LO		273
define WARB_MEDIUM_HI		56
define WARB_MEDIUM_FREQ	4000
define WARB_MEDIUM_N		12

define WARB_FAST_LO		163
define WARB_FAST_HI		34
define WARB_FAST_FREQ		3000
define WARB_FAST_N			12

void DoWarble(port, lo, hi, cfreq, nwarb, nrep)
word_t	port;	* in:  put tone to this port *
int		lo;		* in:  low freq. of warble *
int		hi;		* in:  high freq. of warble *
int		cfreq;	* in:  time between changing freqencies *
int		nwarb;	* in:  no. of changes in a warble *
int		nrep;	* in:  no. of repititions *
{
	register int i, j, k, l, note;

	* allow for different processor speeds *

	if (pPort>processorType == PROC_I_SERIES) {
		lo *= 2;
		hi *= 2;
		cfreq *= 2;
	}

	for (k = 0; k < nrep; k++) {
		for (l = 0; l < nwarb; l++) {
			* note 1 *
			for (j = 0; j < cfreq(lo*2); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < lo; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < lo; i++) ;   * freq. delay *
			}
			* note 2 *
			for (j = 0; j < cfreq(hi*2); j++) {
				PortOut(port, TONE_ON);
				for (i = 0; i < hi; i++) ;   * freq. delay *
				PortOut(port, TONE_OFF);
				for (i = 0; i < hi; i++) ;   * freq. delay *
			}
		}
	}
}

erc_t PutBeepTone(port, period)
word_t	port;		* in:  put tone to this port *
word_t	period;		* in:  period *
{
	erc_t			erc;

	* set up period timer *

	trbPeriod.cEvents	= 0;
	trbPeriod.ctrReload	= 0;
	trbPeriod.counter	= period;

	* turn beeper noise on, wait, then switch it off *

	PortOut(port, TONE_CTS_ON);
	erc = Wait(timerExch, pMsg);
	if (erc = ERC_OK) return erc;
	PortOut(port, TONE_OFF);

	return ERC_OK;
}

erc_t PutTone(port, freq, period)
word_t	port;		* in:  put tone to this port *
word_t	freq;		* in:  frequency *
word_t	period;		* in:  period *
{
	erc_t			erc;
	register word_t	i;

	* allow for different processor speeds *

	if (pPort>processorType == PROC_CP001) freq = 2;

	* set up period timer *

	trbPeriod.cEvents	= 0;
	trbPeriod.ctrReload	= 0;
	trbPeriod.counter	= period;

	for (;;) {
		PortOut(port, TONE_ON);
		for (i = 0; i < freq; i++) ;   * freq. delay *
		erc = Check(timerExch, pMsg);
		if (erc == ERC_OK) {
			if (pMsg>rqCode == PERIOD_RQ_CODE) break;
		}
		else if (erc = ERC_NO_MESSAGE_AVAILABLE)
			return erc;

		PortOut(port, TONE_OFF);
		for (i = 0; i < freq; i++) ;   * freq. delay *
		erc = Check(timerExch, pMsg);
		if (erc == ERC_OK) {
			if (pMsg>rqCode == PERIOD_RQ_CODE) break;
		}
		else if (erc = ERC_NO_MESSAGE_AVAILABLE)
			return erc;
	}

	return ERC_OK;
}

erc_t PlayTune(note, nNote)
note_t	note[];		* in:  tune to be played *
int		nNote;		* in:  no. of elts. in note *
{
	register int	i;
	register word_t	freq, period;
	erc_t			erc;

	for (i = 0; i < nNote; i++) {
		PutTone(pPort>BeeperPortNGen, note[i].freq, note[i].period);
		trbDelay.cEvents	= 0;
		trbDelay.ctrReload	= 0;
		trbDelay.counter	= note[i].delay;
		erc = Wait(timerExch, pMsg);
		if (erc = ERC_OK) return erc;
	}

	return ERC_OK;
}

erc_t DoFunnyNoise(freq, period)
int		freq;
int		period;
{
	erc_t	erc = ERC_OK;

	if (freq == 0) {  * just delay, no noise *
		if (period == 0) return ERC_OK;
		else if (period < 0) return ERC_YOURE_DOG_MEAT;
		trbDelay.cEvents	= 0;
		trbDelay.ctrReload	= 0;
		trbDelay.counter	= period;
		erc = Wait(timerExch, pMsg);
	}
	else if (freq > 0) {  * play a note *
		if (period < 0) return ERC_YOURE_DOG_MEAT;
		erc = PutTone(pPort>BeeperPortNGen, freq, period);
	}
	else {  * use continuous tone or play a tune *
		switch (freq) {

			* funny noises *

			case	BEEP_TONE:
				erc = PutBeepTone(pPort>BeeperPortNGen, period);
				break;
			case	POLICE_SIREN:
				DoSiren(pPort>BeeperPortNGen, 200, 400, 10, 6000, period);
				break;
			case	CAR_ALARM:
				DoSiren(pPort>BeeperPortNGen, 50, 200, 5, 1000, period);
				break;
			case	SLOW_WHOOP:
				DoSweepUp(pPort>BeeperPortNGen, 10, 3000, 10, 6000, period);
				break;
			case	FAST_WHOOP:
				DoSweepUp(pPort>BeeperPortNGen, 10, 300, 5, 800, period);
				break;
			case	SLOW_PHASER:
				DoSweepDown(pPort>BeeperPortNGen, 10, 3000, 10, 6000,period);
				break;
			case	FAST_PHASER:
				DoSweepDown(pPort>BeeperPortNGen, 10, 300, 5, 800, period);
				break;
			case	SLOW_WARBLE:
				DoWarble(pPort>BeeperPortNGen, WARB_SLOW_LO, WARB_SLOW_HI,
				WARB_SLOW_FREQ, WARB_SLOW_N, period);
				break;
			case	MEDIUM_WARBLE:
				DoWarble(pPort>BeeperPortNGen, WARB_MEDIUM_LO,
				WARB_MEDIUM_HI, WARB_MEDIUM_FREQ, WARB_MEDIUM_N, period);
				break;
			case	FAST_WARBLE:
				DoWarble(pPort>BeeperPortNGen, WARB_FAST_LO, WARB_FAST_HI,
				WARB_FAST_FREQ, WARB_FAST_N, period);
				break;
			case	V_SLOW_WARBLE:
				DoWarble(pPort>BeeperPortNGen, WARB_V_SLOW_LO,
				WARB_V_SLOW_HI, WARB_V_SLOW_FREQ, WARB_V_SLOW_N, period);
				break;

			* tunes *

			case	DEATH_MARCH:
				erc = PlayTune(deathMarch, DIM(deathMarch));
				break;

			default:
				erc = ERC_YOURE_DOG_MEAT;
				break;
		}
	}
	return erc;
}

00
define WARB_V_SLOW_N		12

define WARB_SLOW_LO		327
define WARB_SLOW_HI		67
define WARB_SLOW_FREQ		5000
define WARB_SLOW_N			12

define WARB_MEDIUM_LO		273
define WARB_MEDIUM_HI		56
define WARB_MEDIUM_FREQ	4000
define WARB_MEDIUM_N		12

define WARB_FAST_LO		163
define WARB_FAST_HI		34
define WARB_FAST_FREQ		3000
define WARB_FAST_N			12

void DoWarble(port, lo,