/* $Filename:       WormWars/Source/sfx.c $
 * $VER:            WormWars 4.0 (1.11.98) $
 * $Description:    Sound effects engine for Worm Wars $
 *
 * © Copyright 1993-1998 James R. Jacobs of Amigan Software.
 * Freely distributable.
 *
 * Release version.

#INCLUDES -------------------------------------------------------------- */

#include "sfx.h"				// #includes everything else

// EXTERNAL VARIABLES -----------------------------------------------------

IMPORT	BYTE					mode;
IMPORT	struct SampStruct		samp[SAMPLES + 1];

// GLOBAL VARIABLES -------------------------------------------------------

		struct IOAudio*			AudioRqPtr[4]			= {NULL, NULL, NULL, NULL};
		struct MsgPort*			AudioPortPtr[4]			= {NULL, NULL, NULL, NULL};
		UBYTE					chan[]					= {15};
		BYTE					AudioClosed				= TRUE;
		BOOL					eversent[4];
		UBYTE*					fbase					= NULL;
		ULONG					fsize;
		ULONG					length[SAMPLES + 1][2];
								// sample lengths
		UBYTE*					sbase[SAMPLES + 1];
								// for sample memory allocation...
		ULONG					speed[SAMPLES + 1];
		ULONG					ssize[SAMPLES + 1];
								// ...and freeing
        struct FileHandle*      FileHandle              = NULL;
		BYTE					volume[SAMPLES + 1];
		ULONG					y[SAMPLES + 1];
        ULONG                   receipter[4] = {(ULONG) -1, (ULONG) -1, (ULONG) -1, (ULONG) -1};

// FUNCTIONS --------------------------------------------------------------

BYTE loadsample(char* fname, UBYTE index)
{	UBYTE*			p8data;
	BYTE			iobuffer[8];		// buffer for 8SVX.VHDR	
	BYTE*			psample[2];			// sample pointers
	struct Chunk*	p8Chunk;			// pointers for 8SVX parsing
	Voice8Header*	pVoice8Header;
	ULONG			rd8count;

	fbase = NULL;
    FileHandle = NULL;

	sbase[index] = (UBYTE *) AllocMem(ssize[index], MEMF_CHIP | MEMF_CLEAR);

    if (!(FileHandle = (struct FileHandle *) Open(fname, MODE_OLDFILE)))
	{	kill8();
		return(1);							// can't open file
	}
    rd8count = Read((BPTR) FileHandle, iobuffer, 8L);
	if (rd8count == -1)
	{	kill8();
		return(2);							// can't read file
	}
	if (rd8count < 8)
	{	kill8();
		return(3);							// not an IFF 8SVX; too short
	}
	p8Chunk = (struct Chunk *) iobuffer;
	if (p8Chunk->ckID != ID_FORM)
	{	kill8();
		return(4);							// not an IFF FORM
	}
	fbase = (UBYTE *) AllocMem(fsize = p8Chunk->ckSize, MEMF_PUBLIC | MEMF_CLEAR);
	if (!fbase)
	{	kill8();							// no memory for read
		return(5);
	}
	p8data = fbase;
    rd8count = Read((BPTR) FileHandle, p8data, p8Chunk->ckSize);
	if (rd8count == -1)
	{	kill8();
		return(6);							// read error
	}
	if (rd8count < p8Chunk->ckSize)
	{	kill8();
		return(7);							// malformed IFF; too short
	}
	if (MAKE_ID(*p8data, *(p8data + 1), *(p8data + 2), *(p8data + 3)) != ID_8SVX)
	{	kill8();
		return(8);							// not an IFF 8SVX
	}
	p8data = p8data + 4;
	while (p8data < fbase + fsize)
	{	p8Chunk = (struct Chunk *) p8data;
		switch(p8Chunk->ckID) {
		case ID_VHDR:
			pVoice8Header = (Voice8Header *) (p8data + 8L);
			break;
		case ID_BODY:
			psample[0]			= (BYTE *) (p8data + 8L);
			psample[1]			= psample[0] + pVoice8Header->oneShotHiSamples;
			length[index][0]	= (ULONG) pVoice8Header->oneShotHiSamples;
			length[index][1]	= (ULONG) pVoice8Header->repeatHiSamples;
			/* To grab the volume level from the IFF 8SVX file, instead
			of from init.h, add the following line:
			samp[index].volume	= (BYTE) (pVoice8Header->volume / 156); */
			break;
		default:
			break;
		}
		p8data += 8L + p8Chunk->ckSize;
		if (p8Chunk->ckSize & 1L == 1)
			p8data++;
	}
	if (length[index][0] == 0)
		y[index] = 1;
	else
		y[index] = 0;
	if (length[index][y[index]] <= 102400)
		ssize[index] = length[index][y[index]];
	else
		ssize[index] = 102400;
	sbase[index] = (UBYTE *) AllocMem(ssize[index], MEMF_CHIP | MEMF_CLEAR);
	if (!sbase[index])
	{	kill8();
		return(9);							// no chip memory
	}
	CopyMem(psample[y[index]], sbase[index], ssize[index]);
	psample[y[index]] += ssize[index];
	speed[index] = PALCLOCK / pVoice8Header->samplesPerSec;
	if (fbase)
	{	FreeMem(fbase, fsize);
		fbase = NULL;
	}
    if (FileHandle)
    {   Close((BPTR) FileHandle);
        FileHandle = NULL;
	}
	return(0);
}

