
/*
	$VER: 8svx.c (12.08.96)

	8SVX play module

	(c) Copyright 1995-2001 Grzegorz Calkowski
	based on original code (Play8SVX.c) from EA & CBM

	This file is part of SysPic.

	SysPic  is free software; you can redistribute it and/or modify
	it under the terms of the GNU General Public License as published by
	the Free Software Foundation; either version 2, or (at your option)
	any later version.

	Bison is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
	GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with Bison; see the file COPYING.  If not, write to
	the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.

*/

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

#define __USE_SYSBASE
#include <proto/exec.h>
#include <proto/dos.h>

#include "8svxinfo.h"

#include <string.h>

/* protos */

static LONG readvhdr(struct ESVXInfo *esvx, ULONG len);
static LONG readchan(struct ESVXInfo *esvx, ULONG len);
static LONG readbody(struct ESVXInfo *esvx, ULONG len);
static LONG readraw(struct ESVXInfo *esvx);
static void DUnpack(BYTE source[], LONG n, BYTE dest[]);

extern struct ExecBase *SysBase;

extern ULONG memrev;

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

#define NTSC_CLOCK 3579545L

static UBYTE	monoch[] = { 1,2,4,8 }, stereoch[] = { 3, 5, 10, 12 };

/* periods for scale starting at	  65.40Hz (C) with 128 samples per cycle
 *										or	 130.81Hz (C) with  64 samples per cycle
 *										or	 261.63Hz (C) with  32 samples per cycle
 *										or	 523.25Hz (C) with  16 samples per cycle
 *										or 1046.50Hz (C) with	8 samples per cycle
 *										or 2093.00Hz (C) with	4 samples per cycle
 */

static UWORD  per_ntsc[12]= { 428, 404, 380, 360,
										340, 320, 302, 286,
										270, 254, 240, 226 };

/* periods adjusted for system clock frequency */
static UWORD	per[12];
static ULONG	eclock;
UBYTE				periods_done;


/****** esvx/ESVX_Alloc *******************************************************
*
*  NAME
*		ESVX_Alloc -- allocate ESVXInfo structure
*
*  SYNOPSIS
*		ESVXInfo = ESVX_Alloc()
*
*		struct ESVXInfo *ESVX_Alloc(void)
*
*  FUNCTION
*		Just allocates ESVXInfo structure. Use for future compatibility.
*
*  RESULT
*		ESVXInfo -- cleared ESVXInfo structure
*
*	SEE ALSO
*		ESVX_Free
*
******************************************************************************/

struct ESVXInfo *ESVX_Alloc(void)
{
	struct ESVXInfo *esvx;

	if (!(esvx = (struct ESVXInfo *)AllocMem(sizeof(struct ESVXInfo), MEMF_CHIP|memrev)))
		return NULL;

	return esvx;
}


/****** esvx/ESVX_Free ********************************************************
*
*  NAME
*		ESVX_Free -- free ESVXInfo
*
*  SYNOPSIS
* 		ESVX_Free(ESVXInfo)
*
*		void ESVX_Free(struct ESVXInfo *)
*
*  FUNCTION
*		Calls ESVX_UnLoad() on given ESVXInfo structure and then frees it.
*		Safe to call with NULL.
*
*  SEE ALSO
*		ESVX_Alloc, ESVX_UnLoad
*
******************************************************************************/

void ESVX_Free(struct ESVXInfo *esvx)
{
	if (esvx)
	{
		ESVX_UnLoad(esvx);
		FreeMem(esvx, sizeof(struct ESVXInfo));
	}
}


