/*  audio.c		Martin Round    November '89

    This code is taken straight from Steven A. Bennett's 'scales.c'
    Thanks Steven!
    I've mostly left the code in it's original state, only putting in
    a few function prototypes and 'voids' to stop my compiler issuing
    me with warnings.
    I've also stripped comments and altered bracket indenting into my own
    weird style - sorry Steven!  Actually it's not my own style, I'm driven
    to use it by a stupid Lint-like program I use called 'ccheck'.
*/

#define  PRIORITY			  10L
#define  NBR_IOA_STRUCTS	  10L
#define  PV_IOA_STRUCT		   0L
#define  FIN_IOA_STRUCT		   9L
#define  BIG_WAVE			 256L
#define  NBR_WAVES			   7L
#define  WAVES_TOTAL		1024L
#define  YES				   1L
#define  NO					   0L
 
extern struct MsgPort *CreatePort();
extern void *AllocMem();

void clear_audio (int);
 
UBYTE aMap[] = { 0x0f };
long  voiceMap[] = { 1, 2, 4, 8 };
struct IOAudio *ioa, *finishioa, *ioapv;
struct IOAudio *ioainuse[4];
struct IOAudio *freeioa[4];
long unitno = 1;
int error;
int waiting[4] = { NO, NO, NO, NO };
int woffsets[] =
	{ 0, 256, 384, 448, 480, 496, 504, 508, 510 };
int wlen[] =
	{ 256, 128, 64, 32, 16, 8, 4, 2, 1 };
int perval[] =
	{ 428, 404, 381, 360, 339, 320, 302, 285, 269, 254, 240, 226, 214 };
BYTE *wptr;
BYTE *owptr[4] = { NULL, NULL, NULL, NULL };
 
char *portstring[] =
	{
	"Audio one",
	"Audio two",
	"Audio three",
	"Audio four",
	"Audio five",
	"Audio six",
	"Audio seven",
	"Audio eight"
	};
 
int get_audio()
	{
	int i;

	ioa = (struct IOAudio *)
	  AllocMem((NBR_IOA_STRUCTS * (long)sizeof(*ioa)),MEMF_PUBLIC | MEMF_CLEAR);
	if (ioa == NULL)
		{
		clear_audio(1);
		return(0);
		}
 
	for (i = 0; i < 4; ++i)
		{
		ioainuse[i] = &ioa[i + 1];
		freeioa[i] = &ioa[i + 5];
		}
	ioapv = &ioa[PV_IOA_STRUCT];
	finishioa = &ioa[FIN_IOA_STRUCT];
 
	ioa->ioa_Request.io_Message.mn_Node.ln_Pri = PRIORITY;
	ioa->ioa_Request.io_Message.mn_ReplyPort =
		CreatePort("Audio zero", 0L);
	if (ioa->ioa_Request.io_Message.mn_ReplyPort == NULL)
		{
		clear_audio(2);
		return(0);
		}
	ioa->ioa_Data = aMap;
	ioa->ioa_Length = (long)sizeof(aMap);
	error = OpenDevice(AUDIONAME, 0L, (struct ioRequest *) ioa, 0L);
	if (error)
		{
		clear_audio(3);
		return(0);
		}
 
	*finishioa = *ioa;
	finishioa->ioa_Request.io_Flags = IOF_QUICK;
	ioapv->ioa_Request.io_Flags = IOF_QUICK;
 
	finishioa->ioa_Request.io_Command = ADCMD_FINISH;
	ioapv->ioa_Request.io_Command = ADCMD_PERVOL;
 
	for (i = 0; i < 4; ++i)
		{
		*freeioa[i] = *ioa;
		*ioainuse[i] = *ioa;
		freeioa[i]->ioa_Request.io_Message.mn_ReplyPort =
			CreatePort(portstring[i], 0L);
		ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort =
			CreatePort(portstring[i + 4], 0L);
		}
	for (i = 0; i < 4; ++i)
		if
		  (
				freeioa[i]->ioa_Request.io_Message.mn_ReplyPort == NULL ||
				ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort == NULL
		  )
				{
				clear_audio(4);
				return (0);
				}
	return (makewaves());
	}
 