void presetup(void)
{	UBYTE index;
	
	for (index = 0; index <= SAMPLES; index++)
		sbase[index] = NULL;
}

BYTE getaudio(void)
{	BYTE i;
	
	for (i = 0; i <= 3; i++)
	{	if (!(AudioPortPtr[i] = (struct MsgPort *) CreateMsgPort()))
		{	kill8();
			return(11);						// no port
		}
		if (!(AudioRqPtr[i] = (struct IOAudio *) CreateIORequest(AudioPortPtr[i], sizeof(struct IOAudio))))
		{	kill8();						// no I/O memory
			return(10);
		}
		eversent[i] = FALSE;
	}

	AudioRqPtr[0]->ioa_Request.io_Message.mn_ReplyPort		= AudioPortPtr[0];
	AudioRqPtr[0]->ioa_Request.io_Message.mn_Node.ln_Pri	= 127;
	AudioRqPtr[0]->ioa_AllocKey								= 0;
	AudioRqPtr[0]->ioa_Data									= chan;
	AudioRqPtr[0]->ioa_Length								= 1;
		
	if (AudioClosed = OpenDevice(AUDIONAME, 0L, (struct IORequest *) AudioRqPtr[0], 0L))
	{	kill8();
		return(12);							// cannot allocate all channels
	} else
	{	for (i = 1; i <= 3; i++)
			CopyMem(AudioRqPtr[0], AudioRqPtr[i], sizeof(struct IOAudio));
		return(0);							// done
}	}

ULONG effect(UBYTE index)
{           BYTE    i;
            BYTE    ok = -1;
            ULONG   oldestreceipt = (ULONG) -1L;
    STATIC  ULONG   receipt = 1L;

	/* oldestreceipt = temporary variable for ascertaining oldest
	sound still playing.
	receipt = next unused receipt number (monotonically incrementing). */

	if (mode == FX)
	{	for (i = 0; i <= 3; i++)
		{	// decide on a channel

			if (ok == -1)
			{	if (!eversent[i])
					ok = i;
				else if (CheckIO(AudioRqPtr[i]))
				{	WaitIO(AudioRqPtr[i]);
					ok = i;
		}	}	}
        if (ok == -1)
        {   for (i = 0; i <= 3; i++)
                if (receipter[i] < oldestreceipt)
                {   ok = i;
                    oldestreceipt = receipter[i];
                }
            AbortIO(AudioRqPtr[ok]);
            WaitIO(AudioRqPtr[ok]);
        }
        eversent[ok] = TRUE;
        if (samp[index].repeat)
            AudioRqPtr[ok]->ioa_Cycles          = 0;
        else
            AudioRqPtr[ok]->ioa_Cycles          = 1;
        AudioRqPtr[ok]->ioa_Request.io_Command  = CMD_WRITE;
        AudioRqPtr[ok]->ioa_Request.io_Flags    = ADIOF_PERVOL;
        AudioRqPtr[ok]->ioa_Request.io_Unit     = (1 << ok);
        AudioRqPtr[ok]->ioa_Volume              = samp[index].volume;
        AudioRqPtr[ok]->ioa_Period              = (UWORD) speed[index];
        AudioRqPtr[ok]->ioa_Request.io_Message.mn_ReplyPort
                                                = AudioPortPtr[ok];
        AudioRqPtr[ok]->ioa_Data                = (UBYTE *) sbase[index];
        AudioRqPtr[ok]->ioa_Length              = length[index][y[index]];
        BeginIO(AudioRqPtr[ok]);
        receipter[ok] = receipt;
        return(receipt++);
}   }

void kill8(void)
{	BYTE i;

    stopfx(0L);
	if (!AudioClosed)
	{	CloseDevice((struct IORequest *) AudioRqPtr[0]);
		AudioClosed = TRUE;
	}
	for (i = 0; i <= 3; i++)
	{	if (AudioRqPtr[i])
		{	DeleteIORequest(AudioRqPtr[i]);
			AudioRqPtr[i] = NULL;
		}
		if (AudioPortPtr[i])
		{	DeleteMsgPort(AudioPortPtr[i]);
			AudioPortPtr[i] = NULL;
	}	}
	if (fbase)
	{	FreeMem(fbase, fsize);
		fbase = NULL;
	}
    if (FileHandle)
    {   Close((BPTR) FileHandle);
        FileHandle = NULL;
}	}

void destroy8(void)
{	UBYTE index;

	for (index = 0; index <= SAMPLES; index++)
		if (sbase[index])
			FreeMem(sbase[index], ssize[index]);
}

void stopfx(ULONG receipt)
{	BYTE i;

    if (mode == FX)
        for (i = 0; i <= 3; i++)
            if (eversent[i] && (!(CheckIO(AudioRqPtr[i]))) && (receipt == 0L || receipt == receipter[i]))
            {   AbortIO(AudioRqPtr[i]);
                WaitIO(AudioRqPtr[i]);
}           }

// Must have blank line at EOF.
