/* Ilbm.library C application, Play8SVX.c:

	Set your editor's TAB width to 3

	cc +p Play8SVX.c
	as -cd IlbmInterface.asm
	ln Play8SVX.o IlbmInterface.o -lcl32

	This program can only be run from the CLI. It takes 1 arg, the name of a
file. If an 8SVX file, this program will then play the sample.

This is an example of using the library's mid-level routine LoadIFF() with
our own FORM and PROP handlers instead of lib's default routines. This is the
way to go if you need some non-ILBM reader/writer. Note that we need to
define some PropFrame for 8SVX PROPs.
*/

#include "math.h"
#include "functions.h"	  /* Manx C declarations */

#include "exec/tasks.h"
#include "exec/types.h"
#include "exec/memory.h"
#include "libraries/dos.h"
#include "libraries/dosextens.h"
#include "devices/audio.h"

/* The ilbm lib C INCLUDE file */
#include "ILBM_Lib.h"


/*-------------------------------defines---------------------------------*/

IFFP myForm();
IFFP myProp();

typedef struct {
	ULONG	oneShotHiSamples,
	repeatHiSamples,
	samplesPerHiCycle;
	UWORD	samplesPerSec;
	UBYTE	ctOctave,
	sCompression;
	LONG	volume;
	} Voice8Header;

 /* Data for the Dissidents ILBM library */
struct ILBMBase	*ILBMBase=0L;

 /* OK. Let's make up an 8SVXFrame and 8SVXPropFrame */

typedef struct {
	USHORT		vFlags;
	Voice8Header vhdr;
	} v8SVXFrame;

typedef struct {
   ULONG		NextPropFrame;
	LONG		ifID;
	UWORD		PropFrameSize;
	v8SVXFrame PropFrame;
	} v8SVXPropFrame;

	/* definitions for iFlags field */
#define	VHDRFLAG	1	/* if a VHDR chunk found in the file */

#define GetVHDR(context, vhdr)  \
	IFFReadBytes(sizeof(Voice8Header), context, (BYTE *)vhdr)

#define	ID_VHDR	MakeID('V','H','D','R')
#define	ID_8SVX	MakeID('8','S','V','X')

v8SVXFrame		my8SVXFrame;

 /* Data for our program */
struct FileHandle		*fp = 0;
BYTE						*memptr = 0;
ULONG						memsize = 0;

#define SetFlag(v,f)			((v)|=(f))
#define ClearFlag(v,f)		((v)&=~(f))
#define ToggleFlag(v,f)		((v)^=(f))
#define FlagIsSet(v,f)		((BOOL)(((v)&(f))!=0))

/*=================== For audio.device playback of wave ==================*/
struct IOAudio ioaudio = {0}, ioaudio2 = {0};
struct MsgPort *audPort = 0L;
UBYTE  audio_gub = 0;
UBYTE  anychan[4] = {1,2,4,8};


/*-----------closes audio dev, audio port, ILBM lib---------*/

VOID exit_all()
{
	if( audio_gub )	CloseDevice( &ioaudio );
	if( audPort )		DeletePort( audPort );
	if( ILBMBase ) 	CloseLibrary( ILBMBase );
	exit( FALSE );
}


/*---------- Opens audio dev, audio port, ilbm lib ---------*/

VOID open_all()
{

 /* Open the ILBM lib */
	if( !(ILBMBase=(struct ILBMBase *)OpenLibrary("ilbm.library", 0L)) )
	{
		printf("Need the dissidents ilbm.library on boot disk\n");
		exit();
	}

/* Open audio.device and allocate a channel */

	if( !(audPort = CreatePort( "SAMP_Port", 0 )) )
	{
		puts("no audio port");
		exit_all();
	}

	ioaudio.ioa_Request.io_Message.mn_ReplyPort = audPort;

	ioaudio.ioa_Request.io_Command = ADCMD_ALLOCATE;
	ioaudio.ioa_Data = (UBYTE *)anychan;
	ioaudio.ioa_Length = 4;
	ioaudio.ioa_Request.io_Flags = ADIOF_NOWAIT | IOF_QUICK;
	if( OpenDevice("audio.device", 0, &ioaudio, 0 ) )
	{
		puts("audio device open error\n");
		exit_all();
	}

	if( !(ioaudio.ioa_Request.io_Flags & IOF_QUICK ) )
		GetMsg( audPort );

	audio_gub = 1;
	
	ioaudio2 = ioaudio;
	/* initialize the AudioIO for the looping portion */

}