/****** esvx/ESVX_InitAudio ***************************************************
*
*  NAME
*		ESVX_InitAudio -- allocate iorequests and open audio.device
*
*  SYNOPSIS
*		error = ESVX_InitAudio(ESVXInfo, stereo, prec)
*
*		LONG ESVX_InitAudio(struct ESVXInfo *, UBYTE, BYTE)
*
*  FUNCTION
*		Creates msgport, allocates AudioIO requestes	and opens audio.device.
*		Also calculates period values for one octave based on system clock.
*
*  INPUTS
*		ESVXInfo	- control structure (NULL causes error)
*		stereo	- boolean (we want 2 oposite channels)
*		prec		- precedence (priority)
*
*  RESULT
*		0 if ok, -1 otherwise
*
*	SEE ALSO
*		ESVX_FreeAudio
*
******************************************************************************/

LONG ESVX_InitAudio(struct ESVXInfo *esvx, UBYTE stereo, BYTE prec)
{
	struct IOAudio *aio;

	if (!esvx) return -1;
	if (esvx->devopened)	return -1;

	if (!periods_done)
	{
		ULONG	 period;
		WORD	 k;

		eclock = SysBase->ex_EClockFrequency * 5;

		/* calculate period values for one octave based on system clock */
		for (k=0; k<12; k++)
		{
			period = ((per_ntsc[k] * eclock) + (NTSC_CLOCK >> 1)) / NTSC_CLOCK;
			per[k] = period;
		}
	}

	if (esvx->port = CreateMsgPort())
	{
		if (!(esvx->aio = (struct IOAudio *)CreateIORequest(esvx->port, sizeof(struct IOAudio))))
			goto failed;
		if (!(esvx->aio1 = (struct IOAudio *)CreateIORequest(esvx->port, sizeof(struct IOAudio))))
			goto failed;
		if (!(esvx->aio1_rep = (struct IOAudio *)CreateIORequest(esvx->port, sizeof(struct IOAudio))))
			goto failed;

		if (stereo)
		{
			if (!(esvx->aio2 = (struct IOAudio *)CreateIORequest(esvx->port, sizeof(struct IOAudio))))
				goto failed;
			if (!(esvx->aio2_rep = (struct IOAudio *)CreateIORequest(esvx->port, sizeof(struct IOAudio))))
				goto failed;
		}
	}
	else
		goto failed;

	/*----------------------------------------------------------------------*/
	/* Set up the audio I/O block for channel allocation:							*/
	/* ioa_Request.io_Message.mn_ReplyPort is the address of a reply port.	*/
	/* ioa_Request.io_Message.mn_Node.ln_Pri sets the precedence (priority) */
	/*	  of our use of the audio device. Any tasks asking to use the audio	*/
	/*	  device that have a higher precedence will steal the channel from us.*/
	/* ioa_Request.io_Command is the command field for IO.						*/
	/* ioa_Request.io_Flags is used for the IO flags.								*/
	/* ioa_AllocKey will be filled in by the audio device if the allocation */
	/*	  succeeds. We must use the key it gives for all other commands sent.*/
	/* ioa_Data is a pointer to the array listing the channels we want.		*/
	/* ioa_Length tells how long our list of channels is.							*/
	/*----------------------------------------------------------------------*/

	aio = esvx->aio;

	aio->ioa_Request.io_Message.mn_Node.ln_Pri = prec;

	aio->ioa_Request.io_Command	= ADCMD_ALLOCATE;
	aio->ioa_Request.io_Flags		= ADIOF_NOWAIT;
	aio->ioa_AllocKey					= 0;
	aio->ioa_Data						= stereo ? stereoch : monoch;
	aio->ioa_Length					= 4;

	// Open the audio device and allocate a channel(s)

	if (OpenDevice("audio.device", 0L, (struct IORequest *)aio, 0L))
		goto failed;

	*esvx->aio1 = *aio;

	esvx->devopened = TRUE;

	if (stereo)
	{
		UBYTE unit = (ULONG)aio->ioa_Request.io_Unit, unit2;
		switch (unit)
		{
			case 3:  unit2 = 2; break;
			case 5:  unit2 = 4; break;
			case 10: unit2 = 8; break;
			case 12: unit2 = 8; break;
		}
		esvx->aio1->ioa_Request.io_Unit = (APTR)(unit-unit2);
		*esvx->aio2 = *aio;
		esvx->aio2->ioa_Request.io_Unit = (APTR)unit2;
	}

	/* Clone the flags, channel allocation, etc. into other IOAudio requests */
	*esvx->aio1_rep = *esvx->aio1;
	if (stereo)
		*esvx->aio2_rep = *esvx->aio2;

	return 0;

	failed:
	ESVX_FreeAudio(esvx);
	return -1;
}


