C++程序  |  1483行  |  43.1 KB

/*----------------------------------------------------------------------------
 *
 * File: 
 * eas_pcm.c
 *
 * Contents and purpose:
 * Implements the PCM engine including ADPCM decode for SMAF and CMX audio playback.
 *
 * Copyright Sonic Network Inc. 2005

 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *----------------------------------------------------------------------------
 * Revision Control:
 *   $Revision: 849 $
 *   $Date: 2007-08-28 08:59:11 -0700 (Tue, 28 Aug 2007) $
 *----------------------------------------------------------------------------
*/

#include "eas_data.h"
#include "eas_report.h"
#include "eas_host.h"
#include "eas_config.h"
#include "eas_parser.h"
#include "eas_pcm.h"
#include "eas_math.h"
#include "eas_mixer.h"

#define PCM_MIXER_GUARD_BITS (NUM_MIXER_GUARD_BITS + 1)

/*----------------------------------------------------------------------------
 * Decoder interfaces
 *----------------------------------------------------------------------------
*/

static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState);
static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time);

static const S_DECODER_INTERFACE PCMDecoder = 
{
	NULL,
	LinearPCMDecode,
	LinearPCMLocate,
};

/* SMAF ADPCM decoder */
#ifdef _SMAF_PARSER
extern S_DECODER_INTERFACE SmafDecoder;
#define SMAF_DECODER &SmafDecoder
extern S_DECODER_INTERFACE Smaf7BitDecoder;
#define SMAF_7BIT_DECODER &Smaf7BitDecoder
#else
#define SMAF_DECODER NULL
#define SMAF_7BIT_DECODER NULL
#endif

/* IMA ADPCM decoder */
#ifdef _IMA_DECODER
extern S_DECODER_INTERFACE IMADecoder;
#define IMA_DECODER &IMADecoder
#else
#define IMA_DECODER NULL
#endif

static const S_DECODER_INTERFACE * const decoders[] =
{
	&PCMDecoder,
	SMAF_DECODER,
	IMA_DECODER,
	SMAF_7BIT_DECODER
};

/*----------------------------------------------------------------------------
 * Sample rate conversion
 *----------------------------------------------------------------------------
*/

#define SRC_RATE_MULTIPLER (0x40000000 / _OUTPUT_SAMPLE_RATE)

#ifdef _LOOKUP_SAMPLE_RATE	
static const EAS_U32 srcConvRate[][2] =
{
	4000L, (4000L << 15) / _OUTPUT_SAMPLE_RATE,
	8000L, (8000L << 15) / _OUTPUT_SAMPLE_RATE,
	11025L, (11025L << 15) / _OUTPUT_SAMPLE_RATE,
	12000L, (12000L << 15) / _OUTPUT_SAMPLE_RATE,
	16000L, (16000L << 15) / _OUTPUT_SAMPLE_RATE,
	22050L, (22050L << 15) / _OUTPUT_SAMPLE_RATE,
	24000L, (24000L << 15) / _OUTPUT_SAMPLE_RATE,
	32000L, (32000L << 15) / _OUTPUT_SAMPLE_RATE
};
static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate);
#define SRC_CONV_RATE_ENTRIES (sizeof(srcConvRate)/sizeof(EAS_U32)/2)
#endif


/* interface prototypes */
static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples);


/* local prototypes */
static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData);
static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState);

/*----------------------------------------------------------------------------
 * EAS_PEInit()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Initializes the PCM engine
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PEInit (S_EAS_DATA *pEASData)
{
	S_PCM_STATE *pState;
	EAS_INT i;

	/* check for static memory allocation */
	if (pEASData->staticMemoryModel)
		pEASData->pPCMStreams = EAS_CMEnumData(EAS_CM_PCM_DATA);
	/* allocate dynamic memory */
	else
		pEASData->pPCMStreams = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS);

	if (!pEASData->pPCMStreams)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate memory for PCM streams\n"); */ }
		return EAS_ERROR_MALLOC_FAILED;
	}
	
	//zero the memory to insure complete initialization
	EAS_HWMemSet((void *)(pEASData->pPCMStreams),0, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS);

	/* initialize the state data */
	for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++)
		pState->fileHandle = NULL;
	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEShutdown()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Shuts down the PCM engine
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PEShutdown (S_EAS_DATA *pEASData)
{

	/* free any dynamic memory */
	if (!pEASData->staticMemoryModel)
	{
		if (pEASData->pPCMStreams)
		{
			EAS_HWFree(pEASData->hwInstData, pEASData->pPCMStreams);
			pEASData->pPCMStreams = NULL;
		}
	}	
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PERender()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Render a buffer of PCM audio
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PERender (S_EAS_DATA* pEASData, EAS_I32 numSamples)
{
	S_PCM_STATE *pState;
	EAS_RESULT result;
	EAS_INT i;

	/* render all the active streams */	
	for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++)
	{
		if ((pState->fileHandle) && (pState->state != EAS_STATE_STOPPED) && (pState->state != EAS_STATE_PAUSED))
			if ((result = RenderPCMStream(pEASData, pState, numSamples)) != EAS_SUCCESS)
				return result;
	}
	return EAS_SUCCESS;
}