/*----------------------------start of main()----------------------------*/

main( argc, argv )
LONG argc;
UBYTE *argv[];
{
	IFFP  Result;
	LONG	err;

 /* LoadIFF() needs a Vectors structure */
	Vectors myVectors;

 /* We expect the filename for the first arg. */
	if( argc != 2 )
	{
		printf("USAGE: Play8SVX filename\n");
		exit();
	}

 /* Open ilbm.library, setup audio device */
	open_all();

 /* Open the IFF File */
	fp = Open( argv[1], MODE_OLDFILE );

	if (fp)
	{

 /* Note that we are using the lib's middle level functions so that we can
	 install our own handlers for each encountered group and context. Also,
	 we can parse any IFF FORM, not just ILBMs and ANIMs using this level.
	 We are going to install our own function, myForm(), as the routine
	 which the library calls whenever it encounters a FORM ID. Our function,
	 myProp() will be called for 8SVX PROPS.
	 We "install" these routines by setting up the appropriate fields in our
	 Vectors structure. Let's do all this now. */

		myVectors.PROPhandler = myProp;
		myVectors.FORMhandler = myForm;
		myVectors.CHUNKhandler = 0;
		myVectors.NonILBMhandler = 0;
		/* Don't care about CHUNKhandler or NonILBMhandler as long as we have
			our own FORMhandler. */

		/* Start parsing the file using LoadIFF() */
		Result=LoadIFF( fp, &myVectors, &my8SVXFrame );

		/* O.K. we're back. Did we successfully parse as much as we wanted?
			Well, our FORM routine should have returned IFF_DONE if we found
			the 8SVX BODY. Otherwise, myForm, myProp, or an internal library
			routine knocked us out by returning some other IFFP error code. Let's
			get an informative error msg to display.
		*/

	 	/* If some error, get the IFFP error message and print it */
		if( Result != IFF_DONE )
		{
			puts( GetIFFPMsg( Result ) );
		}
		else
		{
			/* Play one lousy, stinking note. Hey this is just for a test! */

			ioaudio.ioa_Volume = 64;	/* Play everything with MAX volume */
			ioaudio2.ioa_Volume = 64;
			ioaudio.ioa_Request.io_Command = CMD_WRITE;
			ioaudio2.ioa_Request.io_Command = CMD_WRITE;

			/* Play the OneShot portion */
			ioaudio.ioa_Data = (UBYTE *)memptr;
			ioaudio.ioa_Length = (ULONG)my8SVXFrame.vhdr.oneShotHiSamples;
			ioaudio.ioa_Request.io_Flags = ADIOF_PERVOL;
			ioaudio.ioa_Cycles = 1;
			if (my8SVXFrame.vhdr.samplesPerSec==0)
				my8SVXFrame.vhdr.samplesPerSec = 7000;
			ioaudio.ioa_Period = 3579545L / my8SVXFrame.vhdr.samplesPerSec;

			ioaudio2.ioa_Data = (UBYTE *)(memptr + my8SVXFrame.vhdr.oneShotHiSamples);
			ioaudio2.ioa_Request.io_Flags = 0;
			ioaudio2.ioa_Cycles = 10;		/* only play the loop 10 times. */
			ioaudio2.ioa_Period = ioaudio.ioa_Period;
			ioaudio2.ioa_Length = (ULONG)my8SVXFrame.vhdr.repeatHiSamples;

			BeginIO( &ioaudio );

			if( ioaudio2.ioa_Length ) 
					BeginIO( &ioaudio2 );
			err = WaitIO( &ioaudio );
			GetMsg( audPort );
			if(ioaudio2.ioa_Length)
			{
				err = WaitIO( &ioaudio2 );
				GetMsg( audPort );
			}

		}

		/* Close the IFF File */
		Close(fp);

		/* Free any allocated CHIP mem for the sample */
		if (memptr)  FreeMem(memptr, memsize);

	}
	else printf( "Couldn't open %s. \n", argv[1]);

	printf("\n");

	exit_all();
}			  /* end of main() */



/*========= LoadIFF() calls me when it finds a FORM ==========*/

