/*
 * $Id: Play8SVX.c 1.3 1997/07/12 13:03:51 olsen Exp olsen $
 *
 * :ts=4
 */

#include <exec/execbase.h>
#include <exec/memory.h>

#include <dos/dosextens.h>
#include <dos/dosasl.h>
#include <dos/rdargs.h>

#include <libraries/iffparse.h>

#include <clib/exec_protos.h>
#include <clib/dos_protos.h>
#include <clib/iffparse_protos.h>

#include <pragmas/exec_sysbase_pragmas.h>
#include <pragmas/dos_pragmas.h>
#include <pragmas/iffparse_pragmas.h>

#define USE_BUILTIN_MATH
#include <string.h>
#include <stdlib.h>

/*****************************************************************************/

#include "libraries/concierto.h"
#include "clib/concierto_protos.h"
#include "pragmas/concierto_pragmas.h"

/*****************************************************************************/

extern struct ExecBase *SysBase;
extern struct DosLibrary *DOSBase;

/*****************************************************************************/

struct Library *IFFParseBase;

/*****************************************************************************/

struct Library *ConciertoBase;
struct AudioContext *ac;

/*****************************************************************************/

struct RDArgs *rdargs;
STRPTR *names;

/*****************************************************************************/

#define ID_8SVX MAKE_ID('8', 'S', 'V', 'X')
#define ID_VHDR MAKE_ID('V', 'H', 'D', 'R')
#define ID_BODY MAKE_ID('B', 'O', 'D', 'Y')
#define ID_CHAN MAKE_ID('C', 'H', 'A', 'N')

typedef LONG Fixed;				/* A fixed-point value, 16 bits to the left of
								 * the point and 16 to the right. A Fixed is a
								 * number of 2**16ths, i.e. 65536ths. */
#define Unity 0x10000L			/* Unity = Fixed 1.0 = maximum volume */

/* sCompression: Choice of compression algorithm applied to the samples. */
#define sCmpNone		0		/* not compressed */
#define sCmpFibDelta	1		/* Fibonacci-delta encoding (Appendix C) */
								/* Could be more kinds in the future. */
typedef struct {
	ULONG	oneShotHiSamples,	/* # samples in the high octave 1-shot part */
			repeatHiSamples,	/* # samples in the high octave repeat part */
			samplesPerHiCycle;	/* # samples/cycle in high octave, else 0 */
	UWORD	samplesPerSec;		/* data sampling rate */
	UBYTE	ctOctave,			/* # of octaves of waveforms */
			sCompression;		/* data compression technique used */
	Fixed	volume;				/* playback nominal volume from 0 to Unity
								 * (full volume). Map this value into
								 * the output hardware's dynamic range.
								 */
} Voice8Header;

#define CHANNEL_Right	4L
#define CHANNEL_Left	2L
#define CHANNEL_Stereo	6L

typedef long sampletype;

/*****************************************************************************/

VOID
CloseAll(VOID)
{
	if(rdargs != NULL)
	{
		FreeArgs(rdargs);
		rdargs = NULL;
	}

	if(IFFParseBase != NULL)
	{
		CloseLibrary(IFFParseBase);
		IFFParseBase = NULL;
	}

	if(ac != NULL)
	{
		CTO_ReleaseAudioContext(ac);
		ac = NULL;
	}

	if(ConciertoBase != NULL)
	{
		CloseLibrary(ConciertoBase);
		ConciertoBase = NULL;
	}
}

BOOL
OpenAll(VOID)
{
	IFFParseBase = OpenLibrary("iffparse.library",37);
	if(IFFParseBase == NULL)
		return(FALSE);

	ConciertoBase = OpenLibrary("concierto.library",0);
	if(ConciertoBase == NULL)
		return(FALSE);

	ac = CTO_ObtainAudioContext(0);
	if(ac == NULL)
		return(FALSE);

	rdargs = ReadArgs("NAMES/M",(LONG *)&names,NULL);
	if(rdargs == NULL)
	{
		PrintFault(IoErr(),"PlayAIFF");
		return(FALSE);
	}

	if(names == NULL)
	{
		PrintFault(ERROR_REQUIRED_ARG_MISSING,"PlayAIFF");
		return(FALSE);
	}

	return(TRUE);
}

/****************************************************************************/

VOID
CloseIFFFile(struct IFFHandle *handle)
{
	if(handle != NULL)
	{
		CloseIFF(handle);
		Close((BPTR)handle->iff_Stream);
		FreeIFF(handle);
	}
}

struct IFFHandle *
OpenIFFFile(const char *name)
{
	struct IFFHandle *handle;

	handle = AllocIFF();
	if(handle != NULL)
	{
		handle->iff_Stream = (ULONG)Open((STRPTR)name,MODE_OLDFILE);
		if(handle->iff_Stream != NULL)
		{
			InitIFFasDOS(handle);

			if(OpenIFF(handle,IFFF_READ) == 0)
				return(handle);

			Close(handle->iff_Stream);
		}

		FreeIFF(handle);
	}

	return(NULL);
}

