summaryrefslogtreecommitdiffstats
path: root/arm-hybrid-22k/lib_src/eas_pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm-hybrid-22k/lib_src/eas_pcm.c')
-rw-r--r--arm-hybrid-22k/lib_src/eas_pcm.c2940
1 files changed, 1470 insertions, 1470 deletions
diff --git a/arm-hybrid-22k/lib_src/eas_pcm.c b/arm-hybrid-22k/lib_src/eas_pcm.c
index 64b8f71..ff3f6f9 100644
--- a/arm-hybrid-22k/lib_src/eas_pcm.c
+++ b/arm-hybrid-22k/lib_src/eas_pcm.c
@@ -1,12 +1,12 @@
-/*----------------------------------------------------------------------------
- *
- * 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
+/*----------------------------------------------------------------------------
+ *
+ * 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.
@@ -19,1464 +19,1464 @@
* 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;
-}
-
+ *
+ *----------------------------------------------------------------------------
+ * 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;
+}
+