IFFP  myForm( chunkID, context, vectors, frame, proplist )
PROPList *proplist;
v8SVXFrame *frame;  /* We expect an 8SVXFrame */
Vectors *vectors;
GroupContext *context;
ID		 chunkID;
{
register	IFFP   code = IFF_OKAY;  /* Assume that we skip the FORM */
register	ULONG  propf;

	/* Is this an 8SVX? If not, skip any other FORM by returning IFF_OKAY */

	if (chunkID == ID_8SVX)
	{

	/* OK. It's 8SVX. Now check to see if we have any 8SVX PROPS.
		If so, copy over that PropFrame to our present 8SVXFrame. */

		propf = SearchPROP(chunkID, proplist);
		if	(propf) CopyMem(propf, frame, sizeof(v8SVXFrame) );

		while ( code >= 0 )
		{

			/* Read in the next chunk */
			code = GetFChunkHdr(context);

			/* ================== VHDR ===================== */
				if( code == ID_VHDR )
				{

					/* Load the VHDR into 8SVXFrame and set VHDR flag */

					code = GetVHDR(context, &frame->vhdr);
					if( code == IFF_OKAY )
					{
						SetFlag(frame->vFlags, VHDRFLAG);
					}
				}	/* end of VHDR parse */

			/* ================== BODY ===================== */
				if( code == ID_BODY )
				{
				/* If we got an VHDR, start loading the samples into chip mem */
					if ( FlagIsSet(frame->vFlags, VHDRFLAG) );
					{
						ClearFlag(frame->vFlags, VHDRFLAG);
						code = IFF_NOMEM;			/* Assume error */
						if ( memptr ) FreeMem( memsize, memptr );
						memsize = ChunkMoreBytes(context);
						if ( (memptr = AllocMem(memsize, MEMF_CHIP)) );
							{
							code = IFFReadBytes(memsize, context, memptr);
							if( code == IFF_OKAY )	code = IFF_DONE; /* Stop the parsing and return this */
							}
					}
				}	/* end of BODY parse */

		}	/* end of while */

	} /* end of ID_8SVX */

	/*  Reasons for falling out:
		 1). We found and successfully loaded the BODY, returning IFF_DONE.
		 2). GetFChunkHdr returned END_MARK, meaning that we got to the end
				of our 8SVX without finding a BODY.
		 3). One of the lib routines returned some IFFP error. We simply
				return that error.
		 All 3 scenarios cause our parsing to stop. Note that if we never find
		 an 8SVX FORM in the file, the lib will eventually return us to main
		 with LoadIFF returning END_MARK.  Note: To change this routine so that
		 it continues parsing the rest of the file, eliminate the line:
		 if (code == IFF_OKAY) code = IFF_DONE;
		 Instead, when you get back to main, you can test memptr to see if any
		 8SVX file was loaded.  You might want to do this if you were going to
		 add parsing for ID_SMUS to this form handler, and didn't want to stop
		 until you parsed the entire file.
	*/

	return( code );
}


/*========= LoadIFF() calls me when it finds a PROP ==========*/

IFFP myProp( chunkID, propID, context, vectors, frame, proplist )
PROPList *proplist;
v8SVXFrame *frame;
Vectors *vectors;
GroupContext *context;
ID   propID;
ID   chunkID;
{
register	IFFP	code = IFF_OKAY;
register	v8SVXFrame	*pframe;

	/* We only care about 8SVX PROPS. If PropID not 8SVX, immediately return
		IFF_OKAY to skip.  Otherwise, we allocate an 8SVXPropFrame and parse
		the PROP's chunks just like in the FORMhandler, storing info in
		this new PropFrame. Later, in our FORMHandler, we search the PROPList
		for the appropriate ID and retrieve the info. The library automatically
		updates the PROPList when it gets to the end of an IFF group so that you
		don't have to keep track of which level we're at and which PROPS affect
		which chunks. Also, the library frees all PropFrames that we allocate
		with GetPROPStruct() by the time we return to main. */


	if ( propID == ID_8SVX )
	{
		/* Allocate an 8SVXPropFrame and link into PROPList */
		code = IFF_NOMEM;
		pframe = GetPROPStruct( sizeof(v8SVXPropFrame), propID, proplist );
		if ( pframe )
		{
			code = IFF_OKAY;
			while ( code >= 0 )
			{
				/* Read in the next chunk */
				code = GetPChunkHdr(context);

				/* ================== VHDR ===================== */
				if( code == ID_VHDR )
				{

					/* Load the VHDR into 8SVXPropFrame and set VHDR flag */
					code = GetVHDR(context, &pframe->vhdr);
					if( code == IFF_OKAY )
					{
						SetFlag(frame->vFlags, VHDRFLAG);
					}
				}	/* end of VHDR parse */

			}	/* end of while */

		}
	}

	return( code );
}

