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