/****************************************************************************/

#define OK	(0)
#define NOT	!

/****************************************************************************/

BOOL
Load8SVX(
	struct IFFHandle *	handle,
	Voice8Header *		pVoiceHeader,
	APTR *				pSampleLeft,
	APTR *				pSampleRight)
{
	STATIC LONG Stops[3 * 2] =
	{
		ID_8SVX,ID_VHDR,
		ID_8SVX,ID_CHAN,
		ID_8SVX,ID_BODY
	};

	sampletype st;
	Voice8Header vh;

	LONG status,size;
	UBYTE *sampleLeft,*sampleRight;
	BOOL result = FALSE;

	st = 0;
	memset(&vh,0,sizeof(vh));

	sampleLeft	= NULL;
	sampleRight	= NULL;

	status = StopChunks(handle,Stops,3);
	if(status == OK)
	{
		struct ContextNode *chunk;
		BOOL done = FALSE;

		while(NOT done && status == OK && ParseIFF(handle,IFFPARSE_SCAN) == OK)
		{
			chunk = CurrentChunk(handle);

			switch(chunk->cn_ID)
			{
				case ID_VHDR:

					size = ReadChunkBytes(handle,&vh,sizeof(vh));
					if(size != sizeof(vh))
					{
						status = size;
						break;
					}

					if(vh.ctOctave         != 1 ||
					   vh.repeatHiSamples  != 0 ||
					   vh.oneShotHiSamples == 0)
					{
						status = ERROR_NOT_IMPLEMENTED;
						break;
					}

					sampleLeft = AllocVec(vh.oneShotHiSamples,MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR);
					if(sampleLeft == NULL)
						status = ERROR_NO_FREE_STORE;

					break;

				case ID_CHAN:

					size = ReadChunkBytes(handle,&st,sizeof(st));
					if(size != sizeof(st))
						status = size;

					break;

				case ID_BODY:

					if(st == CHANNEL_Stereo)
					{
						sampleRight = AllocVec(vh.oneShotHiSamples,MEMF_ANY|MEMF_PUBLIC|MEMF_CLEAR);
						if(sampleRight == NULL)
							status = ERROR_NO_FREE_STORE;
					}

					size = ReadChunkBytes(handle,sampleLeft,vh.oneShotHiSamples);
					if(size != vh.oneShotHiSamples)
					{
						status = size;
						break;
					}

					if(st == CHANNEL_Stereo)
					{
						size = ReadChunkBytes(handle,sampleRight,vh.oneShotHiSamples);
						if(size != vh.oneShotHiSamples)
						{
							status = size;
							break;
						}
					}

					*pVoiceHeader = vh;

					result = TRUE;
					done = TRUE;

					break;
			}
		}
	}

	if(result)
	{
		*pSampleLeft	= sampleLeft;
		*pSampleRight	= sampleRight;
	}
	else
	{
		FreeVec(sampleLeft);
		FreeVec(sampleRight);
	}

	return(result);
}

VOID
Play8SVX(const char *name)
{
	struct IFFHandle *handle;

	handle = OpenIFFFile(name);
	if(handle != NULL)
	{
		Voice8Header vh;
		APTR sampleLeft,sampleRight;

		if(Load8SVX(handle,&vh,&sampleLeft,&sampleRight))
		{
			ULONG closestRate;
			LONG result;
			LONG numChannels;

			if(sampleLeft != NULL && sampleRight != NULL)
				numChannels = 2;
			else
				numChannels = 1;

			closestRate = CTO_FindNearestSampleRate(vh.samplesPerSec);

			Printf("Playing... ");Flush(Output());

			result = CTO_StartPlayback(ac,
				DSPA_SampleDataLeft,	sampleLeft,
				DSPA_SampleDataRight,	sampleRight,
				DSPA_NumSampleBytes,	vh.oneShotHiSamples,
				DSPA_PlaybackRate,		closestRate,
				DSPA_NumChannels,		numChannels,
				DSPA_NumBitsPerSample,	8,
				DSPA_WakeTask,			FindTask(NULL),
				DSPA_WakeSigBit,		SIGBREAKB_CTRL_C,
			TAG_DONE);

			if(result == 0)
			{
				Wait(SIGBREAKF_CTRL_C);

				CTO_Abort(ac);

				SetSignal(0,SIGBREAKF_CTRL_C);
			}
			else
			{
				Printf("error #%ld, ",result);
			}

			Printf("done.\n");

			FreeVec(sampleLeft);
			FreeVec(sampleRight);
		}

		CloseIFFFile(handle);
	}
}

/****************************************************************************/

int
main(void)
{
	if(OpenAll())
	{
		int i;

		for(i = 0 ; names[i] != NULL ; i++)
		{
			Play8SVX(names[i]);

			if(CheckSignal(SIGBREAKF_CTRL_D))
			{
				PrintFault(ERROR_BREAK,"Play8SVX");
				break;
			}
		}
	}

	CloseAll();

	return(0);
}
