#include <exec/types.h>
#include <exec/memory.h>
#include <devices/audio.h>
#include <stdio.h>
#include "sample.h"
#include "sounds.h"

#define YES 1
#define NO 0

extern int debug;

/* #define WHERE fprintf(stderr,"%s %s %d\n",__FILE__,__FUNC__,__LINE__); */
/* #define WHAT(x) fprintf(stderr,"%s %s %d %s\n",__FILE__,__FUNC__,__LINE__,x->name); */
#define WHERE
#define WHAT(x)

#define CLOCKFREQ 3579545

extern Sound *sounds[];
extern int maxsounds;

int audio_up = NO;			/* set to yes after initilization is complete, */
							/* used by "endaudio" to figure out clean up */

struct Device *AudioDevice;
struct MsgPort *AudioPort;
struct IOAudio *openIOB;

static UBYTE LeftMap[] = {LEFT0, LEFT1};
static UBYTE RightMap[] = {RIGHT0, RIGHT1};
static UBYTE TryLeftMap[] = {LEFT1, LEFT0, RIGHT1, RIGHT0};
static UBYTE TryRightMap[] = {RIGHT1, RIGHT0, LEFT1, LEFT0};

_abort()
{
	fputs("abort caused by ^C or audio panic\n",stderr);
	cleanup(debug);
	exit(10);
}

panic(s)
char *s;
{
	fprintf(stderr,"panic: %s\n",s);
	_abort();
}

struct IOAudio *IOBallocate()
{
	struct IOAudio *IOB;

	if ((IOB = (struct IOAudio *)AllocMem(sizeof(struct IOAudio), MEMF_PUBLIC | MEMF_CLEAR)) == (struct IOAudio *) NULL)
		panic("IOBallocate failed"); 

	return(IOB);
}

IOBfree(IOBp)
struct IOAudio *IOBp;
{
	if (IOBp != NULL)
		FreeMem(IOBp, sizeof(struct IOAudio));
}


InitPlayIOB(soundp)
Sound *soundp;
{
	struct IOAudio *IOB = soundp->IOBs[PLAY_IOB];

	IOB->ioa_Request.io_Command = CMD_WRITE;
	IOB->ioa_Request.io_Flags = ADIOF_PERVOL;
	IOB->ioa_Volume = soundp->volume;
	IOB->ioa_Cycles = soundp->cycles;
	IOB->ioa_Data = (UBYTE *)&soundp->samplep->sampledata[0];
	IOB->ioa_Length = (ULONG)soundp->samplep->nsamples;
	IOB->ioa_Period = (CLOCKFREQ / soundp->samplep->sampsec) + soundp->detune;
}

InitStopIOB(soundp)
Sound *soundp;
{
	struct IOAudio *IOB = soundp->IOBs[STOP_IOB];

	IOB->ioa_Request.io_Command = ADCMD_FINISH;
	IOB->ioa_Request.io_Flags = IOF_QUICK;
}


InitAllocIOB(soundp)
Sound *soundp;
{
	struct IOAudio *allocIOB = soundp->IOBs[ALLOC_IOB];

	allocIOB->ioa_Request.io_Command = ADCMD_ALLOCATE;
	allocIOB->ioa_Request.io_Message.mn_Node.ln_Pri = soundp->precedence;

	if (!soundp->flags & EITHER) {
		allocIOB->ioa_Data = (soundp->flags & LEFT) ? LeftMap : RightMap;
		allocIOB->ioa_Length = 2;
	} else {
		allocIOB->ioa_Data = (soundp->flags & LEFT) ? TryLeftMap : TryRightMap;
		allocIOB->ioa_Length = 4;
	}

	allocIOB->ioa_Request.io_Flags = (ADIOF_NOWAIT | IOF_QUICK);
}

InitDeallocIOB(soundp)
Sound *soundp;
{
	struct IOAudio *IOB = soundp->IOBs[FREE_IOB];

	IOB->ioa_Request.io_Command = ADCMD_FREE;
	IOB->ioa_Request.io_Flags = IOF_QUICK;
}