/****** esvx/ESVX_FreeAudio ***************************************************
*
*  NAME
*		ESVX_FreeAudio -- close audio.device
*
*  SYNOPSIS
*		ESVX_FreeAudio(ESVXInfo)
*
*		void ESVX_FreeAudio(struct ESVXInfo *)
*
*  FUNCTION
*		Closes audio.device, deletes iorequestes and msgport.
*
*  INPUTS
*		ESVXInfo	- control structure (or NULL)
*
*	SEE ALSO
*		ESVX_InitAudio
*
******************************************************************************/


void ESVX_FreeAudio(struct ESVXInfo *esvx)
{
	if (!esvx)
		return;

	/* Note - we know we have no outstanding audio requests */
	if (esvx->devopened)
	{
		CloseDevice((struct IORequest *)esvx->aio);
	 	esvx->devopened = FALSE;
	}

	DeleteIORequest(esvx->aio);
	esvx->aio = NULL;
	DeleteIORequest(esvx->aio1);
	esvx->aio1 = NULL;
	DeleteIORequest(esvx->aio1_rep);
	esvx->aio1_rep = NULL;
	DeleteIORequest(esvx->aio2);
	esvx->aio2 = NULL;
	DeleteIORequest(esvx->aio2_rep);
	esvx->aio2_rep = NULL;

	DeleteMsgPort(esvx->port);
	esvx->port = NULL;
}

/****** esvx/ESVX_Play ********************************************************
*
*  NAME
*     ESVX_Play -- play the sample
*
*  SYNOPSIS
*		error ESVX_Play(ESVXInfo, octave, note, volume, stereo)
*
*		LONG ESVX_Play(struct ESVXInfo *, LONG, LONG, WORD, UBYTE)
*
*  FUNCTION
*		Play note in octave or play a sound effect (set octave and note to 0,
*		-1, respectively). 'stereo' forces stereo-mode (i.e mono samples will
*		be played on both speakers). Plays samples only <= 128k. Repeat portion
*		is played "forever".
*
*		Calls ESVX_InitAudio() when necessary.
*
*		When playing notes:
*		Expects note values between 0 (C) and 11 (B#)
*		Uses largest octave sample in 8SVX as octave 0, next smallest
*		as octave 1, etc.
*
*  INPUTS
*		ESVXInfo	- control structure (if NULL error returned)
*		octave	- octave to play note in (if too high will be zeroed)
*		note		- note to play (-1 to play at default rate)
*		volume	- volume to play at
*		stereo	- force stereo play
*
*  RESULT
*		0 if ok, -1 if ESVX_InitAudio() failed
*
*	SEE ALSO
*		ESVX_InitAudio, ESVX_Flush
*
******************************************************************************/

