/* PlaySound.c */
/* This routine sets up an ioRequest and plays back a */
/* waveform through the Amiga audio hardware */

/* Usage : PlaySound(buffer,repeat,period,volume) */
/* buffer is a pointer to a waveform */
/* repeat is the number of times to play the wave (0 == forever) */
/* period determines the playback rate (minimum value == 124) */
/* volume goes from 0 to 64 */

#include "exec/types.h"
#include "devices/audio.h"
#include "exec/memory.h"
#include "SoundErrors.h"

#define LEFT0F			1
#define RIGHT0F		2
#define RIGHT1F		4
#define LEFT1F			8

extern struct MsgPort *CreatePort();

/* Look for a left channel, then a right */
UBYTE allocationMap[] = {LEFT0F,LEFT1F,RIGHT0F,RIGHT1F};

/* Allocate and initialize an IO Request Block */
SetIOA(per,vol,repeat,len,ioa)
int per,vol,repeat;
ULONG len;
struct IOAudio **ioa;
{
	struct MsgPort *port;
	char *PortName;

	PortName = "Sound Channel";

	/* Allocate IOAudio structure */
	(*ioa) = (struct IOAudio *)AllocMem(sizeof(struct IOAudio),
									    MEMF_PUBLIC|MEMF_CLEAR);

	/* If Allocation Successful ... */
	if((*ioa)!=0)
	{
		/* Set Priority */
		(*ioa)->ioa_Request.io_Message.mn_Node.ln_Pri = 10;
		/* Create Message port for IORequest to talk to Amiga */
		if ((port = CreatePort(PortName,0)) == 0)
		{
			FreeMem((*ioa),sizeof(struct IOAudio));
		}
		/* If Creation Successful ... */
		else
		{
			/* Get a channel */
			(*ioa)->ioa_Request.io_Message.mn_ReplyPort = port;
			(*ioa)->ioa_Data = allocationMap;
			(*ioa)->ioa_Length = sizeof(allocationMap);
			/* Open Audio Device for output */
			if(OpenDevice(AUDIONAME,0,(*ioa),0) != 0)
			{
				DeletePort(port);
				FreeMem((*ioa),sizeof(struct IOAudio));
			}
			/* If open worked ... */
			else
			{
				/* Set Up Request */
				(*ioa)->ioa_Request.io_Flags = ADIOF_PERVOL;
				(*ioa)->ioa_Request.io_Command = CMD_WRITE;
				(*ioa)->ioa_Period = per;
				(*ioa)->ioa_Volume = vol;

				/* For some reason, the Audio chip can't play samples */
				/* longer than 131k, so we kludge.  Oh, well. */
				if(len<131000)
				{
					(*ioa)->ioa_Cycles = repeat;
				}
				else
				{
					(*ioa)->ioa_Cycles = 1;
				}
			}
		}
	}
}

/* Play a sound */
struct IOAudio *PlaySound(buffer,buflen,repeat,period,volume)
BYTE *buffer;
ULONG buflen;
int repeat,period,volume;
{
	struct IOAudio *ioa;
	BYTE *DataPtr;
	ULONG PlayLen=0;
	int sound_done=0;
	int count;

	count = repeat?(repeat-1):-1;
	DataPtr = buffer;

	/* Set Up IOAudio structure */
	SetIOA(period,volume,repeat,buflen,&ioa);

	/* Set up data and length pointers of ioa */
	SetLength(&PlayLen,buflen,&DataPtr,ioa);

	/* Send command to Audio chip */
	BeginIO(ioa);

	/* If no data remains to play, return ioa pointer */
	if(PlayLen == 0)
	{
		return(ioa);
	}

	/* If there is more (buflen >131000), continue until there isn't */
	while(!sound_done)
	{
		/* If current chunk is done ... */
		if(CheckIO(ioa))
		{
			/* If more remains, continue */
			if(PlayLen > 0)
			{
				/* update pointers and start next chunk */
				FixLength(&PlayLen,&DataPtr,ioa);
				BeginIO(ioa);
				if((PlayLen == 0) && (count == 0))
				{
					return(ioa);
				}
			}
			/* If not ... */
			else
			{
				/* Check repeat counter */
				if(count != 0)
				{
					/* Restart wave */
					DataPtr = buffer;
					SetLength(&PlayLen,buflen,&DataPtr,ioa);
					BeginIO(ioa);
					if(count>0)
					{
						count--;
					}
				}
				else
				{
					/* finished */
					sound_done = 1;
				}
			}
		}
	}
	/* Free IOAudio stuff */
	StopSound(ioa);
}

StopSound(ioa)
struct IOAudio *ioa;
{
	AbortIO(ioa);
	if(ioa->ioa_Request.io_Device)
	{
		CloseDevice(ioa);
	}
	if(ioa->ioa_Request.io_Message.mn_ReplyPort)
	{
		DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
	}
	if(ioa)
	{
		FreeMem(ioa,sizeof(struct IOAudio));
	}
}

SetLength(LenPtr,buflen,DataHndl,ioa)
ULONG *LenPtr,buflen;
BYTE **DataHndl;
struct IOAudio *ioa;
{
	if(buflen<= 131000)
	{
		(*LenPtr) = buflen;
	}
	else
	{
		(*LenPtr) = 131000;
	}

	ioa->ioa_Length = (*LenPtr);
	ioa->ioa_Data = (*DataHndl);

	if((*LenPtr) != buflen)
	{
		(*LenPtr) = buflen - 131000;
		(*DataHndl) += 131000;
	}
	else
	{
		(*LenPtr) = 0;
	}
}	

FixLength(LenPtr,DataHndl,ioa)
ULONG *LenPtr;
BYTE **DataHndl;
struct IOAudio *ioa;
{
	if((*LenPtr) > 131000)
	{
		ioa->ioa_Length = 131000;
		ioa->ioa_Data = (*DataHndl);
		(*LenPtr) -= 131000;
		(*DataHndl) += 131000;
	}
	else
	{
		ioa->ioa_Length = (*LenPtr);
		ioa->ioa_Data = (*DataHndl);
		(*LenPtr) = 0;
	}
}
