| /*---------------------------------------------------------------------------- |
| * |
| * 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; |
| } |
| |