LONG ESVX_Play(struct ESVXInfo *esvx, LONG octave, LONG note, WORD volume, UBYTE stereo)
{
	struct IOAudio *aio1, *aio2, *aio1_rep, *aio2_rep;
	ULONG	 period;
	LONG	 osize, rsize;
	BYTE	 *oneshot, *oneshot2, *repeat, *repeat2;

	if (!esvx)
		return -1;

	if (!esvx->devopened)
	{
		stereo |= esvx->stereo;
		if (ESVX_InitAudio(esvx, stereo, 0))
			if (ESVX_InitAudio(esvx, 0 /*mono*/, 0))
				return -1;
	}

	if (!esvx->aio2) stereo = FALSE;

	if (note > 11) note=0;

	if (note == -1)
		period = eclock / esvx->vhdr.samplesPerSec;
	else
		period = per[note]; /* table set up by OpenAudio */

	if (octave > esvx->vhdr.ctOctave)
		octave = 0;
	if (volume == -1)
		volume = esvx->vhdr.volume >> 10;
	if (volume > 64)
		volume = 64;

	oneshot = esvx->osamps[octave];
	osize	  = esvx->osizes[octave];

	/*------------------------------------------------------------*/
	/* Set up audio I/O blocks to play a sample using CMD_WRITE.  */
	/* The io_Flags are set to ADIOF_PERVOL so we can set the	  */
	/*		period (speed) and volume with the our sample;			  */
	/* ioa_Data points to the sample; ioa_Length gives the length */
	/* ioa_Cycles tells how many times to repeat the sample		  */
	/* If you want to play the sample at a given sampling rate,	  */
	/* set ioa_Period = clock/(given sampling rate)					  */
	/*------------------------------------------------------------*/

	aio1 = esvx->aio1;

	aio1->ioa_Request.io_Command		= CMD_WRITE;
	aio1->ioa_Request.io_Flags			= ADIOF_PERVOL;
	aio1->ioa_Data							= oneshot;
	aio1->ioa_Length						= osize;
	aio1->ioa_Period						= period;
	aio1->ioa_Volume					 	= volume;
	aio1->ioa_Cycles						= 1;

	if (stereo)
	{
		oneshot2 = oneshot;
		if (esvx->stereo)
			oneshot2 += (esvx->samplebytes >> 1);

		aio2 = esvx->aio2;

		aio2->ioa_Request.io_Command		= CMD_WRITE;
		aio2->ioa_Request.io_Flags			= ADIOF_PERVOL;
		aio2->ioa_Data							= oneshot2;
		aio2->ioa_Length						= osize;
		aio2->ioa_Period						= period;
		aio2->ioa_Volume					 	= volume;
		aio2->ioa_Cycles						= 1;
	}

	if (rsize = esvx->rsizes[octave])
	{
		repeat  = esvx->rsamps[octave];

		aio1_rep = esvx->aio1_rep;

		aio1_rep->ioa_Request.io_Command	= CMD_WRITE;
		aio1_rep->ioa_Request.io_Flags	= ADIOF_PERVOL;
		aio1_rep->ioa_Data					= repeat;
		aio1_rep->ioa_Length					= rsize;
		aio1_rep->ioa_Period					= period;
		aio1_rep->ioa_Volume					= volume;
		aio1_rep->ioa_Cycles					= 0;	/* repeat until stopped */

		if (stereo)
		{
			repeat2 = repeat;
			if (esvx->stereo)
				repeat2 += (esvx->samplebytes >> 1);

			aio2_rep = esvx->aio2_rep;

			aio2_rep->ioa_Request.io_Command	= CMD_WRITE;
			aio2_rep->ioa_Request.io_Flags	= ADIOF_PERVOL;
			aio2_rep->ioa_Data					= repeat2;
			aio2_rep->ioa_Length					= rsize;
			aio2_rep->ioa_Period					= period;
			aio2_rep->ioa_Volume					= volume;
			aio2_rep->ioa_Cycles					= 0;	/* repeat until stopped */
		}
	}

	/*---------------------------------------------------*/
	/* Send the command to start a sound using BeginIO() */
	/*---------------------------------------------------*/

	if (osize)	/* Simple case for oneshot sample <= 128K (ie. most samples) */
	{
		BeginIO((struct IORequest *)aio1);
		if (stereo)
			BeginIO((struct IORequest *)aio2);
	}

	if (rsize)	/* Simple case for repeat sample <= 128K (ie. most samples) */
	{
		BeginIO((struct IORequest *)aio1_rep);
		if (stereo)
			BeginIO((struct IORequest *)aio2_rep);

/*		/* Wait for any requests we still have out */
		if (aio)
			WaitIO((struct IORequest *)aio);

		if (aio_rep)
		{
			if (note >= 0)
				AbortIO((struct IORequest *)aio_rep); /* if a note, stop it now */
			WaitIO((struct IORequest *)aio_rep);
		}
*/
	}

	return 0;
}

