/* quasi-minimal IFF 8SVX sample file loader for Karl's Audio Stuff
 * by Karl Lehenbauer, based originally on public domain IFF code from
 * Electronic Arts
 * rewritten 2/25/88 KEL to support chaining of samples through a
 * central sample store and to support music (it's now carrying
 * around octave and one-shot info)

 *
 * this version for the sound effect player has had the octave stuff
 * hacked back out but the central sample store is still there
 *
 */

#include <exec/types.h>
#include <functions.h>
#include <exec/memory.h>
#include <exec/nodes.h>
#include <exec/lists.h>
#include <stdio.h>
#include <fcntl.h>
#include "assert.h"

#include "iff.h"
#include "8svx.h"

#include "sample.h"

struct List SampleStore;
struct List *SampleList = &SampleStore;

/* this is a global variable that is set by Load8SVX to indicate whether
 * it found the sample in memory already or not.  When we're playing
 * sounds for the timer and noload is selected, it's a good thing to know
 * and we already return a value (bleah, C is a bit restrictive in this
 * case)
 */
int sample_was_in_cache;

char *SamplePath = "ChatterBox:";

void UnloadAllSamples()
{
#ifdef DEBUG
	fprintf(stderr,"UnloadAllSamples cleanup routine executing\n");
#endif
	while (SampleList->lh_TailPred != SampleList)
	{
		UnloadSampleAt(SampleList->lh_Head);
	}
#ifdef DEBUG
	fprintf(stderr,"UnloadAllSamples cleanup routine complete\n");
#endif
}


Init8SVX()
{
	NewList(SampleList);
	add_cleanup(UnloadAllSamples);
}

Sample *Load8SVX(fname)
char *fname;
{
	int iffile;
	ChunkHeader chunkhead;
	Voice8Header voicehead;
	LONG samplebytes, fibbytes;
	LONG formtype;
    char *sampbufp, *fibbufp;
	register Sample *samptr;
	LONG headsize;
	short flags = 0;
	char fnamebuf[256];

	assert(sizeof(chunkhead) == 8);

#ifndef NOAUDIT
	if (SampleList->lh_Head == (struct Node *)NULL)
		panic("called Load8SVX without calling Init8SVX\n");
#endif

#ifdef DEBUG
	printf("checking sample store for %s - ",fname);
#endif
	if ((samptr = (Sample *)FindName(SampleList, fname)) != NULL)
	{
#ifdef DEBUG
		printf("request for %s satisfied from sample store\n",fname);
#endif
		sample_was_in_cache = YES;
		return(samptr);
	}

	sample_was_in_cache = NO;

#ifdef DEBUG
	printf("didn't find it\n");
#endif

	sprintf(fnamebuf,"%s%s",SamplePath,fname);
#ifdef DEBUG
	printf("looking for IFF file, name of %s\n",fnamebuf);
	fflush(stdout);
#endif
	if ((iffile = OpenIFF(fnamebuf, ID_8SVX)) < 0)
	{
		fprintf(stderr,"Load8SVX: can't open IFF sample file %s\n",fnamebuf);
		return((Sample *)NULL);
	}

	if ((headsize = chunkuntil(iffile,ID_VHDR)) == -1)
	{
		fputs("Load8SVX: IFF 8SVX VHDR chunk search failed\n",stderr);
		return((Sample *)NULL);
	}

	if (!readchunk(iffile,&voicehead,headsize))
	{
		fputs("Load8SVX: read of IFF VHDR chunk failed\n",stderr);
		return((Sample *)NULL);
	}
#ifdef DEBUG
	DumpVoiceHead(&voicehead);
#endif

	if ((samplebytes = chunkuntil(iffile,ID_BODY)) < 0)
	{
		fputs("Load8SVX: IFF 8SVX BODY chunk search failed\n",stderr);
		return((Sample *)NULL);
	}

	if (voicehead.sCompression == sCmpFibDelta)
	{
#ifdef DEBUG
		fprintf(stderr,"Load8SVX: sample uses Fibonacci Delta Encoding\n");
#endif
		fibbytes = samplebytes;
		samplebytes = (fibbytes - 2) * 2;
	}

    if ((sampbufp = (char *)AllocMem(samplebytes,MEMF_FAST)) == (char *)NULL)
	{
	    if ((sampbufp = (char *)AllocMem(samplebytes,MEMF_CHIP)) == (char *)NULL)
		{
			fputs("Load8SVX: unable to allocate memory for waveform\n",stderr);
			return((Sample *)NULL);
		}
		else
			flags = SAMPLE_ALLOCATED_IN_CHIP_RAM;

	}
	else
		flags = SAMPLE_ALLOCATED_IN_FAST_RAM;

	if (voicehead.sCompression == sCmpFibDelta)
	{

		/* load the delta compressed data into the back half of the sample
		 * buffer just allocated.  We will decompress the data into the 
		 * same sample memory, without overwriting the compressed data.
		 * It'll work.  Trust me, I know what I'm doing.
		 */

		fibbufp = &sampbufp[samplebytes / 2];

		if (!readchunk(iffile,fibbufp,fibbytes))
		{
			fputs("Read of fib sample data failed!\n",stderr);
			FreeMem(sampbufp,samplebytes);
			return((Sample *)NULL);
		}
		FibUnpack(fibbufp+2,fibbytes-2,sampbufp,fibbufp[1]);
	}
	else if (!readchunk(iffile,sampbufp,samplebytes))
	{
		fputs("Read of sample data failed!\n",stderr);
		FreeMem(sampbufp,samplebytes);
		return((Sample *)NULL);
	}

	close(iffile);

	if ((samptr = (Sample *)AllocMem(sizeof(Sample),0L)) == (Sample *)NULL)
	{
	   fputs("Load8SVX: unable to allocate memory for Sample structure\n",stderr);
	   FreeMem(sampbufp,samplebytes);
	   return((Sample *)NULL);
	}

	/* allocate memory for sample name */
	if ((samptr->node.ln_Name = (char *)AllocMem(strlen(fname) + 1,0)) == (char *)NULL)
	{
		fputs("Load8SVX: unable to allocate memory for Sample name\n",stderr);
		FreeMem(sampbufp,samplebytes);
		FreeMem(samptr,sizeof(Sample));
		return((Sample *)NULL);
	}

	/* structure copy in Voice8Header info */
	samptr->sampleheader = voicehead;

	/* set up pointer to sample data and store length */
	if (flags & SAMPLE_ALLOCATED_IN_FAST_RAM)
	{
		samptr->fastsampledata = sampbufp;
		samptr->sampledata = NULL;
	}
	else if (flags & SAMPLE_ALLOCATED_IN_CHIP_RAM)
	{
		samptr->fastsampledata = NULL;
		samptr->sampledata = sampbufp;
	}
	samptr->samplebytes = samplebytes;

	/* copy in the voice name */
	strcpy(samptr->node.ln_Name,fname);

	/* ...and set up the flags */
	samptr->sample_flags = flags;

	/* set the number of current "users" of the sample in chip RAM to zero */
	samptr->chip_use_count = 0;

	/* initialize the rest of the node fields */
	samptr->node.ln_Type = samptr->node.ln_Pri = 0;

	/* stick this new sample onto the end of the sample list */
	AddTail(SampleList,&samptr->node);

#ifndef NOAUDIT
	samptr->magic = SAMPLE_MAGIC;
#endif

#ifdef DEBUG
	fprintf(stderr,"Load8SVX: waveform bytes %ld, samples/second %d\n",samplebytes,voicehead.samplesPerSec);
	fprintf(stderr,"Load8SVX: samptr %lx, sampledata %lx\n",samptr,sampbufp);
#endif
	return(samptr);
}