/*----------------------------------------------------------------------------
 * EAS_PEState()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Returns the current state of the stream
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 * pState			- pointer to variable to store state
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 * Notes:
 * This interface is also exposed in the internal library for use by the other modules.
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEState (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pInstData, EAS_STATE *pState)
{
	/* return current state */
	*pState = pInstData->state;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEClose()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Close the file and clean up
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PEClose (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState)
{
	EAS_RESULT result;

	if ((result = EAS_HWCloseFile(pEASData->hwInstData, pState->fileHandle)) != EAS_SUCCESS)
		return result;

	pState->fileHandle = NULL;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * PCM_Reset()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Reset the sequencer. Used for locating backwards in the file.
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * handle			- pointer to file handle
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PEReset (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState)
{
	EAS_RESULT result;

	/* reset file position to first byte of data in the stream */
	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Error %d seeking to start of PCM file\n", result); */ }
		return result;
	}

	/* re-initialize stream */
	return InitPCMStream(pEASData, pState);
}

/*----------------------------------------------------------------------------
 * EAS_PEOpenStream()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Starts up a PCM playback
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PEOpenStream (S_EAS_DATA *pEASData, S_PCM_OPEN_PARAMS *pParams, EAS_PCM_HANDLE *pHandle)
{
	EAS_RESULT result;
	S_PCM_STATE *pState;
	EAS_I32 filePos;

	/* make sure we support this decoder */
	if (pParams->decoder >= NUM_DECODER_MODULES)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder selector out of range\n"); */ }
		return EAS_ERROR_PARAMETER_RANGE;
	}
	if (decoders[pParams->decoder] == NULL)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder module not available\n"); */ }
		return EAS_ERROR_FEATURE_NOT_AVAILABLE;
	}
	
	/* find a slot for the new stream */
	if ((pState = FindSlot(pEASData, pParams->fileHandle, pParams->pCallbackFunc, pParams->cbInstData)) == NULL)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unable to open ADPCM stream, too many streams open\n"); */ }
		return EAS_ERROR_MAX_PCM_STREAMS;
	}

	/* get the current file position */
	if ((result = EAS_HWFilePos(pEASData->hwInstData, pState->fileHandle, &filePos)) != EAS_SUCCESS)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "EAS_HWFilePos returned %ld\n",result); */ }
		pState->fileHandle = NULL;
		return result;
	}
	
	pState->pDecoder = decoders[pParams->decoder];
	pState->startPos = filePos;
	pState->bytesLeftLoop = pState->byteCount = pParams->size;
	pState->loopStart = pParams->loopStart;
	pState->samplesTilLoop = (EAS_I32) pState->loopStart;
	pState->loopSamples = pParams->loopSamples;
	pState->samplesInLoop = 0;
	pState->blockSize = (EAS_U16) pParams->blockSize;
	pState->flags = pParams->flags;
	pState->envData = pParams->envData;
	pState->volume = pParams->volume;
	pState->sampleRate = (EAS_U16) pParams->sampleRate;

	/* set the base frequency */
	pState->basefreq = (SRC_RATE_MULTIPLER * (EAS_U32) pParams->sampleRate) >> 15;

	/* calculate shift for frequencies > 1.0 */
	pState->rateShift = 0;
	while (pState->basefreq > 32767)
	{
		pState->basefreq = pState->basefreq >> 1;
		pState->rateShift++;
	}

	/* initialize */
	if ((result = InitPCMStream(pEASData, pState)) != EAS_SUCCESS)
		return result;
	
	*pHandle = pState;
	
	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PEOpenStream: StartPos=%d, byteCount = %d, loopSamples=%d\n", 
		pState->startPos, pState->byteCount, pState->loopSamples); */ }
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEContinueStream()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Continues a PCM stream
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -e{715} reserved for future use */
EAS_RESULT EAS_PEContinueStream (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 size)
{

	/* add new samples to count */
	pState->bytesLeft += size;
	if (pState->bytesLeft > 0)
		pState->flags &= ~PCM_FLAGS_EMPTY;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEGetFileHandle()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Returns the file handle of a stream
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEGetFileHandle (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_FILE_HANDLE *pFileHandle)
{
	*pFileHandle = pState->fileHandle;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEUpdateParams()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Update the pitch and volume parameters for a PCM stream
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * handle			- pointer to S_PCM_STATE for this stream
 * gainLeft			- linear gain multipler in 1.15 fraction format
 * gainRight		- linear gain multipler in 1.15 fraction format
 * pitch			- pitch shift in cents
 * initial			- initial settings, set current gain
 *
 * Outputs:
 * 
 *
 * Side Effects:
 *
 * Notes
 * In mono mode, leftGain controls the output gain and rightGain is ignored
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
/*lint -esym(715, gainRight) used only in 2-channel version */
EAS_RESULT EAS_PEUpdateParams (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch, EAS_I16 gainLeft, EAS_I16 gainRight)
{

	pState->gainLeft = gainLeft;
	
#if (NUM_OUTPUT_CHANNELS == 2)
	pState->gainRight = gainRight;
#endif

	pState->pitch = pitch;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PELocate()
 *----------------------------------------------------------------------------
 * Purpose: 
 * This function seeks to the requested place in the file. Accuracy
 * is dependent on the sample rate and block size.
 *
 * Inputs:
 * pEASData			- pointer to overall EAS data structure
 * pState			- stream handle
 * time				- media time in milliseconds
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PELocate (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 time)
{
	if (pState->pDecoder->pfLocate == NULL)
		return EAS_ERROR_FEATURE_NOT_AVAILABLE;

	return pState->pDecoder->pfLocate(pEASData, pState, time);
}

/*----------------------------------------------------------------------------
 * EAS_PEUpdateVolume()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Update the volume parameters for a PCM stream
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * handle			- pointer to S_PCM_STATE for this stream
 * gainLeft			- linear gain multipler in 1.15 fraction format
 * gainRight		- linear gain multipler in 1.15 fraction format
 * initial			- initial settings, set current gain
 *
 * Outputs:
 * 
 *
 * Side Effects:
 *
 * Notes
 * In mono mode, leftGain controls the output gain and rightGain is ignored
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEUpdateVolume (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 volume)
{
	pState->volume = volume;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEUpdatePitch()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Update the pitch parameter for a PCM stream
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * pState			- pointer to S_PCM_STATE for this stream
 * pitch			- new pitch value in pitch cents
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEUpdatePitch (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch)
{
	pState->pitch = pitch;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEPause()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback
 * at the end of the next audio frame.
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * handle			- pointer to S_PCM_STATE for this stream
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEPause (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState)
{
	/* set state to stopping */
	pState->state = EAS_STATE_PAUSING;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * EAS_PEResume()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Resume rendering a PCM stream. Sets the gain target back to its
 * previous setting and restarts playback at the end of the next audio
 * frame.
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * handle			- pointer to S_PCM_STATE for this stream
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PEResume (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState)
{
	/* set state to stopping */
	pState->state = EAS_STATE_PLAY;
	return EAS_SUCCESS;
}

EAS_U32 getDecayScale(EAS_U32 index)
{
	EAS_U32 utemp;

	//envelope decay segment
	switch (index)
	{
	case 0:	//no decay
		utemp = 512;//32768;
		break;
	case 1: //.0156 dB per update
		utemp = 511;//32709;
		break;
	case 2: //.03125
		utemp = 510;//32649;
		break;
	case 3: //.0625
		utemp = 508;//32532;
		break;
	case 4: //.125
		utemp = 505;//32298;
		break;
	case 5: //.25
		utemp = 497;//31835;
		break;
	case 6: //.5
		utemp = 483;//30929;
		break;
	case 7: //1.0
		utemp = 456;//29193;
		break;
	case 8:	//2.0
		utemp = 406;//26008;
		break;
	case 9:	//4.0
		utemp = 323;//20642;
		break;
	case 10: //8.0
		utemp = 203;//13004;
		break;
	case 11: //16.0
		utemp = 81;//5160;
		break;
	case 12: //32.0
		utemp = 13;//813;
		break;
	case 13: //64.0
		utemp = 0;//20;
		break;
	case 14: //128.0
		utemp = 0;
		break;
	case 15: //256.0
	default:
		utemp = 0;
		break;
	}
	//printf("getdecayscale returned %d\n",utemp);
	return utemp;
}

EAS_U32 getAttackIncrement(EAS_U32 index)
{
	EAS_U32 utemp;

	//envelope decay segment
	switch (index)
	{
	case 0:	
		utemp = 32;
		break;
	case 1: 
		utemp = 64;
		break;
	case 2: 
		utemp = 128;
		break;
	case 3: 
		utemp = 256;
		break;
	case 4: 
		utemp = 512;
		break;
	case 5: 
		utemp = 1024;
		break;
	case 6: 
		utemp = 2048;
		break;
	case 7: 
		utemp = 4096;
		break;
	case 8:	
		utemp = 8192;
		break;
	case 9:	
		utemp = 16384;
		break;
	case 10: 
		utemp = 32768;
		break;
	case 11: 
		utemp = 65536;
		break;
	case 12: 
		utemp = 65536;
		break;
	case 13: 
		utemp = 65536;
		break;
	case 14: 
		utemp = 65535;
		break;
	case 15: 
	default:
		utemp = 0;
		break;
	}
	//printf("getattackincrement returned %d\n",utemp);
	return utemp;
}

/*----------------------------------------------------------------------------
 * EAS_PERelease()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Put the PCM stream envelope into release. 
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * handle			- pointer to S_PCM_STATE for this stream
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
/*lint -esym(715, pEASData) reserved for future use */
EAS_RESULT EAS_PERelease (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState)
{
	EAS_U32 utemp;

	//printf("handling note-off part of envelope\n");
	/*if the note is not ignore release or sustained*/
	if (((pState->envData >> 24) & 0x0F)==0)
	{
		/* set envelope state to release */
		pState->envState = PCM_ENV_RELEASE;
		utemp = ((pState->envData >> 20) & 0x0F);
		pState->envScale = getDecayScale(utemp); //getReleaseScale(utemp);
	}
	else
	{
		/*else change envelope state to sustain */
		pState->envState = PCM_ENV_SUSTAIN;
		utemp = ((pState->envData >> 28) & 0x0F);
		pState->envScale = getDecayScale(utemp); //getSustainScale(utemp);
	}
	//since we are in release, don't let anything hang around too long
	//printf("checking env scale, val = %d\n",((S_PCM_STATE*) handle)->envScale);
	if (pState->envScale > 505)
		pState->envScale = 505;
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * FindSlot()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Locates an empty stream slot and assigns the file handle
 *
 * Inputs:
 * pEASData			- pointer to EAS library instance data
 * fileHandle		- file handle
 * pCallbackFunc	- function to be called back upon EAS_STATE_STOPPED
 *		
 * Outputs:
 * returns handle to slot or NULL if all slots are used
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData)
{
	EAS_INT i;
	S_PCM_STATE *pState;

#ifndef NO_PCM_STEAL	
	S_PCM_STATE *foundState = NULL;
	EAS_INT count = 0;
	EAS_U32 startOrder = 0xFFFFFFFF;
	S_PCM_STATE *stealState = NULL;
	EAS_U32 youngest = 0;

	/* find an empty slot, count total in use, and find oldest in use (lowest start order) */
	for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++)
	{
		/* if this one is available */
		if (pState->fileHandle == NULL)
		{
			foundState = pState;
		}
		/* else this one is in use, so see if it is the oldest, and count total in use */
		/* also find youngest */
		else
		{
			/*one more voice in use*/
			count++;
			/* is this the oldest? (lowest start order) */
			if ((pState->state != EAS_STATE_STOPPING) && (pState->startOrder < startOrder))
			{
				/* remember this one */
				stealState = pState;
				/* remember the oldest so far */
				startOrder = pState->startOrder;
			}
			/* is this the youngest? (highest start order) */
			if (pState->startOrder >= youngest)
			{
				youngest = pState->startOrder;
			}
		}
	}

	/* if there are too many voices active, stop the oldest one */
	if (count > PCM_STREAM_THRESHOLD)
	{
		//printf("stealing!!!\n");
		/* make sure we got one, although we should always have one at this point */
		if (stealState != NULL)
		{
			//flag this as stopping, so it will get shut off
			stealState->state = EAS_STATE_STOPPING;
		}
	}

	/* if there are no available open streams (we won't likely see this, due to stealing) */
	if (foundState == NULL)
		return NULL;

	/* save info */
	foundState->startOrder = youngest + 1;
	foundState->fileHandle = fileHandle;
	foundState->pCallback = pCallbackFunc;
	foundState->cbInstData = cbInstData;
	return foundState;
#else
	/* find an empty slot*/
	for (i = 0; i < MAX_PCM_STREAMS; i++)
	{
		pState = &pEASData->pPCMStreams[i];
		if (pState->fileHandle != NULL)
			continue;
		
		pState->fileHandle = fileHandle;
		pState->pCallback = pCallbackFunc;
		pState->cbInstData = cbInstData;
		return pState;
	}
	return NULL;
#endif
}

#ifdef _LOOKUP_SAMPLE_RATE	
/*----------------------------------------------------------------------------
 * CalcBaseFreq()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Calculates the fractional phase increment for the sample rate converter
 *
 * Inputs:
 * sampleRate		- sample rate in samples/sec
 *		
 * Outputs:
 * Returns fractional sample rate with a 15-bit fraction
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate)
{
	EAS_INT i;

	/* look up the conversion rate */
	for (i = 0; i < (EAS_INT)(SRC_CONV_RATE_ENTRIES); i ++)
	{
		if (srcConvRate[i][0] == sampleRate)
			return srcConvRate[i][1];
	}

	/* if not found in table, do it the long way */
	{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Sample rate %u not in table, calculating by division\n", sampleRate); */ }

	return (SRC_RATE_MULTIPLER * (EAS_U32) sampleRate) >> 15;
}
#endif

/*----------------------------------------------------------------------------
 * InitPCMStream()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Start an ADPCM stream playback. Decodes the header, preps the engine.
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState)
{

	/* initialize the data structure */
	pState->bytesLeft = pState->byteCount;
	pState->phase = 0;
	pState->srcByte = 0;
	pState->decoderL.acc = 0;
	pState->decoderL.output = 0;
	pState->decoderL.x0 = pState->decoderL.x1 = 0;
	pState->decoderL.step = 0;
	pState->decoderR.acc = 0;
	pState->decoderR.output = 0;
	pState->decoderR.x0 = pState->decoderR.x1 = 0;
	pState->decoderR.step = 0;
	pState->hiNibble = EAS_FALSE;
	pState->pitch = 0;
	pState->blockCount = 0;
	pState->gainLeft = PCM_DEFAULT_GAIN_SETTING;
//	pState->currentGainLeft = PCM_DEFAULT_GAIN_SETTING;
	pState->envValue = 0;
	pState->envState = PCM_ENV_START;
	
#if (NUM_OUTPUT_CHANNELS == 2)
	pState->gainRight = PCM_DEFAULT_GAIN_SETTING;
//	pState->currentGainRight = PCM_DEFAULT_GAIN_SETTING;
#endif
	pState->state = EAS_STATE_READY;

	/* initialize the decoder */
	if (pState->pDecoder->pfInit)
		return (*pState->pDecoder->pfInit)(pEASData, pState);
	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * RenderPCMStream()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Decodes a buffer of ADPCM data.
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples)
{
	EAS_RESULT result;
	EAS_U32 phaseInc;
	EAS_I32 gainLeft, gainIncLeft;
	EAS_I32 *pOut;
	EAS_I32 temp;
	EAS_U32 utemp;
	
#if (NUM_OUTPUT_CHANNELS == 2)
	EAS_I32 gainRight, gainIncRight;
#endif

#if 0
	printf("env data: AR = %d, DR = %d, SL = %d, SR = %d, RR = %d\n",
		((pState->envData >> 12) & 0x0F),
		((pState->envData >> 16) & 0x0F),
		((pState->envData >> 8) & 0x0F),
		((pState->envData >> 28) & 0x0F),
		((pState->envData >> 20) & 0x0F));
#endif

	if (pState->envState == PCM_ENV_START)
	{
		//printf("env start\n");
		utemp = ((pState->envData >> 12) & 0x0F);
		//if fastest rate, attack is already completed
		//do the same for slowest rate, since that allows zero to be passed for default envelope
		if (utemp == 0x0F || utemp == 0x00)
		{
			//start envelope at full
			pState->envValue = (32768<<7);
			//jump right into decay
			utemp = ((pState->envData >> 16) & 0x0F);
			pState->envScale = getDecayScale(utemp);
			pState->envState = PCM_ENV_DECAY;
			pState->currentGainLeft = (EAS_I16) FMUL_15x15(pState->gainLeft, pState->volume);
			pState->currentGainRight = (EAS_I16) FMUL_15x15(pState->gainRight, pState->volume);
		}
		//else attack has a ramp
		else
		{
			//start the envelope very low
			pState->envValue = (2<<7);
			pState->currentGainLeft = 0;
			pState->currentGainRight = 0;
			//get envelope attack scaling value
			pState->envScale = getAttackIncrement(utemp);
			//go to attack state
			pState->envState = PCM_ENV_ATTACK;
		}
	}
	if (pState->envState == PCM_ENV_ATTACK)
	{
		//printf("env attack, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale);
		//update envelope value
		pState->envValue = pState->envValue + (pState->envScale << 7);
		//check envelope level and update state if needed
		if (pState->envValue >= (32768<<7))
		{
			pState->envValue = (32768<<7);
			utemp = ((pState->envData >> 16) & 0x0F);
			pState->envScale = getDecayScale(utemp);
			pState->envState = PCM_ENV_DECAY;
		}
	}
	else if (pState->envState == PCM_ENV_DECAY)
	{
		//printf("env decay, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale);
		//update envelope value
		pState->envValue = (pState->envValue * pState->envScale)>>9;
		//check envelope level against sustain level and update state if needed
		utemp = ((pState->envData >> 8) & 0x0F);
		if (utemp == (EAS_U32)0x0F)
			utemp = (2<<7);
		else
		{
			utemp = ((32769<<7) >> (utemp>>1));
		}
		if (pState->envValue <= utemp)
		{
			utemp = ((pState->envData >> 28) & 0x0F);
			pState->envScale = getDecayScale(utemp); //getSustainScale(utemp);
			pState->envState = PCM_ENV_SUSTAIN;
		}
	}
	else if (pState->envState == PCM_ENV_SUSTAIN)
	{
		//printf("env sustain, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale);
		//update envelope value
		pState->envValue = (pState->envValue * pState->envScale)>>9;
		//check envelope level against bottom level and update state if needed
		if (pState->envValue <= (2<<7))
		{
			//no more decay
			pState->envScale = 512;
			pState->envState = PCM_ENV_END;
		}
	}
	else if (pState->envState == PCM_ENV_RELEASE)
	{
		//printf("env release, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale);
		//update envelope value
		pState->envValue = (pState->envValue * pState->envScale)>>9;
		//check envelope level against bottom level and update state if needed
		if (pState->envValue <= (2<<7))
		{
			//no more decay
			pState->envScale = 512;
			pState->envState = PCM_ENV_END;
		}
	}
	else if (pState->envState == PCM_ENV_END)
	{
		//printf("env end\n");
		/* set state to stopping, already ramped down */
		pState->state = EAS_STATE_STOPPING;
	}

	//pState->gainLeft = (EAS_U16)((pState->gainLeft * (pState->envValue>>7))>>15);
	//pState->gainRight = (EAS_U16)((pState->gainRight * (pState->envValue>>7))>>15);

	/* gain to 32-bits to increase resolution on anti-zipper filter */
	/*lint -e{703} use shift for performance */
	gainLeft = (EAS_I32) pState->currentGainLeft << SYNTH_UPDATE_PERIOD_IN_BITS;
#if (NUM_OUTPUT_CHANNELS == 2)
	/*lint -e{703} use shift for performance */
	gainRight = (EAS_I32) pState->currentGainRight << SYNTH_UPDATE_PERIOD_IN_BITS;
#endif

	/* calculate a new gain increment, gain target is zero if pausing */
	if ((pState->state == EAS_STATE_PAUSING) || (pState->state == EAS_STATE_PAUSED))
	{
		gainIncLeft = -pState->currentGainLeft;
#if (NUM_OUTPUT_CHANNELS == 2)
		gainIncRight= -pState->currentGainRight;
#endif
	}
	else
	{
		EAS_I32 gain = FMUL_15x15(pState->envValue >> 7, pState->volume);
		gainIncLeft = FMUL_15x15(pState->gainLeft, gain) - pState->currentGainLeft;
#if (NUM_OUTPUT_CHANNELS == 2)
		gainIncRight = FMUL_15x15(pState->gainRight, gain) - pState->currentGainRight;
#endif
	}

	/* calculate phase increment */
	phaseInc = pState->basefreq;

	/* convert pitch cents to linear multiplier */
	if (pState->pitch)
	{
		temp = EAS_Calculate2toX(pState->pitch);
		phaseInc = FMUL_15x15(phaseInc, temp);
	}
	phaseInc = phaseInc << pState->rateShift;

	/* pointer to mix buffer */
	pOut = pEASData->pMixBuffer;
	
	/* render a buffer of samples */
	while (numSamples--)
	{

		/* interpolate an output sample */
		pState->decoderL.output = pState->decoderL.x0 + FMUL_15x15((pState->decoderL.x1 - pState->decoderL.x0), pState->phase & PHASE_FRAC_MASK);

		/* stereo output */
#if (NUM_OUTPUT_CHANNELS == 2)

		/* stereo stream? */
		if (pState->flags & PCM_FLAGS_STEREO)
			pState->decoderR.output = pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK);

		/* gain scale and mix */
		/*lint -e{704} use shift instead of division */
		*pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS;
		gainLeft += gainIncLeft;
		
		/*lint -e{704} use shift instead of division */
		if (pState->flags & PCM_FLAGS_STEREO)
			*pOut++ += (pState->decoderR.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS;
		else
			*pOut++ += (pState->decoderL.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS;
		
		gainRight += gainIncRight;

		/* mono output */
#else
		/* if stereo stream, decode right channel and mix to mono */
		if (pState->flags & PCM_FLAGS_STEREO)
		{
			pState->decoderR.output= pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK);

			/* for mono, sum stereo ADPCM to mono */
			/*lint -e{704} use shift instead of division */
			*pOut++ += ((pState->decoderL.output + pState->decoderR.output) * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS;
		}
		else
			/*lint -e{704} use shift instead of division */
			*pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS;
		
		gainLeft += gainIncLeft;
#endif		

		/* advance phase accumulator */
		pState->phase += phaseInc;

		/* if integer part of phase accumulator is non-zero, advance to next sample */
		while (pState->phase & ~PHASE_FRAC_MASK)
		{
			pState->decoderL.x0 = pState->decoderL.x1;
			pState->decoderR.x0 = pState->decoderR.x1;

			/* give the source a chance to continue the stream */
			if (!pState->bytesLeft && pState->pCallback && ((pState->flags & PCM_FLAGS_EMPTY) == 0))
			{
				pState->flags |= PCM_FLAGS_EMPTY;
				(*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY);
				{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "RenderPCMStream: After empty callback, bytesLeft = %d\n", pState->bytesLeft); */ }
			}
			
			/* decode the next sample */
			if ((result = (*pState->pDecoder->pfDecodeSample)(pEASData, pState)) != EAS_SUCCESS)
				return result;

			/* adjust phase by one sample */
			pState->phase -= (1L << NUM_PHASE_FRAC_BITS);
		}
			
	}

	/* save new gain */
	/*lint -e{704} use shift instead of division */
	pState->currentGainLeft = (EAS_I16) (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS);
	
#if (NUM_OUTPUT_CHANNELS == 2)
	/*lint -e{704} use shift instead of division */
	pState->currentGainRight = (EAS_I16) (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS);
#endif

	/* if pausing, set new state and notify */
	if (pState->state == EAS_STATE_PAUSING)
	{
		pState->state = EAS_STATE_PAUSED;
		if (pState->pCallback)
			(*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state);
	}

	/* if out of data, set stopped state and notify */
	if (pState->bytesLeft == 0 || pState->state == EAS_STATE_STOPPING)
	{
		pState->state = EAS_STATE_STOPPED;

		/* do callback unless the file has already been closed */
		if (pState->pCallback && pState->fileHandle)
			(*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state);
	}

	if (pState->state == EAS_STATE_READY)
		pState->state = EAS_STATE_PLAY;

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * LinearPCMDecode()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Decodes a PCM sample
 *
 * Inputs:
 * 
 *		
 * Outputs:
 * 
 *
 * Side Effects:
 *
 *----------------------------------------------------------------------------
*/
static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState)
{
	EAS_RESULT result;
	EAS_HW_DATA_HANDLE hwInstData;

	hwInstData = ((S_EAS_DATA*) pEASData)->hwInstData;

	/* if out of data, check for loop */
	if ((pState->bytesLeft == 0) && (pState->loopSamples != 0))
	{
		if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, (EAS_I32) (pState->startPos + pState->loopLocation))) != EAS_SUCCESS)
			return result;
		pState->bytesLeft = pState->byteCount = (EAS_I32) pState->bytesLeftLoop;
		pState->flags &= ~PCM_FLAGS_EMPTY;
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "LinearPCMDecode: Rewind file to %d, bytesLeft = %d\n", pState->startPos, pState->bytesLeft); */ }
	}

	if (pState->bytesLeft)
	{

		/* check format byte for 8-bit samples */
		if (pState->flags & PCM_FLAGS_8_BIT)
		{
			/* fetch left or mono sample */
			if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS)
				return result;

			/* if unsigned */
			if (pState->flags & PCM_FLAGS_UNSIGNED)
			{
				/*lint -e{734} converting unsigned 8-bit to signed 16-bit */
				pState->decoderL.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000);
			}
			else
			{
				/*lint -e{734} converting signed 8-bit to signed 16-bit */
				pState->decoderL.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8);
			}
			pState->bytesLeft--;
			
			/* fetch right sample */
			if(pState->flags & PCM_FLAGS_STEREO)
			{
				if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS)
					return result;

				/* if unsigned */
				if (pState->flags & PCM_FLAGS_UNSIGNED)
				{
					/*lint -e{734} converting unsigned 8-bit to signed 16-bit */
					pState->decoderR.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000);
				}
				else
				{
					/*lint -e{734} converting signed 8-bit to signed 16-bit */
					pState->decoderR.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8);
				}
				pState->bytesLeft--;
			}
		}

		/* must be 16-bit samples */
		else
		{
			//unsigned 16 bit currently not supported
			if (pState->flags & PCM_FLAGS_UNSIGNED)
			{
				return EAS_ERROR_INVALID_PCM_TYPE;
			}

			/* fetch left or mono sample */
			if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderL.x1, EAS_FALSE)) != EAS_SUCCESS)
				return result;
			pState->bytesLeft -= 2;
			
			/* fetch right sample */
			if(pState->flags & PCM_FLAGS_STEREO)
			{
				if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderR.x1, EAS_FALSE)) != EAS_SUCCESS)
					return result;
				pState->bytesLeft -= 2;
			}
		}
	}

	/* no more data, force zero samples */
	else
		pState->decoderL.x1 = pState->decoderR.x1 = 0;

	return EAS_SUCCESS;
}

