summaryrefslogtreecommitdiffstats
path: root/arm-wt-22k/lib_src/eas_pcm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arm-wt-22k/lib_src/eas_pcm.c')
-rw-r--r--arm-wt-22k/lib_src/eas_pcm.c1482
1 files changed, 1482 insertions, 0 deletions
diff --git a/arm-wt-22k/lib_src/eas_pcm.c b/arm-wt-22k/lib_src/eas_pcm.c
new file mode 100644
index 0000000..64b8f71
--- /dev/null
+++ b/arm-wt-22k/lib_src/eas_pcm.c
@@ -0,0 +1,1482 @@
+/*----------------------------------------------------------------------------
+ *
+ * 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;
+}
+