/* FibUnpack - unpack Fibonacci Delta Compressed audio sample
 *				see IFF file formats in the ROM Kernel Exec manual
 *				or on one of the Fred Fish disks
 */

char FibToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};

FibUnpack(inp,n,outp,val)
char *inp, *outp;
long n;
char val;
{
	register char pair;
#ifdef DEBUG
	fprintf(stderr,"FibUnpack: inp %lx, n %ld, outp %lx, val %d\n",inp,n,outp,val);
#endif
	while (n--)
	{
		pair = *inp++;				/* get a pair of compressed samples */
		val += FibToDelta[(pair >> 4) & 0xf]; /* uncompress first sample */
		*outp++ = val;
		val += FibToDelta[pair & 0xf];		/* uncompress second sample */
		*outp++ = val;
	}
}

/* unload sample at address */
UnloadSampleAt(samptr)
Sample *samptr;
{
	ASSERT_SAMPLE_MAGIC(samptr);

#ifdef DEBUG
	printf("UnloadSampleAt: Unloading Sample %s\n",samptr->node.ln_Name);
#endif

	/* remove the sample from the sample list */
	Remove(&samptr->node);

	/* free the space allocated for the sample's name */
	FreeMem(samptr->node.ln_Name,strlen(samptr->node.ln_Name)+1);

	/* free the sample data itself */
	if (samptr->sample_flags & SAMPLE_ALLOCATED_IN_CHIP_RAM)
	{
		FreeMem(samptr->sampledata,samptr->samplebytes);
	}
	else
	{
		assert(samptr->sample_flags & SAMPLE_ALLOCATED_IN_FAST_RAM);
		FreeMem(samptr->fastsampledata,samptr->samplebytes);
	}
#ifndef NOAUDIT
	samptr->magic = 0;
#endif
	samptr->sample_flags = 0;

	/* free the sample structure */
	FreeMem(samptr,sizeof(Sample));
}

UnloadSample(name)
char *name;
{
	Sample *samptr;

#ifdef DEBUG
	printf("UnloadSample(%s)\n",name);
#endif
	
	if ((samptr = (Sample *)FindName(SampleList,name)) != NULL)
		UnloadSampleAt(samptr);
	else
		fprintf(stderr,"couldn't find sample %s to unload it\n",name);
}

#ifdef DEBUG
DumpVoiceHead(vptr)
Voice8Header *vptr;
{
	printf("oneShotHiSamples %d, repeatHiSamples %d\n",
		vptr->oneShotHiSamples, vptr->repeatHiSamples);
	printf("samplesPerHiCycle %ld, samplesPerSec %d\n",
		vptr->samplesPerHiCycle, vptr->samplesPerSec);
	printf("ctOctave %d, sCompression %d, volume %d\n",
		vptr->ctOctave, vptr->sCompression, vptr->volume);
}
#endif

/* end of 8svx.c */