/*----------------------------------------------------------------------------
 * LinearPCMLocate()
 *----------------------------------------------------------------------------
 * Purpose: 
 * Locate in a linear PCM stream
 *----------------------------------------------------------------------------
*/
static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time)
{
	EAS_RESULT result;
	EAS_I32 temp;
	EAS_I32 secs, msecs;
	EAS_INT shift;

	/* calculate size of sample frame */
	if (pState->flags & PCM_FLAGS_8_BIT)
		shift = 0;
	else
		shift = 1;
	if (pState->flags & PCM_FLAGS_STEREO)
		shift++;

	/* break down into secs and msecs */
	secs = time / 1000;
	msecs = time - (secs * 1000);

	/* calculate sample number fraction from msecs */
	temp = (msecs * pState->sampleRate);
	temp = (temp >> 10) + ((temp * 49) >> 21);

	/* add integer sample count */
	temp += secs * pState->sampleRate;

	/* calculate the position based on sample frame size */
	/*lint -e{703} use shift for performance */
	temp <<= shift;

	/* past end of sample? */
	if (temp > (EAS_I32) pState->loopStart)
	{
		/* if not looped, flag error */
		if (pState->loopSamples == 0)
		{
			pState->bytesLeft = 0;
			pState->flags |= PCM_FLAGS_EMPTY;
			return EAS_ERROR_LOCATE_BEYOND_END;
		}

		/* looped sample - calculate position in loop */
		while (temp > (EAS_I32) pState->loopStart)
			temp -= (EAS_I32) pState->loopStart;
	}

	/* seek to new position */
	if ((result = EAS_PESeek(pEASData, pState, &temp)) != EAS_SUCCESS)
		return result;

	/* reset state */
	if ((pState->state != EAS_STATE_PAUSING) && (pState->state != EAS_STATE_PAUSED))
		pState->state = EAS_STATE_READY;

	return EAS_SUCCESS;	
}