/****** esvx/ESVX_Flush *******************************************************
*
*  NAME
*		ESVX_Flush -- finish all requests (i.e stop playing)
*
*  SYNOPSIS
*		ESVX_Flush(ESVXInfo)
*
*		void ESVX_Flush(struct ESVXInfo *)
*
*  FUNCTION
*		Calls CMD_FLUSH on AudioIO.
*
*	INPUTS
*		ESVXInfo - ESVXInfo structure	(or NULL)
*
******************************************************************************/

void ESVX_Flush(struct ESVXInfo *esvx)
{
	if (esvx)
		if (esvx->devopened)
		{
			esvx->aio->ioa_Request.io_Command = CMD_FLUSH;
			BeginIO((struct IORequest *)esvx->aio);
			WaitIO((struct IORequest *)esvx->aio);
		}
}

/****** esvx/ESVX_Load ********************************************************
*
*  NAME
*		ESVX_Load -- loads sample into memory
*
*  SYNOPSIS
*		error = ESVX_Load(ESVXInfo, filename, allowraw)
*
*		LONG ESVX_Load(struct ESVXInfo *, UBYTE *, BOOL)
*
*  FUNCTION
*		Loads IFF-8SVX sample into memory and initializes ESVXInfo structure.
*     Supports stereo samples and fibbonaci compression. If 'allowraw' is
*		TRUE, also raw samples.
*
*  INPUTS
*		ESVXInfo - ESVXInfo structure
*		filename - sample filename to load
*		allowraw - boolean
*
*  RESULT
*		error - error code definied in iff.h and esvx.h
*
*  SEE ALSO
*		iff.h, esvx.h
*
******************************************************************************/

LONG ESVX_Load(struct ESVXInfo *esvx, UBYTE *filename, BOOL allowraw)
{
	LONG	 error = 1L;
	ULONG	 buf[3], len;

	BYTE	 *oneshot, *repeat;
	ULONG	 osize, rsize, spcyc;
	WORD	 oct;

	if (!esvx)	return CLIENT_ERROR;

	memset(esvx, 0, sizeof(struct ESVXInfo));

	if (!(esvx->fh = Open(filename, MODE_OLDFILE)))
		return IFFERR_NOFILE;

	/* read header (first 3 longs) */
	if (FRead(esvx->fh, buf, 4, 3) == 3)
	{
		/* must be equal to FORM????8SVX */
		if (buf[0] == ID_FORM && buf[2] == ID_8SVX)
		{
			len = buf[1] - 4;

			while (len)
			{
				/* buf[0] - id, buf[1] - len */
				if (FRead(esvx->fh, buf, 4, 2) != 2)
				{
					error = IFFERR_IO;
					goto bailoff;
				}

				switch (buf[0])
				{
					case ID_VHDR:
						if (error = readvhdr(esvx, buf[1]))
							goto bailoff;
						break;

					case ID_CHAN:
						if (error = readchan(esvx, buf[1]))
							goto bailoff;
						break;

					case ID_BODY:
						error = readbody(esvx, buf[1]);
						goto bailoff;

					default:
						Seek(esvx->fh, buf[1], OFFSET_CURRENT);
				}

				len -= buf[1];

				if (buf[1] & 1)
				{
					Seek(esvx->fh, 1, OFFSET_CURRENT);
					len--;
				}
			}
		}
		else
			if (allowraw)
				error = readraw(esvx);
			else
				error = ESVXERR_NOT8SVX;
	}		
	else
		error = IFFERR_IO;

	bailoff:

	if (esvx->fh)
		Close(esvx->fh);

	if (error)
		ESVX_UnLoad(esvx);
	else
	{
		osize	  = esvx->vhdr.oneShotHiSamples & ~1;
		rsize	  = esvx->vhdr.repeatHiSamples & ~1;
		spcyc = esvx->vhdr.samplesPerHiCycle;
		if (!spcyc)
			spcyc = esvx->vhdr.repeatHiSamples;
		if (!spcyc)
			spcyc = 8;

		oneshot = esvx->sample;

		for(oct = esvx->vhdr.ctOctave-1; oct >= 0;
			 oct--, oneshot += (osize+rsize), osize <<= 1, rsize <<=1, spcyc <<=1)
	 	{
	 		repeat = oneshot + osize;
 			esvx->osizes[oct] = osize;
 			if (osize)
				esvx->osamps[oct] = oneshot;
 			else
				esvx->osamps[oct] = 0;

 			esvx->rsizes[oct] = rsize;
 			if (rsize)
				esvx->rsamps[oct] = repeat;
			else
				esvx->rsamps[oct] = 0;

			esvx->spcycs[oct] = spcyc;
 		}
	}

	return error;
}