initsound(soundindex)
int soundindex;
{
	int i;
	struct IOAudio *IOB;
	Sound *soundp = sounds[soundindex];

	if ((soundp->port = CreatePort(soundp->name, 0)) == 0)
	{
		fprintf(stderr,"can't create audio port \"%s\"\n",soundp->name);
		panic("can't create audio port");
	}

	for (i = 0; i < MAX_SOUND_IOBS; i++)
	{
		IOB = soundp->IOBs[i] = IOBallocate();

		IOB->ioa_Request.io_Message.mn_ReplyPort = soundp->port;
		IOB->ioa_Request.io_Device = AudioDevice;

		/* We assign our own AllocKeys so we'll have a handle to determine
		 * which sound structure we received at a reply port.  This also
		 * makes playtime overhead less by not having to throw around
		 * dynamically-allocated allocation keys.  If AllocKey
		 * is zero when channels are allocated, a key is dynamically assigned
		 * We use soundindex + 1 instead of soundindex to prevent a zero
		 * allockey.  Also, we reduce playtime overhead by not throwing
		 * dynamically-assigned keys around.
		 */

		IOB->ioa_AllocKey = soundindex + 1;
	}

	InitPlayIOB(soundp);
	InitStopIOB(soundp);
	InitAllocIOB(soundp);
	InitDeallocIOB(soundp);
}

initaudio()
{
	int i;

	openIOB = IOBallocate();

	if (OpenDevice(AUDIONAME, 0, openIOB, 0) != 0)
		panic("can't open audio device");

	AudioDevice = openIOB->ioa_Request.io_Device;

	for (i = 0; i < maxsounds; i++)
		initsound(i);

	audio_up = YES;
}

endaudio()
{
		int i, j;


		if (audio_up)
			{
				for (i = 0; i < maxsounds; i++)
				StopSound(sounds[i]);
			}

		for (i = 0; i < maxsounds; i++)
		{
			for (j = 0; j < MAX_SOUND_IOBS; j++)
				IOBfree(sounds[i]->IOBs[j]);
			if (sounds[i]->port)
				DeletePort(sounds[i]->port,sizeof(struct MsgPort));
		}
		CloseDevice(openIOB);
		IOBfree(openIOB);
}

PlaySound(soundp)
Sound *soundp;
{
	int i;

	struct IOAudio *allocIOB = soundp->IOBs[ALLOC_IOB];
	struct IOAudio *playIOB = soundp->IOBs[PLAY_IOB];

	if (soundp->flags & PLAYING)
		return;

	BeginIO(allocIOB);

	/* the only possible error return is ADIOERR_NOALLOCATION */
	if (allocIOB->ioa_Request.io_Error)
		return;

	for (i = PLAY_IOB; i <= FREE_IOB; i++)
		soundp->IOBs[i]->ioa_Request.io_Unit = allocIOB->ioa_Request.io_Unit;

	BeginIO(playIOB);

	/* possible errors are IOERR_ABORTED and ADIOERR_NOALLOCATION */
	if (playIOB->ioa_Request.io_Error)
		return;

	soundp->flags |= PLAYING;
}

DeallocSound(soundp)
Sound *soundp;
{
	struct IOAudio *freeIOB = soundp->IOBs[FREE_IOB];

	BeginIO(freeIOB);

	/* only possible error return is ADIOERR_NOALLOCATION */
}

StopSound(soundp)
Sound *soundp;
{
	struct IOAudio *stopIOB = soundp->IOBs[STOP_IOB];
	struct IOAudio *freeIOB= soundp->IOBs[FREE_IOB];

	WHAT(soundp);
	if (soundp->flags & PLAYING)
	{
		soundp->flags &= ~PLAYING;
		BeginIO(stopIOB);
		/* only error return is ADIOERR_NOALLOCATION */

		WaitIO(soundp->IOBs[PLAY_IOB]);
		/* error returns can be IOERR_ABORTED and ADIOERR_NOALLOCATION */
	}
	DeallocSound(soundp);
}

WaitSound(soundp)
Sound *soundp;
{
	WHAT(soundp);

	if (soundp->flags & PLAYING)
	{
		WaitIO(soundp->IOBs[PLAY_IOB]);
		soundp->flags &= ~PLAYING;
	}
	DeallocSound(soundp);
}

dumpsound(soundp)
Sound *soundp;
{
	printf("dump of sound %s\n",soundp->name);
	printf("cycles %d, volume %d, flags %d, precedence %d, detune %d\n",
		soundp->cycles, soundp->volume, soundp->flags, soundp->precedence,
		soundp->detune);
	dumpsample(soundp->samplep);
}

dumpsample(samptr)
Sample *samptr;
{
	int i, linect;
	printf("nsamples %d, sampsec %d\n",samptr->nsamples, samptr->sampsec);
	/* for (i = 0; i < samptr->nsamples; i++)
	{
		printf("%4d ",samptr->sampledata[i]);
		if (++linect > 14)
		{
			putchar('\n');
			linect = 0;
		}
	} */
}