/*----------------------------------------------------------------------------
 * EAS_PESeek
 *----------------------------------------------------------------------------
 * Purpose: 
 * Locate to a particular byte in a PCM stream
 *----------------------------------------------------------------------------
 * This bit is tricky because the chunks may not be contiguous,
 * so we have to rely on the parser to position in the file. We
 * do this by seeking to the end of each chunk and simulating an
 * empty buffer condition until we get to where we want to go.
 *
 * A better solution would be a parser API for re-positioning,
 * but there isn't time at the moment to re-factor all the
 * parsers to support a new API.
 *----------------------------------------------------------------------------
*/
EAS_RESULT EAS_PESeek (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 *pLocation)
{
	EAS_RESULT result;

	/* seek to start of audio */
	if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS)
	{
		pState->state = EAS_STATE_ERROR;
		return result;
	}
	pState->bytesLeft = pState->bytesLeftLoop;

	/* skip through chunks until we find the right chunk */	
	while (*pLocation > (EAS_I32) pState->bytesLeft)
	{
		/* seek to end of audio chunk */
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", pState->bytesLeft); */ }
		if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, pState->bytesLeft)) != EAS_SUCCESS)
		{
			pState->state = EAS_STATE_ERROR;
			return result;
		}
		*pLocation -= pState->bytesLeft;
		pState->bytesLeft = 0;
		pState->flags |= PCM_FLAGS_EMPTY;
		
		/* retrieve more data */
		if (pState->pCallback)
			(*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY);

		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: bytesLeft=%d, byte location = %d\n", pState->bytesLeft, *pLocation); */ }

		/* no more samples */
		if (pState->bytesLeft == 0)
			return EAS_ERROR_LOCATE_BEYOND_END;
	}

	/* seek to new offset in current chunk */
	if (*pLocation > 0)
	{
		{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", *pLocation); */ }
		if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, *pLocation)) != EAS_SUCCESS)
		{
			pState->state = EAS_STATE_ERROR;
			return result;
		}
		
		/* if not streamed, calculate number of bytes left */
		if (pState->flags & PCM_FLAGS_STREAMING)
			pState->bytesLeft = 0x7fffffff;
		else
			pState->bytesLeft -= *pLocation;
	}
	return EAS_SUCCESS;
}