/****** esvx/ESVX_UnLoad ******************************************************
*
*  NAME
*		ESVX_UnLoad -- unload sample
*
*  SYNOPSIS
*		ESVX_UnLoad(ESVXInfo)
*
*		void ESVX_UnLoad(struct ESVXInfo *)
*
*  FUNCTION
*		Unloads sample from memory and clears (to NULLs) ESVXInfo structure.
*
*  INPUTS
*		ESVXInfo - ESVXInfo structure (or NULL)
*
******************************************************************************/

void ESVX_UnLoad(struct ESVXInfo *esvx)
{
	if (esvx)
	{
		if (esvx->devopened)
		{
			ESVX_Flush(esvx);
			ESVX_FreeAudio(esvx);
		}

		if (esvx->sample)
		{
			FreeMem(esvx->sample, esvx->samplebytes);
			memset(esvx, 0, sizeof(struct ESVXInfo));
		}
	}
}

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

static LONG readvhdr(struct ESVXInfo *esvx, ULONG len)
{
	if (len != sizeof(Voice8Header) || esvx->vhdrread)
		return IFFERR_MANGLED;

	if (FRead(esvx->fh, &esvx->vhdr, len, 1) != 1)
		return IFFERR_IO;

	esvx->vhdrread = TRUE;

	return 0; /* ok */
}

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

static LONG readchan(struct ESVXInfo *esvx, ULONG len)
{
	ULONG tmp;

	if (len != 4)
		return IFFERR_MANGLED;

	if (FRead(esvx->fh, &tmp, len, 1) != 1)
		return IFFERR_IO;

	esvx->stereo = (tmp == 6);

	return 0; /* ok */
}

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

static LONG readbody(struct ESVXInfo *esvx, ULONG len)
{
	LONG	error = 0;
	Voice8Header *vhdr = &esvx->vhdr;
	ULONG memtype;
	UBYTE *tmp;

	if (esvx->vhdrread)
	{
		/* if we have to decompress, let's just load it anywhere */
		memtype = vhdr->sCompression ? MEMF_ANY : MEMF_CHIP;
		memtype |= memrev;

		if (esvx->sample = AllocMem(len, memtype))
		{
			Flush(esvx->fh);

			if (Read(esvx->fh, esvx->sample, len) != len)
				error = IFFERR_IO;

			esvx->samplebytes = len;
		}
		else
			error = IFFERR_NOMEM;
	}
	else
		error = IFFERR_MANGLED;

	if (!error)
	{
		if (vhdr->sCompression == sCmpFibDelta ) /* Decompress, if needed. */
		{
			if (tmp = AllocMem(len << 1, MEMF_CHIP | memrev)) 
			{
				DUnpack(esvx->sample, len, tmp); 
				FreeMem(esvx->sample, len); 
				esvx->sample = tmp;
				esvx->samplebytes = len << 1;
			}
		 	else
				error = IFFERR_NOMEM;
		} 
		else
			if (vhdr->sCompression)	/* unknown ompression method */
		 		error = CLIENT_ERROR;
	}
	else
		ESVX_UnLoad(esvx);

	return(error);
} 

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