void clear_audio(finishcode)
	int finishcode;
	{
	int i;
 
	switch(finishcode)
		{
	case 0:

		FreeMem(wptr, WAVES_TOTAL);
 
	case 4:
	case 5:

		for (i = 0; i < 4; ++i)
			{
			if (freeioa[i]->ioa_Request.io_Message.mn_ReplyPort)
				DeletePort(freeioa[i]->ioa_Request.io_Message.mn_ReplyPort);
			if (ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort)
				DeletePort(ioainuse[i]->ioa_Request.io_Message.mn_ReplyPort);
			}
 

		CloseDevice((struct ioRequest *) ioa);
 
	case 3:

		DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
 
	case 2:

		FreeMem(ioa, (NBR_IOA_STRUCTS * (long)sizeof(*ioa)));
 
		}

	}
 
void setwpv(wf, len, per, vol, voice)
	char *wf;
	int len, per, vol, voice;
	{
	struct IOAudio *tmpioa;
 
	freeioa[voice]->ioa_Request.io_Command = CMD_WRITE;
	freeioa[voice]->ioa_Request.io_Flags = ADIOF_PERVOL | IOF_QUICK;
	freeioa[voice]->ioa_Cycles = 0;
 
	freeioa[voice]->ioa_Request.io_Unit = (struct Unit *)unitno;
	finishioa->ioa_Request.io_Unit = (struct Unit *)unitno;
 

	freeioa[voice]->ioa_Data = (UBYTE *)wf;
	freeioa[voice]->ioa_Length = len;
	freeioa[voice]->ioa_Period = per;
	freeioa[voice]->ioa_Volume = vol;
 
	if (waiting[voice])
		{
		BeginIO((struct ioRequest *) finishioa);
		WaitIO((struct ioRequest *) ioainuse[voice]);
		waiting[voice] = NO;
		}
 

	BeginIO((struct ioRequest *) freeioa[voice]);
	error = CheckIO((struct ioRequest *) freeioa[voice]);
	if (error)
		WaitIO((struct ioRequest *) freeioa[voice]);
		
	waiting[voice] = YES;
 
	tmpioa = ioainuse[voice];
	ioainuse[voice] = freeioa[voice];
	freeioa[voice] = tmpioa;
	}
 
void setpv(per, vol)
	int per, vol;
	{
	ioapv->ioa_Period = per;
	ioapv->ioa_Volume = vol;
	ioapv->ioa_Request.io_Unit = (struct Unit *)unitno;
	BeginIO((struct ioRequest *) ioapv);
	}
 
void StopVoices()
	{
	int voice;
 
	for (voice = 0; voice < 4; ++voice)
		{
		if (waiting[voice])
			{
			unitno = voiceMap[voice];
			setpv(128, 0);
			finishioa->ioa_Request.io_Unit = (struct Unit *)unitno;
			BeginIO((struct ioRequest *) finishioa);
			WaitIO((struct ioRequest *) ioainuse[voice]);
			waiting[voice] = NO;
			}
		}
	}
 
void setwave(wfp)
	UBYTE *wfp;
	{
	int i;
 
	for (i = 0; i < BIG_WAVE; ++i)
		wfp[i] = i;
	}
 
void xpandwave(wfp)
	BYTE *wfp;
	{
	int i, j, rate;
	BYTE *tptr;
 
	rate = 1;
	tptr = wfp + BIG_WAVE;
	for (i = 0; i < NBR_WAVES - 1; ++i)
		{
		rate *= 2;
		for (j = 0; j < BIG_WAVE; j += rate)
			*tptr++ = wfp[j];
		}
	}
 
int makewaves()
	{
	wptr = (BYTE *)AllocMem(WAVES_TOTAL, MEMF_CHIP);
	if (wptr == NULL)
		{
		clear_audio(5);
		return (0);
		}
 
	setwave(wptr);
	xpandwave(wptr);
	return (1);
	}
 
void play_note(note, voice)
	int note, voice;
	{
	int per, oct;
	BYTE *wfp;
 
	unitno = voiceMap[voice];
	if (note >= 100)
		{
		if (waiting[voice])
			setpv(200, 0);
		return;
		}
	oct = note / 12;
	per = perval[note % 12];
	wfp = wptr + woffsets[oct];
 
	if (wfp == owptr[voice])
		setpv(per, 32);
	else
		{
		setwpv(wfp, wlen[oct], per, 32, voice);
		owptr[voice] = wfp;
		}
	}