static LONG readraw(struct ESVXInfo *esvx)
{
	BPTR fh = esvx->fh;
	LONG len, error = 0L;

	Seek(fh, 0, OFFSET_END);
	len = Seek(fh, 0, OFFSET_BEGINING);

	if (len > 0x20000) /* 128k */
		len = 0x20000;

	/* must be even */
	len &= ~1;

	if (esvx->sample = AllocMem(len, MEMF_CHIP | memrev))
	{
		Flush(fh);

		if (Read(fh, esvx->sample, len) != len)
			error = IFFERR_IO;

		esvx->samplebytes = len;
		esvx->vhdr.oneShotHiSamples = len;
		esvx->vhdr.samplesPerSec = 10000;
		esvx->vhdr.ctOctave = 1;
		esvx->vhdr.volume = Unity;

	}
	else
		error = IFFERR_NOMEM;

	return error;
}

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

/* DUnpack.c --- Fibonacci Delta decompression by Steve Hayes */

/* Fibonacci delta encoding for sound data */
BYTE codeToDelta[16] = {-34,-21,-13,-8,-5,-3,-2,-1,0,1,2,3,5,8,13,21};

/* Unpack Fibonacci-delta encoded data from n byte source
 * buffer into 2*n byte dest buffer, given initial data
 * value x.	 It returns the lats data value x so you can
 * call it several times to incrementally decompress the data.
 */

static BYTE D1Unpack(BYTE source[], LONG n, BYTE dest[], BYTE x)
	{
	BYTE d;
	LONG i, lim;

	lim = n << 1;
	for (i=0; i < lim; ++i)
		{
		/* Decode a data nibble, high nibble then low nibble */
		d = source[i >> 1];	  /* get a pair of nibbles */
		if (!(i & 1))				  /* select low or high nibble */
			d >>= 4;				  /* shift to get the high nibble */
		d &= 0xf;			  /* mask to get the low nibble */
		x += codeToDelta[d];	  /* add in the decoded delta */
		dest[i] = x;			  /* store a 1 byte sample */
		}
	return(x);
	}


/* Unpack Fibonacci-delta encoded data from n byte
 * source buffer into 2*(n-2) byte dest buffer.
 * Source buffer has a pad byte, an 8-bit initial
 * value, followed by n-2 bytes comprising 2*(n-2)
 * 4-bit encoded samples.
 */

static void DUnpack(BYTE source[], LONG n, BYTE dest[])
{
	D1Unpack(source+2, n-2, dest, source[1]);
}


/****** esvx/ESVX_Setup *******************************************************
*
*  NAME
*		ESVX_Setup --	set volume and rate
*
*  SYNOPSIS
*		ESVX_Setup(struct ESVXInfo *esvx, LONG rate, LONG volume)
*
*		void ESVX_Setup(struct ESVXInfo *, LONG, LONG)
*
*  FUNCTION
*		Changes default rate and volume values in esvx->vhdr structure.
*		Changes will take effect on another ESVX_Play() call.
*		Value of -1 preserves current setting.
*
*  INPUTS
*		ESVXInfo - ESVXInfo structure (or NULL)
*		rate		- new rate (or -1)
*		volume	- new volume (or -1)
*
******************************************************************************/

void ESVX_Setup(struct ESVXInfo *esvx, LONG rate, LONG volume)
{
	if (!esvx)
		return;

	if (rate != -1)
		esvx->vhdr.samplesPerSec = rate;

	if (volume != -1)
		esvx->vhdr.volume = volume << 10;
}
