From a8c89077d78769bf4840fa91609edc51fe2fa02d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Queru Date: Thu, 12 Nov 2009 18:45:37 -0800 Subject: eclair snapshot --- arm-fm-22k/lib_src/eas_pcm.c | 2940 +++++++++++++++++++++--------------------- 1 file changed, 1470 insertions(+), 1470 deletions(-) (limited to 'arm-fm-22k/lib_src/eas_pcm.c') diff --git a/arm-fm-22k/lib_src/eas_pcm.c b/arm-fm-22k/lib_src/eas_pcm.c index 64b8f71..ff3f6f9 100644 --- a/arm-fm-22k/lib_src/eas_pcm.c +++ b/arm-fm-22k/lib_src/eas_pcm.c @@ -1,12 +1,12 @@ -/*---------------------------------------------------------------------------- - * - * File: - * eas_pcm.c - * - * Contents and purpose: - * Implements the PCM engine including ADPCM decode for SMAF and CMX audio playback. - * - * Copyright Sonic Network Inc. 2005 +/*---------------------------------------------------------------------------- + * + * File: + * eas_pcm.c + * + * Contents and purpose: + * Implements the PCM engine including ADPCM decode for SMAF and CMX audio playback. + * + * Copyright Sonic Network Inc. 2005 * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,1464 +19,1464 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. - * - *---------------------------------------------------------------------------- - * Revision Control: - * $Revision: 849 $ - * $Date: 2007-08-28 08:59:11 -0700 (Tue, 28 Aug 2007) $ - *---------------------------------------------------------------------------- -*/ - -#include "eas_data.h" -#include "eas_report.h" -#include "eas_host.h" -#include "eas_config.h" -#include "eas_parser.h" -#include "eas_pcm.h" -#include "eas_math.h" -#include "eas_mixer.h" - -#define PCM_MIXER_GUARD_BITS (NUM_MIXER_GUARD_BITS + 1) - -/*---------------------------------------------------------------------------- - * Decoder interfaces - *---------------------------------------------------------------------------- -*/ - -static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState); -static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time); - -static const S_DECODER_INTERFACE PCMDecoder = -{ - NULL, - LinearPCMDecode, - LinearPCMLocate, -}; - -/* SMAF ADPCM decoder */ -#ifdef _SMAF_PARSER -extern S_DECODER_INTERFACE SmafDecoder; -#define SMAF_DECODER &SmafDecoder -extern S_DECODER_INTERFACE Smaf7BitDecoder; -#define SMAF_7BIT_DECODER &Smaf7BitDecoder -#else -#define SMAF_DECODER NULL -#define SMAF_7BIT_DECODER NULL -#endif - -/* IMA ADPCM decoder */ -#ifdef _IMA_DECODER -extern S_DECODER_INTERFACE IMADecoder; -#define IMA_DECODER &IMADecoder -#else -#define IMA_DECODER NULL -#endif - -static const S_DECODER_INTERFACE * const decoders[] = -{ - &PCMDecoder, - SMAF_DECODER, - IMA_DECODER, - SMAF_7BIT_DECODER -}; - -/*---------------------------------------------------------------------------- - * Sample rate conversion - *---------------------------------------------------------------------------- -*/ - -#define SRC_RATE_MULTIPLER (0x40000000 / _OUTPUT_SAMPLE_RATE) - -#ifdef _LOOKUP_SAMPLE_RATE -static const EAS_U32 srcConvRate[][2] = -{ - 4000L, (4000L << 15) / _OUTPUT_SAMPLE_RATE, - 8000L, (8000L << 15) / _OUTPUT_SAMPLE_RATE, - 11025L, (11025L << 15) / _OUTPUT_SAMPLE_RATE, - 12000L, (12000L << 15) / _OUTPUT_SAMPLE_RATE, - 16000L, (16000L << 15) / _OUTPUT_SAMPLE_RATE, - 22050L, (22050L << 15) / _OUTPUT_SAMPLE_RATE, - 24000L, (24000L << 15) / _OUTPUT_SAMPLE_RATE, - 32000L, (32000L << 15) / _OUTPUT_SAMPLE_RATE -}; -static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate); -#define SRC_CONV_RATE_ENTRIES (sizeof(srcConvRate)/sizeof(EAS_U32)/2) -#endif - - -/* interface prototypes */ -static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples); - - -/* local prototypes */ -static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData); -static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState); - -/*---------------------------------------------------------------------------- - * EAS_PEInit() - *---------------------------------------------------------------------------- - * Purpose: - * Initializes the PCM engine - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PEInit (S_EAS_DATA *pEASData) -{ - S_PCM_STATE *pState; - EAS_INT i; - - /* check for static memory allocation */ - if (pEASData->staticMemoryModel) - pEASData->pPCMStreams = EAS_CMEnumData(EAS_CM_PCM_DATA); - /* allocate dynamic memory */ - else - pEASData->pPCMStreams = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); - - if (!pEASData->pPCMStreams) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate memory for PCM streams\n"); */ } - return EAS_ERROR_MALLOC_FAILED; - } - - //zero the memory to insure complete initialization - EAS_HWMemSet((void *)(pEASData->pPCMStreams),0, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); - - /* initialize the state data */ - for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) - pState->fileHandle = NULL; - - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEShutdown() - *---------------------------------------------------------------------------- - * Purpose: - * Shuts down the PCM engine - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PEShutdown (S_EAS_DATA *pEASData) -{ - - /* free any dynamic memory */ - if (!pEASData->staticMemoryModel) - { - if (pEASData->pPCMStreams) - { - EAS_HWFree(pEASData->hwInstData, pEASData->pPCMStreams); - pEASData->pPCMStreams = NULL; - } - } - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PERender() - *---------------------------------------------------------------------------- - * Purpose: - * Render a buffer of PCM audio - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PERender (S_EAS_DATA* pEASData, EAS_I32 numSamples) -{ - S_PCM_STATE *pState; - EAS_RESULT result; - EAS_INT i; - - /* render all the active streams */ - for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) - { - if ((pState->fileHandle) && (pState->state != EAS_STATE_STOPPED) && (pState->state != EAS_STATE_PAUSED)) - if ((result = RenderPCMStream(pEASData, pState, numSamples)) != EAS_SUCCESS) - return result; - } - return EAS_SUCCESS; -} - - -/*---------------------------------------------------------------------------- - * EAS_PEState() - *---------------------------------------------------------------------------- - * Purpose: - * Returns the current state of the stream - * - * Inputs: - * pEASData - pointer to overall EAS data structure - * handle - pointer to file handle - * pState - pointer to variable to store state - * - * Outputs: - * - * - * Side Effects: - * - * Notes: - * This interface is also exposed in the internal library for use by the other modules. - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEState (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pInstData, EAS_STATE *pState) -{ - /* return current state */ - *pState = pInstData->state; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEClose() - *---------------------------------------------------------------------------- - * Purpose: - * Close the file and clean up - * - * Inputs: - * pEASData - pointer to overall EAS data structure - * handle - pointer to file handle - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PEClose (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) -{ - EAS_RESULT result; - - if ((result = EAS_HWCloseFile(pEASData->hwInstData, pState->fileHandle)) != EAS_SUCCESS) - return result; - - pState->fileHandle = NULL; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * PCM_Reset() - *---------------------------------------------------------------------------- - * Purpose: - * Reset the sequencer. Used for locating backwards in the file. - * - * Inputs: - * pEASData - pointer to overall EAS data structure - * handle - pointer to file handle - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PEReset (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) -{ - EAS_RESULT result; - - /* reset file position to first byte of data in the stream */ - if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Error %d seeking to start of PCM file\n", result); */ } - return result; - } - - /* re-initialize stream */ - return InitPCMStream(pEASData, pState); -} - -/*---------------------------------------------------------------------------- - * EAS_PEOpenStream() - *---------------------------------------------------------------------------- - * Purpose: - * Starts up a PCM playback - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PEOpenStream (S_EAS_DATA *pEASData, S_PCM_OPEN_PARAMS *pParams, EAS_PCM_HANDLE *pHandle) -{ - EAS_RESULT result; - S_PCM_STATE *pState; - EAS_I32 filePos; - - /* make sure we support this decoder */ - if (pParams->decoder >= NUM_DECODER_MODULES) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder selector out of range\n"); */ } - return EAS_ERROR_PARAMETER_RANGE; - } - if (decoders[pParams->decoder] == NULL) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder module not available\n"); */ } - return EAS_ERROR_FEATURE_NOT_AVAILABLE; - } - - /* find a slot for the new stream */ - if ((pState = FindSlot(pEASData, pParams->fileHandle, pParams->pCallbackFunc, pParams->cbInstData)) == NULL) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unable to open ADPCM stream, too many streams open\n"); */ } - return EAS_ERROR_MAX_PCM_STREAMS; - } - - /* get the current file position */ - if ((result = EAS_HWFilePos(pEASData->hwInstData, pState->fileHandle, &filePos)) != EAS_SUCCESS) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "EAS_HWFilePos returned %ld\n",result); */ } - pState->fileHandle = NULL; - return result; - } - - pState->pDecoder = decoders[pParams->decoder]; - pState->startPos = filePos; - pState->bytesLeftLoop = pState->byteCount = pParams->size; - pState->loopStart = pParams->loopStart; - pState->samplesTilLoop = (EAS_I32) pState->loopStart; - pState->loopSamples = pParams->loopSamples; - pState->samplesInLoop = 0; - pState->blockSize = (EAS_U16) pParams->blockSize; - pState->flags = pParams->flags; - pState->envData = pParams->envData; - pState->volume = pParams->volume; - pState->sampleRate = (EAS_U16) pParams->sampleRate; - - /* set the base frequency */ - pState->basefreq = (SRC_RATE_MULTIPLER * (EAS_U32) pParams->sampleRate) >> 15; - - /* calculate shift for frequencies > 1.0 */ - pState->rateShift = 0; - while (pState->basefreq > 32767) - { - pState->basefreq = pState->basefreq >> 1; - pState->rateShift++; - } - - /* initialize */ - if ((result = InitPCMStream(pEASData, pState)) != EAS_SUCCESS) - return result; - - *pHandle = pState; - - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PEOpenStream: StartPos=%d, byteCount = %d, loopSamples=%d\n", - pState->startPos, pState->byteCount, pState->loopSamples); */ } - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEContinueStream() - *---------------------------------------------------------------------------- - * Purpose: - * Continues a PCM stream - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -/*lint -e{715} reserved for future use */ -EAS_RESULT EAS_PEContinueStream (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 size) -{ - - /* add new samples to count */ - pState->bytesLeft += size; - if (pState->bytesLeft > 0) - pState->flags &= ~PCM_FLAGS_EMPTY; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEGetFileHandle() - *---------------------------------------------------------------------------- - * Purpose: - * Returns the file handle of a stream - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEGetFileHandle (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_FILE_HANDLE *pFileHandle) -{ - *pFileHandle = pState->fileHandle; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEUpdateParams() - *---------------------------------------------------------------------------- - * Purpose: - * Update the pitch and volume parameters for a PCM stream - * - * Inputs: - * pEASData - pointer to EAS library instance data - * handle - pointer to S_PCM_STATE for this stream - * gainLeft - linear gain multipler in 1.15 fraction format - * gainRight - linear gain multipler in 1.15 fraction format - * pitch - pitch shift in cents - * initial - initial settings, set current gain - * - * Outputs: - * - * - * Side Effects: - * - * Notes - * In mono mode, leftGain controls the output gain and rightGain is ignored - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -/*lint -esym(715, gainRight) used only in 2-channel version */ -EAS_RESULT EAS_PEUpdateParams (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch, EAS_I16 gainLeft, EAS_I16 gainRight) -{ - - pState->gainLeft = gainLeft; - -#if (NUM_OUTPUT_CHANNELS == 2) - pState->gainRight = gainRight; -#endif - - pState->pitch = pitch; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PELocate() - *---------------------------------------------------------------------------- - * Purpose: - * This function seeks to the requested place in the file. Accuracy - * is dependent on the sample rate and block size. - * - * Inputs: - * pEASData - pointer to overall EAS data structure - * pState - stream handle - * time - media time in milliseconds - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PELocate (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 time) -{ - if (pState->pDecoder->pfLocate == NULL) - return EAS_ERROR_FEATURE_NOT_AVAILABLE; - - return pState->pDecoder->pfLocate(pEASData, pState, time); -} - -/*---------------------------------------------------------------------------- - * EAS_PEUpdateVolume() - *---------------------------------------------------------------------------- - * Purpose: - * Update the volume parameters for a PCM stream - * - * Inputs: - * pEASData - pointer to EAS library instance data - * handle - pointer to S_PCM_STATE for this stream - * gainLeft - linear gain multipler in 1.15 fraction format - * gainRight - linear gain multipler in 1.15 fraction format - * initial - initial settings, set current gain - * - * Outputs: - * - * - * Side Effects: - * - * Notes - * In mono mode, leftGain controls the output gain and rightGain is ignored - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEUpdateVolume (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 volume) -{ - pState->volume = volume; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEUpdatePitch() - *---------------------------------------------------------------------------- - * Purpose: - * Update the pitch parameter for a PCM stream - * - * Inputs: - * pEASData - pointer to EAS library instance data - * pState - pointer to S_PCM_STATE for this stream - * pitch - new pitch value in pitch cents - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEUpdatePitch (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch) -{ - pState->pitch = pitch; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEPause() - *---------------------------------------------------------------------------- - * Purpose: - * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback - * at the end of the next audio frame. - * - * Inputs: - * pEASData - pointer to EAS library instance data - * handle - pointer to S_PCM_STATE for this stream - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEPause (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) -{ - /* set state to stopping */ - pState->state = EAS_STATE_PAUSING; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PEResume() - *---------------------------------------------------------------------------- - * Purpose: - * Resume rendering a PCM stream. Sets the gain target back to its - * previous setting and restarts playback at the end of the next audio - * frame. - * - * Inputs: - * pEASData - pointer to EAS library instance data - * handle - pointer to S_PCM_STATE for this stream - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PEResume (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) -{ - /* set state to stopping */ - pState->state = EAS_STATE_PLAY; - return EAS_SUCCESS; -} - -EAS_U32 getDecayScale(EAS_U32 index) -{ - EAS_U32 utemp; - - //envelope decay segment - switch (index) - { - case 0: //no decay - utemp = 512;//32768; - break; - case 1: //.0156 dB per update - utemp = 511;//32709; - break; - case 2: //.03125 - utemp = 510;//32649; - break; - case 3: //.0625 - utemp = 508;//32532; - break; - case 4: //.125 - utemp = 505;//32298; - break; - case 5: //.25 - utemp = 497;//31835; - break; - case 6: //.5 - utemp = 483;//30929; - break; - case 7: //1.0 - utemp = 456;//29193; - break; - case 8: //2.0 - utemp = 406;//26008; - break; - case 9: //4.0 - utemp = 323;//20642; - break; - case 10: //8.0 - utemp = 203;//13004; - break; - case 11: //16.0 - utemp = 81;//5160; - break; - case 12: //32.0 - utemp = 13;//813; - break; - case 13: //64.0 - utemp = 0;//20; - break; - case 14: //128.0 - utemp = 0; - break; - case 15: //256.0 - default: - utemp = 0; - break; - } - //printf("getdecayscale returned %d\n",utemp); - return utemp; -} - -EAS_U32 getAttackIncrement(EAS_U32 index) -{ - EAS_U32 utemp; - - //envelope decay segment - switch (index) - { - case 0: - utemp = 32; - break; - case 1: - utemp = 64; - break; - case 2: - utemp = 128; - break; - case 3: - utemp = 256; - break; - case 4: - utemp = 512; - break; - case 5: - utemp = 1024; - break; - case 6: - utemp = 2048; - break; - case 7: - utemp = 4096; - break; - case 8: - utemp = 8192; - break; - case 9: - utemp = 16384; - break; - case 10: - utemp = 32768; - break; - case 11: - utemp = 65536; - break; - case 12: - utemp = 65536; - break; - case 13: - utemp = 65536; - break; - case 14: - utemp = 65535; - break; - case 15: - default: - utemp = 0; - break; - } - //printf("getattackincrement returned %d\n",utemp); - return utemp; -} - -/*---------------------------------------------------------------------------- - * EAS_PERelease() - *---------------------------------------------------------------------------- - * Purpose: - * Put the PCM stream envelope into release. - * - * Inputs: - * pEASData - pointer to EAS library instance data - * handle - pointer to S_PCM_STATE for this stream - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -/*lint -esym(715, pEASData) reserved for future use */ -EAS_RESULT EAS_PERelease (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) -{ - EAS_U32 utemp; - - //printf("handling note-off part of envelope\n"); - /*if the note is not ignore release or sustained*/ - if (((pState->envData >> 24) & 0x0F)==0) - { - /* set envelope state to release */ - pState->envState = PCM_ENV_RELEASE; - utemp = ((pState->envData >> 20) & 0x0F); - pState->envScale = getDecayScale(utemp); //getReleaseScale(utemp); - } - else - { - /*else change envelope state to sustain */ - pState->envState = PCM_ENV_SUSTAIN; - utemp = ((pState->envData >> 28) & 0x0F); - pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); - } - //since we are in release, don't let anything hang around too long - //printf("checking env scale, val = %d\n",((S_PCM_STATE*) handle)->envScale); - if (pState->envScale > 505) - pState->envScale = 505; - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * FindSlot() - *---------------------------------------------------------------------------- - * Purpose: - * Locates an empty stream slot and assigns the file handle - * - * Inputs: - * pEASData - pointer to EAS library instance data - * fileHandle - file handle - * pCallbackFunc - function to be called back upon EAS_STATE_STOPPED - * - * Outputs: - * returns handle to slot or NULL if all slots are used - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData) -{ - EAS_INT i; - S_PCM_STATE *pState; - -#ifndef NO_PCM_STEAL - S_PCM_STATE *foundState = NULL; - EAS_INT count = 0; - EAS_U32 startOrder = 0xFFFFFFFF; - S_PCM_STATE *stealState = NULL; - EAS_U32 youngest = 0; - - /* find an empty slot, count total in use, and find oldest in use (lowest start order) */ - for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) - { - /* if this one is available */ - if (pState->fileHandle == NULL) - { - foundState = pState; - } - /* else this one is in use, so see if it is the oldest, and count total in use */ - /* also find youngest */ - else - { - /*one more voice in use*/ - count++; - /* is this the oldest? (lowest start order) */ - if ((pState->state != EAS_STATE_STOPPING) && (pState->startOrder < startOrder)) - { - /* remember this one */ - stealState = pState; - /* remember the oldest so far */ - startOrder = pState->startOrder; - } - /* is this the youngest? (highest start order) */ - if (pState->startOrder >= youngest) - { - youngest = pState->startOrder; - } - } - } - - /* if there are too many voices active, stop the oldest one */ - if (count > PCM_STREAM_THRESHOLD) - { - //printf("stealing!!!\n"); - /* make sure we got one, although we should always have one at this point */ - if (stealState != NULL) - { - //flag this as stopping, so it will get shut off - stealState->state = EAS_STATE_STOPPING; - } - } - - /* if there are no available open streams (we won't likely see this, due to stealing) */ - if (foundState == NULL) - return NULL; - - /* save info */ - foundState->startOrder = youngest + 1; - foundState->fileHandle = fileHandle; - foundState->pCallback = pCallbackFunc; - foundState->cbInstData = cbInstData; - return foundState; -#else - /* find an empty slot*/ - for (i = 0; i < MAX_PCM_STREAMS; i++) - { - pState = &pEASData->pPCMStreams[i]; - if (pState->fileHandle != NULL) - continue; - - pState->fileHandle = fileHandle; - pState->pCallback = pCallbackFunc; - pState->cbInstData = cbInstData; - return pState; - } - return NULL; -#endif -} - -#ifdef _LOOKUP_SAMPLE_RATE -/*---------------------------------------------------------------------------- - * CalcBaseFreq() - *---------------------------------------------------------------------------- - * Purpose: - * Calculates the fractional phase increment for the sample rate converter - * - * Inputs: - * sampleRate - sample rate in samples/sec - * - * Outputs: - * Returns fractional sample rate with a 15-bit fraction - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate) -{ - EAS_INT i; - - /* look up the conversion rate */ - for (i = 0; i < (EAS_INT)(SRC_CONV_RATE_ENTRIES); i ++) - { - if (srcConvRate[i][0] == sampleRate) - return srcConvRate[i][1]; - } - - /* if not found in table, do it the long way */ - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Sample rate %u not in table, calculating by division\n", sampleRate); */ } - - return (SRC_RATE_MULTIPLER * (EAS_U32) sampleRate) >> 15; -} -#endif - -/*---------------------------------------------------------------------------- - * InitPCMStream() - *---------------------------------------------------------------------------- - * Purpose: - * Start an ADPCM stream playback. Decodes the header, preps the engine. - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState) -{ - - /* initialize the data structure */ - pState->bytesLeft = pState->byteCount; - pState->phase = 0; - pState->srcByte = 0; - pState->decoderL.acc = 0; - pState->decoderL.output = 0; - pState->decoderL.x0 = pState->decoderL.x1 = 0; - pState->decoderL.step = 0; - pState->decoderR.acc = 0; - pState->decoderR.output = 0; - pState->decoderR.x0 = pState->decoderR.x1 = 0; - pState->decoderR.step = 0; - pState->hiNibble = EAS_FALSE; - pState->pitch = 0; - pState->blockCount = 0; - pState->gainLeft = PCM_DEFAULT_GAIN_SETTING; -// pState->currentGainLeft = PCM_DEFAULT_GAIN_SETTING; - pState->envValue = 0; - pState->envState = PCM_ENV_START; - -#if (NUM_OUTPUT_CHANNELS == 2) - pState->gainRight = PCM_DEFAULT_GAIN_SETTING; -// pState->currentGainRight = PCM_DEFAULT_GAIN_SETTING; -#endif - pState->state = EAS_STATE_READY; - - /* initialize the decoder */ - if (pState->pDecoder->pfInit) - return (*pState->pDecoder->pfInit)(pEASData, pState); - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * RenderPCMStream() - *---------------------------------------------------------------------------- - * Purpose: - * Decodes a buffer of ADPCM data. - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples) -{ - EAS_RESULT result; - EAS_U32 phaseInc; - EAS_I32 gainLeft, gainIncLeft; - EAS_I32 *pOut; - EAS_I32 temp; - EAS_U32 utemp; - -#if (NUM_OUTPUT_CHANNELS == 2) - EAS_I32 gainRight, gainIncRight; -#endif - -#if 0 - printf("env data: AR = %d, DR = %d, SL = %d, SR = %d, RR = %d\n", - ((pState->envData >> 12) & 0x0F), - ((pState->envData >> 16) & 0x0F), - ((pState->envData >> 8) & 0x0F), - ((pState->envData >> 28) & 0x0F), - ((pState->envData >> 20) & 0x0F)); -#endif - - if (pState->envState == PCM_ENV_START) - { - //printf("env start\n"); - utemp = ((pState->envData >> 12) & 0x0F); - //if fastest rate, attack is already completed - //do the same for slowest rate, since that allows zero to be passed for default envelope - if (utemp == 0x0F || utemp == 0x00) - { - //start envelope at full - pState->envValue = (32768<<7); - //jump right into decay - utemp = ((pState->envData >> 16) & 0x0F); - pState->envScale = getDecayScale(utemp); - pState->envState = PCM_ENV_DECAY; - pState->currentGainLeft = (EAS_I16) FMUL_15x15(pState->gainLeft, pState->volume); - pState->currentGainRight = (EAS_I16) FMUL_15x15(pState->gainRight, pState->volume); - } - //else attack has a ramp - else - { - //start the envelope very low - pState->envValue = (2<<7); - pState->currentGainLeft = 0; - pState->currentGainRight = 0; - //get envelope attack scaling value - pState->envScale = getAttackIncrement(utemp); - //go to attack state - pState->envState = PCM_ENV_ATTACK; - } - } - if (pState->envState == PCM_ENV_ATTACK) - { - //printf("env attack, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); - //update envelope value - pState->envValue = pState->envValue + (pState->envScale << 7); - //check envelope level and update state if needed - if (pState->envValue >= (32768<<7)) - { - pState->envValue = (32768<<7); - utemp = ((pState->envData >> 16) & 0x0F); - pState->envScale = getDecayScale(utemp); - pState->envState = PCM_ENV_DECAY; - } - } - else if (pState->envState == PCM_ENV_DECAY) - { - //printf("env decay, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); - //update envelope value - pState->envValue = (pState->envValue * pState->envScale)>>9; - //check envelope level against sustain level and update state if needed - utemp = ((pState->envData >> 8) & 0x0F); - if (utemp == (EAS_U32)0x0F) - utemp = (2<<7); - else - { - utemp = ((32769<<7) >> (utemp>>1)); - } - if (pState->envValue <= utemp) - { - utemp = ((pState->envData >> 28) & 0x0F); - pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); - pState->envState = PCM_ENV_SUSTAIN; - } - } - else if (pState->envState == PCM_ENV_SUSTAIN) - { - //printf("env sustain, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); - //update envelope value - pState->envValue = (pState->envValue * pState->envScale)>>9; - //check envelope level against bottom level and update state if needed - if (pState->envValue <= (2<<7)) - { - //no more decay - pState->envScale = 512; - pState->envState = PCM_ENV_END; - } - } - else if (pState->envState == PCM_ENV_RELEASE) - { - //printf("env release, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); - //update envelope value - pState->envValue = (pState->envValue * pState->envScale)>>9; - //check envelope level against bottom level and update state if needed - if (pState->envValue <= (2<<7)) - { - //no more decay - pState->envScale = 512; - pState->envState = PCM_ENV_END; - } - } - else if (pState->envState == PCM_ENV_END) - { - //printf("env end\n"); - /* set state to stopping, already ramped down */ - pState->state = EAS_STATE_STOPPING; - } - - //pState->gainLeft = (EAS_U16)((pState->gainLeft * (pState->envValue>>7))>>15); - //pState->gainRight = (EAS_U16)((pState->gainRight * (pState->envValue>>7))>>15); - - /* gain to 32-bits to increase resolution on anti-zipper filter */ - /*lint -e{703} use shift for performance */ - gainLeft = (EAS_I32) pState->currentGainLeft << SYNTH_UPDATE_PERIOD_IN_BITS; -#if (NUM_OUTPUT_CHANNELS == 2) - /*lint -e{703} use shift for performance */ - gainRight = (EAS_I32) pState->currentGainRight << SYNTH_UPDATE_PERIOD_IN_BITS; -#endif - - /* calculate a new gain increment, gain target is zero if pausing */ - if ((pState->state == EAS_STATE_PAUSING) || (pState->state == EAS_STATE_PAUSED)) - { - gainIncLeft = -pState->currentGainLeft; -#if (NUM_OUTPUT_CHANNELS == 2) - gainIncRight= -pState->currentGainRight; -#endif - } - else - { - EAS_I32 gain = FMUL_15x15(pState->envValue >> 7, pState->volume); - gainIncLeft = FMUL_15x15(pState->gainLeft, gain) - pState->currentGainLeft; -#if (NUM_OUTPUT_CHANNELS == 2) - gainIncRight = FMUL_15x15(pState->gainRight, gain) - pState->currentGainRight; -#endif - } - - /* calculate phase increment */ - phaseInc = pState->basefreq; - - /* convert pitch cents to linear multiplier */ - if (pState->pitch) - { - temp = EAS_Calculate2toX(pState->pitch); - phaseInc = FMUL_15x15(phaseInc, temp); - } - phaseInc = phaseInc << pState->rateShift; - - /* pointer to mix buffer */ - pOut = pEASData->pMixBuffer; - - /* render a buffer of samples */ - while (numSamples--) - { - - /* interpolate an output sample */ - pState->decoderL.output = pState->decoderL.x0 + FMUL_15x15((pState->decoderL.x1 - pState->decoderL.x0), pState->phase & PHASE_FRAC_MASK); - - /* stereo output */ -#if (NUM_OUTPUT_CHANNELS == 2) - - /* stereo stream? */ - if (pState->flags & PCM_FLAGS_STEREO) - pState->decoderR.output = pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); - - /* gain scale and mix */ - /*lint -e{704} use shift instead of division */ - *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; - gainLeft += gainIncLeft; - - /*lint -e{704} use shift instead of division */ - if (pState->flags & PCM_FLAGS_STEREO) - *pOut++ += (pState->decoderR.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; - else - *pOut++ += (pState->decoderL.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; - - gainRight += gainIncRight; - - /* mono output */ -#else - /* if stereo stream, decode right channel and mix to mono */ - if (pState->flags & PCM_FLAGS_STEREO) - { - pState->decoderR.output= pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); - - /* for mono, sum stereo ADPCM to mono */ - /*lint -e{704} use shift instead of division */ - *pOut++ += ((pState->decoderL.output + pState->decoderR.output) * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; - } - else - /*lint -e{704} use shift instead of division */ - *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; - - gainLeft += gainIncLeft; -#endif - - /* advance phase accumulator */ - pState->phase += phaseInc; - - /* if integer part of phase accumulator is non-zero, advance to next sample */ - while (pState->phase & ~PHASE_FRAC_MASK) - { - pState->decoderL.x0 = pState->decoderL.x1; - pState->decoderR.x0 = pState->decoderR.x1; - - /* give the source a chance to continue the stream */ - if (!pState->bytesLeft && pState->pCallback && ((pState->flags & PCM_FLAGS_EMPTY) == 0)) - { - pState->flags |= PCM_FLAGS_EMPTY; - (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "RenderPCMStream: After empty callback, bytesLeft = %d\n", pState->bytesLeft); */ } - } - - /* decode the next sample */ - if ((result = (*pState->pDecoder->pfDecodeSample)(pEASData, pState)) != EAS_SUCCESS) - return result; - - /* adjust phase by one sample */ - pState->phase -= (1L << NUM_PHASE_FRAC_BITS); - } - - } - - /* save new gain */ - /*lint -e{704} use shift instead of division */ - pState->currentGainLeft = (EAS_I16) (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS); - -#if (NUM_OUTPUT_CHANNELS == 2) - /*lint -e{704} use shift instead of division */ - pState->currentGainRight = (EAS_I16) (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS); -#endif - - /* if pausing, set new state and notify */ - if (pState->state == EAS_STATE_PAUSING) - { - pState->state = EAS_STATE_PAUSED; - if (pState->pCallback) - (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); - } - - /* if out of data, set stopped state and notify */ - if (pState->bytesLeft == 0 || pState->state == EAS_STATE_STOPPING) - { - pState->state = EAS_STATE_STOPPED; - - /* do callback unless the file has already been closed */ - if (pState->pCallback && pState->fileHandle) - (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); - } - - if (pState->state == EAS_STATE_READY) - pState->state = EAS_STATE_PLAY; - - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * LinearPCMDecode() - *---------------------------------------------------------------------------- - * Purpose: - * Decodes a PCM sample - * - * Inputs: - * - * - * Outputs: - * - * - * Side Effects: - * - *---------------------------------------------------------------------------- -*/ -static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState) -{ - EAS_RESULT result; - EAS_HW_DATA_HANDLE hwInstData; - - hwInstData = ((S_EAS_DATA*) pEASData)->hwInstData; - - /* if out of data, check for loop */ - if ((pState->bytesLeft == 0) && (pState->loopSamples != 0)) - { - if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, (EAS_I32) (pState->startPos + pState->loopLocation))) != EAS_SUCCESS) - return result; - pState->bytesLeft = pState->byteCount = (EAS_I32) pState->bytesLeftLoop; - pState->flags &= ~PCM_FLAGS_EMPTY; - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "LinearPCMDecode: Rewind file to %d, bytesLeft = %d\n", pState->startPos, pState->bytesLeft); */ } - } - - if (pState->bytesLeft) - { - - /* check format byte for 8-bit samples */ - if (pState->flags & PCM_FLAGS_8_BIT) - { - /* fetch left or mono sample */ - if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) - return result; - - /* if unsigned */ - if (pState->flags & PCM_FLAGS_UNSIGNED) - { - /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ - pState->decoderL.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); - } - else - { - /*lint -e{734} converting signed 8-bit to signed 16-bit */ - pState->decoderL.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); - } - pState->bytesLeft--; - - /* fetch right sample */ - if(pState->flags & PCM_FLAGS_STEREO) - { - if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) - return result; - - /* if unsigned */ - if (pState->flags & PCM_FLAGS_UNSIGNED) - { - /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ - pState->decoderR.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); - } - else - { - /*lint -e{734} converting signed 8-bit to signed 16-bit */ - pState->decoderR.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); - } - pState->bytesLeft--; - } - } - - /* must be 16-bit samples */ - else - { - //unsigned 16 bit currently not supported - if (pState->flags & PCM_FLAGS_UNSIGNED) - { - return EAS_ERROR_INVALID_PCM_TYPE; - } - - /* fetch left or mono sample */ - if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderL.x1, EAS_FALSE)) != EAS_SUCCESS) - return result; - pState->bytesLeft -= 2; - - /* fetch right sample */ - if(pState->flags & PCM_FLAGS_STEREO) - { - if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderR.x1, EAS_FALSE)) != EAS_SUCCESS) - return result; - pState->bytesLeft -= 2; - } - } - } - - /* no more data, force zero samples */ - else - pState->decoderL.x1 = pState->decoderR.x1 = 0; - - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * LinearPCMLocate() - *---------------------------------------------------------------------------- - * Purpose: - * Locate in a linear PCM stream - *---------------------------------------------------------------------------- -*/ -static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time) -{ - EAS_RESULT result; - EAS_I32 temp; - EAS_I32 secs, msecs; - EAS_INT shift; - - /* calculate size of sample frame */ - if (pState->flags & PCM_FLAGS_8_BIT) - shift = 0; - else - shift = 1; - if (pState->flags & PCM_FLAGS_STEREO) - shift++; - - /* break down into secs and msecs */ - secs = time / 1000; - msecs = time - (secs * 1000); - - /* calculate sample number fraction from msecs */ - temp = (msecs * pState->sampleRate); - temp = (temp >> 10) + ((temp * 49) >> 21); - - /* add integer sample count */ - temp += secs * pState->sampleRate; - - /* calculate the position based on sample frame size */ - /*lint -e{703} use shift for performance */ - temp <<= shift; - - /* past end of sample? */ - if (temp > (EAS_I32) pState->loopStart) - { - /* if not looped, flag error */ - if (pState->loopSamples == 0) - { - pState->bytesLeft = 0; - pState->flags |= PCM_FLAGS_EMPTY; - return EAS_ERROR_LOCATE_BEYOND_END; - } - - /* looped sample - calculate position in loop */ - while (temp > (EAS_I32) pState->loopStart) - temp -= (EAS_I32) pState->loopStart; - } - - /* seek to new position */ - if ((result = EAS_PESeek(pEASData, pState, &temp)) != EAS_SUCCESS) - return result; - - /* reset state */ - if ((pState->state != EAS_STATE_PAUSING) && (pState->state != EAS_STATE_PAUSED)) - pState->state = EAS_STATE_READY; - - return EAS_SUCCESS; -} - -/*---------------------------------------------------------------------------- - * EAS_PESeek - *---------------------------------------------------------------------------- - * Purpose: - * Locate to a particular byte in a PCM stream - *---------------------------------------------------------------------------- - * This bit is tricky because the chunks may not be contiguous, - * so we have to rely on the parser to position in the file. We - * do this by seeking to the end of each chunk and simulating an - * empty buffer condition until we get to where we want to go. - * - * A better solution would be a parser API for re-positioning, - * but there isn't time at the moment to re-factor all the - * parsers to support a new API. - *---------------------------------------------------------------------------- -*/ -EAS_RESULT EAS_PESeek (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 *pLocation) -{ - EAS_RESULT result; - - /* seek to start of audio */ - if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) - { - pState->state = EAS_STATE_ERROR; - return result; - } - pState->bytesLeft = pState->bytesLeftLoop; - - /* skip through chunks until we find the right chunk */ - while (*pLocation > (EAS_I32) pState->bytesLeft) - { - /* seek to end of audio chunk */ - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", pState->bytesLeft); */ } - if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, pState->bytesLeft)) != EAS_SUCCESS) - { - pState->state = EAS_STATE_ERROR; - return result; - } - *pLocation -= pState->bytesLeft; - pState->bytesLeft = 0; - pState->flags |= PCM_FLAGS_EMPTY; - - /* retrieve more data */ - if (pState->pCallback) - (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); - - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: bytesLeft=%d, byte location = %d\n", pState->bytesLeft, *pLocation); */ } - - /* no more samples */ - if (pState->bytesLeft == 0) - return EAS_ERROR_LOCATE_BEYOND_END; - } - - /* seek to new offset in current chunk */ - if (*pLocation > 0) - { - { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", *pLocation); */ } - if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, *pLocation)) != EAS_SUCCESS) - { - pState->state = EAS_STATE_ERROR; - return result; - } - - /* if not streamed, calculate number of bytes left */ - if (pState->flags & PCM_FLAGS_STREAMING) - pState->bytesLeft = 0x7fffffff; - else - pState->bytesLeft -= *pLocation; - } - return EAS_SUCCESS; -} - + * + *---------------------------------------------------------------------------- + * Revision Control: + * $Revision: 849 $ + * $Date: 2007-08-28 08:59:11 -0700 (Tue, 28 Aug 2007) $ + *---------------------------------------------------------------------------- +*/ + +#include "eas_data.h" +#include "eas_report.h" +#include "eas_host.h" +#include "eas_config.h" +#include "eas_parser.h" +#include "eas_pcm.h" +#include "eas_math.h" +#include "eas_mixer.h" + +#define PCM_MIXER_GUARD_BITS (NUM_MIXER_GUARD_BITS + 1) + +/*---------------------------------------------------------------------------- + * Decoder interfaces + *---------------------------------------------------------------------------- +*/ + +static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState); +static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time); + +static const S_DECODER_INTERFACE PCMDecoder = +{ + NULL, + LinearPCMDecode, + LinearPCMLocate, +}; + +/* SMAF ADPCM decoder */ +#ifdef _SMAF_PARSER +extern S_DECODER_INTERFACE SmafDecoder; +#define SMAF_DECODER &SmafDecoder +extern S_DECODER_INTERFACE Smaf7BitDecoder; +#define SMAF_7BIT_DECODER &Smaf7BitDecoder +#else +#define SMAF_DECODER NULL +#define SMAF_7BIT_DECODER NULL +#endif + +/* IMA ADPCM decoder */ +#ifdef _IMA_DECODER +extern S_DECODER_INTERFACE IMADecoder; +#define IMA_DECODER &IMADecoder +#else +#define IMA_DECODER NULL +#endif + +static const S_DECODER_INTERFACE * const decoders[] = +{ + &PCMDecoder, + SMAF_DECODER, + IMA_DECODER, + SMAF_7BIT_DECODER +}; + +/*---------------------------------------------------------------------------- + * Sample rate conversion + *---------------------------------------------------------------------------- +*/ + +#define SRC_RATE_MULTIPLER (0x40000000 / _OUTPUT_SAMPLE_RATE) + +#ifdef _LOOKUP_SAMPLE_RATE +static const EAS_U32 srcConvRate[][2] = +{ + 4000L, (4000L << 15) / _OUTPUT_SAMPLE_RATE, + 8000L, (8000L << 15) / _OUTPUT_SAMPLE_RATE, + 11025L, (11025L << 15) / _OUTPUT_SAMPLE_RATE, + 12000L, (12000L << 15) / _OUTPUT_SAMPLE_RATE, + 16000L, (16000L << 15) / _OUTPUT_SAMPLE_RATE, + 22050L, (22050L << 15) / _OUTPUT_SAMPLE_RATE, + 24000L, (24000L << 15) / _OUTPUT_SAMPLE_RATE, + 32000L, (32000L << 15) / _OUTPUT_SAMPLE_RATE +}; +static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate); +#define SRC_CONV_RATE_ENTRIES (sizeof(srcConvRate)/sizeof(EAS_U32)/2) +#endif + + +/* interface prototypes */ +static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples); + + +/* local prototypes */ +static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData); +static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState); + +/*---------------------------------------------------------------------------- + * EAS_PEInit() + *---------------------------------------------------------------------------- + * Purpose: + * Initializes the PCM engine + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PEInit (S_EAS_DATA *pEASData) +{ + S_PCM_STATE *pState; + EAS_INT i; + + /* check for static memory allocation */ + if (pEASData->staticMemoryModel) + pEASData->pPCMStreams = EAS_CMEnumData(EAS_CM_PCM_DATA); + /* allocate dynamic memory */ + else + pEASData->pPCMStreams = EAS_HWMalloc(pEASData->hwInstData, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); + + if (!pEASData->pPCMStreams) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_FATAL, "Failed to allocate memory for PCM streams\n"); */ } + return EAS_ERROR_MALLOC_FAILED; + } + + //zero the memory to insure complete initialization + EAS_HWMemSet((void *)(pEASData->pPCMStreams),0, sizeof(S_PCM_STATE) * MAX_PCM_STREAMS); + + /* initialize the state data */ + for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) + pState->fileHandle = NULL; + + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEShutdown() + *---------------------------------------------------------------------------- + * Purpose: + * Shuts down the PCM engine + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PEShutdown (S_EAS_DATA *pEASData) +{ + + /* free any dynamic memory */ + if (!pEASData->staticMemoryModel) + { + if (pEASData->pPCMStreams) + { + EAS_HWFree(pEASData->hwInstData, pEASData->pPCMStreams); + pEASData->pPCMStreams = NULL; + } + } + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PERender() + *---------------------------------------------------------------------------- + * Purpose: + * Render a buffer of PCM audio + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PERender (S_EAS_DATA* pEASData, EAS_I32 numSamples) +{ + S_PCM_STATE *pState; + EAS_RESULT result; + EAS_INT i; + + /* render all the active streams */ + for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) + { + if ((pState->fileHandle) && (pState->state != EAS_STATE_STOPPED) && (pState->state != EAS_STATE_PAUSED)) + if ((result = RenderPCMStream(pEASData, pState, numSamples)) != EAS_SUCCESS) + return result; + } + return EAS_SUCCESS; +} + + +/*---------------------------------------------------------------------------- + * EAS_PEState() + *---------------------------------------------------------------------------- + * Purpose: + * Returns the current state of the stream + * + * Inputs: + * pEASData - pointer to overall EAS data structure + * handle - pointer to file handle + * pState - pointer to variable to store state + * + * Outputs: + * + * + * Side Effects: + * + * Notes: + * This interface is also exposed in the internal library for use by the other modules. + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEState (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pInstData, EAS_STATE *pState) +{ + /* return current state */ + *pState = pInstData->state; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEClose() + *---------------------------------------------------------------------------- + * Purpose: + * Close the file and clean up + * + * Inputs: + * pEASData - pointer to overall EAS data structure + * handle - pointer to file handle + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PEClose (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) +{ + EAS_RESULT result; + + if ((result = EAS_HWCloseFile(pEASData->hwInstData, pState->fileHandle)) != EAS_SUCCESS) + return result; + + pState->fileHandle = NULL; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * PCM_Reset() + *---------------------------------------------------------------------------- + * Purpose: + * Reset the sequencer. Used for locating backwards in the file. + * + * Inputs: + * pEASData - pointer to overall EAS data structure + * handle - pointer to file handle + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PEReset (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) +{ + EAS_RESULT result; + + /* reset file position to first byte of data in the stream */ + if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Error %d seeking to start of PCM file\n", result); */ } + return result; + } + + /* re-initialize stream */ + return InitPCMStream(pEASData, pState); +} + +/*---------------------------------------------------------------------------- + * EAS_PEOpenStream() + *---------------------------------------------------------------------------- + * Purpose: + * Starts up a PCM playback + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PEOpenStream (S_EAS_DATA *pEASData, S_PCM_OPEN_PARAMS *pParams, EAS_PCM_HANDLE *pHandle) +{ + EAS_RESULT result; + S_PCM_STATE *pState; + EAS_I32 filePos; + + /* make sure we support this decoder */ + if (pParams->decoder >= NUM_DECODER_MODULES) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder selector out of range\n"); */ } + return EAS_ERROR_PARAMETER_RANGE; + } + if (decoders[pParams->decoder] == NULL) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Decoder module not available\n"); */ } + return EAS_ERROR_FEATURE_NOT_AVAILABLE; + } + + /* find a slot for the new stream */ + if ((pState = FindSlot(pEASData, pParams->fileHandle, pParams->pCallbackFunc, pParams->cbInstData)) == NULL) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "Unable to open ADPCM stream, too many streams open\n"); */ } + return EAS_ERROR_MAX_PCM_STREAMS; + } + + /* get the current file position */ + if ((result = EAS_HWFilePos(pEASData->hwInstData, pState->fileHandle, &filePos)) != EAS_SUCCESS) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_ERROR, "EAS_HWFilePos returned %ld\n",result); */ } + pState->fileHandle = NULL; + return result; + } + + pState->pDecoder = decoders[pParams->decoder]; + pState->startPos = filePos; + pState->bytesLeftLoop = pState->byteCount = pParams->size; + pState->loopStart = pParams->loopStart; + pState->samplesTilLoop = (EAS_I32) pState->loopStart; + pState->loopSamples = pParams->loopSamples; + pState->samplesInLoop = 0; + pState->blockSize = (EAS_U16) pParams->blockSize; + pState->flags = pParams->flags; + pState->envData = pParams->envData; + pState->volume = pParams->volume; + pState->sampleRate = (EAS_U16) pParams->sampleRate; + + /* set the base frequency */ + pState->basefreq = (SRC_RATE_MULTIPLER * (EAS_U32) pParams->sampleRate) >> 15; + + /* calculate shift for frequencies > 1.0 */ + pState->rateShift = 0; + while (pState->basefreq > 32767) + { + pState->basefreq = pState->basefreq >> 1; + pState->rateShift++; + } + + /* initialize */ + if ((result = InitPCMStream(pEASData, pState)) != EAS_SUCCESS) + return result; + + *pHandle = pState; + + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PEOpenStream: StartPos=%d, byteCount = %d, loopSamples=%d\n", + pState->startPos, pState->byteCount, pState->loopSamples); */ } + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEContinueStream() + *---------------------------------------------------------------------------- + * Purpose: + * Continues a PCM stream + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +/*lint -e{715} reserved for future use */ +EAS_RESULT EAS_PEContinueStream (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 size) +{ + + /* add new samples to count */ + pState->bytesLeft += size; + if (pState->bytesLeft > 0) + pState->flags &= ~PCM_FLAGS_EMPTY; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEGetFileHandle() + *---------------------------------------------------------------------------- + * Purpose: + * Returns the file handle of a stream + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEGetFileHandle (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_FILE_HANDLE *pFileHandle) +{ + *pFileHandle = pState->fileHandle; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEUpdateParams() + *---------------------------------------------------------------------------- + * Purpose: + * Update the pitch and volume parameters for a PCM stream + * + * Inputs: + * pEASData - pointer to EAS library instance data + * handle - pointer to S_PCM_STATE for this stream + * gainLeft - linear gain multipler in 1.15 fraction format + * gainRight - linear gain multipler in 1.15 fraction format + * pitch - pitch shift in cents + * initial - initial settings, set current gain + * + * Outputs: + * + * + * Side Effects: + * + * Notes + * In mono mode, leftGain controls the output gain and rightGain is ignored + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +/*lint -esym(715, gainRight) used only in 2-channel version */ +EAS_RESULT EAS_PEUpdateParams (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch, EAS_I16 gainLeft, EAS_I16 gainRight) +{ + + pState->gainLeft = gainLeft; + +#if (NUM_OUTPUT_CHANNELS == 2) + pState->gainRight = gainRight; +#endif + + pState->pitch = pitch; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PELocate() + *---------------------------------------------------------------------------- + * Purpose: + * This function seeks to the requested place in the file. Accuracy + * is dependent on the sample rate and block size. + * + * Inputs: + * pEASData - pointer to overall EAS data structure + * pState - stream handle + * time - media time in milliseconds + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PELocate (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState, EAS_I32 time) +{ + if (pState->pDecoder->pfLocate == NULL) + return EAS_ERROR_FEATURE_NOT_AVAILABLE; + + return pState->pDecoder->pfLocate(pEASData, pState, time); +} + +/*---------------------------------------------------------------------------- + * EAS_PEUpdateVolume() + *---------------------------------------------------------------------------- + * Purpose: + * Update the volume parameters for a PCM stream + * + * Inputs: + * pEASData - pointer to EAS library instance data + * handle - pointer to S_PCM_STATE for this stream + * gainLeft - linear gain multipler in 1.15 fraction format + * gainRight - linear gain multipler in 1.15 fraction format + * initial - initial settings, set current gain + * + * Outputs: + * + * + * Side Effects: + * + * Notes + * In mono mode, leftGain controls the output gain and rightGain is ignored + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEUpdateVolume (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 volume) +{ + pState->volume = volume; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEUpdatePitch() + *---------------------------------------------------------------------------- + * Purpose: + * Update the pitch parameter for a PCM stream + * + * Inputs: + * pEASData - pointer to EAS library instance data + * pState - pointer to S_PCM_STATE for this stream + * pitch - new pitch value in pitch cents + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEUpdatePitch (S_EAS_DATA* pEASData, EAS_PCM_HANDLE pState, EAS_I16 pitch) +{ + pState->pitch = pitch; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEPause() + *---------------------------------------------------------------------------- + * Purpose: + * Mute and stop rendering a PCM stream. Sets the gain target to zero and stops the playback + * at the end of the next audio frame. + * + * Inputs: + * pEASData - pointer to EAS library instance data + * handle - pointer to S_PCM_STATE for this stream + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEPause (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) +{ + /* set state to stopping */ + pState->state = EAS_STATE_PAUSING; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PEResume() + *---------------------------------------------------------------------------- + * Purpose: + * Resume rendering a PCM stream. Sets the gain target back to its + * previous setting and restarts playback at the end of the next audio + * frame. + * + * Inputs: + * pEASData - pointer to EAS library instance data + * handle - pointer to S_PCM_STATE for this stream + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PEResume (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) +{ + /* set state to stopping */ + pState->state = EAS_STATE_PLAY; + return EAS_SUCCESS; +} + +EAS_U32 getDecayScale(EAS_U32 index) +{ + EAS_U32 utemp; + + //envelope decay segment + switch (index) + { + case 0: //no decay + utemp = 512;//32768; + break; + case 1: //.0156 dB per update + utemp = 511;//32709; + break; + case 2: //.03125 + utemp = 510;//32649; + break; + case 3: //.0625 + utemp = 508;//32532; + break; + case 4: //.125 + utemp = 505;//32298; + break; + case 5: //.25 + utemp = 497;//31835; + break; + case 6: //.5 + utemp = 483;//30929; + break; + case 7: //1.0 + utemp = 456;//29193; + break; + case 8: //2.0 + utemp = 406;//26008; + break; + case 9: //4.0 + utemp = 323;//20642; + break; + case 10: //8.0 + utemp = 203;//13004; + break; + case 11: //16.0 + utemp = 81;//5160; + break; + case 12: //32.0 + utemp = 13;//813; + break; + case 13: //64.0 + utemp = 0;//20; + break; + case 14: //128.0 + utemp = 0; + break; + case 15: //256.0 + default: + utemp = 0; + break; + } + //printf("getdecayscale returned %d\n",utemp); + return utemp; +} + +EAS_U32 getAttackIncrement(EAS_U32 index) +{ + EAS_U32 utemp; + + //envelope decay segment + switch (index) + { + case 0: + utemp = 32; + break; + case 1: + utemp = 64; + break; + case 2: + utemp = 128; + break; + case 3: + utemp = 256; + break; + case 4: + utemp = 512; + break; + case 5: + utemp = 1024; + break; + case 6: + utemp = 2048; + break; + case 7: + utemp = 4096; + break; + case 8: + utemp = 8192; + break; + case 9: + utemp = 16384; + break; + case 10: + utemp = 32768; + break; + case 11: + utemp = 65536; + break; + case 12: + utemp = 65536; + break; + case 13: + utemp = 65536; + break; + case 14: + utemp = 65535; + break; + case 15: + default: + utemp = 0; + break; + } + //printf("getattackincrement returned %d\n",utemp); + return utemp; +} + +/*---------------------------------------------------------------------------- + * EAS_PERelease() + *---------------------------------------------------------------------------- + * Purpose: + * Put the PCM stream envelope into release. + * + * Inputs: + * pEASData - pointer to EAS library instance data + * handle - pointer to S_PCM_STATE for this stream + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +/*lint -esym(715, pEASData) reserved for future use */ +EAS_RESULT EAS_PERelease (S_EAS_DATA *pEASData, EAS_PCM_HANDLE pState) +{ + EAS_U32 utemp; + + //printf("handling note-off part of envelope\n"); + /*if the note is not ignore release or sustained*/ + if (((pState->envData >> 24) & 0x0F)==0) + { + /* set envelope state to release */ + pState->envState = PCM_ENV_RELEASE; + utemp = ((pState->envData >> 20) & 0x0F); + pState->envScale = getDecayScale(utemp); //getReleaseScale(utemp); + } + else + { + /*else change envelope state to sustain */ + pState->envState = PCM_ENV_SUSTAIN; + utemp = ((pState->envData >> 28) & 0x0F); + pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); + } + //since we are in release, don't let anything hang around too long + //printf("checking env scale, val = %d\n",((S_PCM_STATE*) handle)->envScale); + if (pState->envScale > 505) + pState->envScale = 505; + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * FindSlot() + *---------------------------------------------------------------------------- + * Purpose: + * Locates an empty stream slot and assigns the file handle + * + * Inputs: + * pEASData - pointer to EAS library instance data + * fileHandle - file handle + * pCallbackFunc - function to be called back upon EAS_STATE_STOPPED + * + * Outputs: + * returns handle to slot or NULL if all slots are used + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static S_PCM_STATE *FindSlot (S_EAS_DATA *pEASData, EAS_FILE_HANDLE fileHandle, EAS_PCM_CALLBACK pCallbackFunc, EAS_VOID_PTR cbInstData) +{ + EAS_INT i; + S_PCM_STATE *pState; + +#ifndef NO_PCM_STEAL + S_PCM_STATE *foundState = NULL; + EAS_INT count = 0; + EAS_U32 startOrder = 0xFFFFFFFF; + S_PCM_STATE *stealState = NULL; + EAS_U32 youngest = 0; + + /* find an empty slot, count total in use, and find oldest in use (lowest start order) */ + for (i = 0, pState = pEASData->pPCMStreams; i < MAX_PCM_STREAMS; i++, pState++) + { + /* if this one is available */ + if (pState->fileHandle == NULL) + { + foundState = pState; + } + /* else this one is in use, so see if it is the oldest, and count total in use */ + /* also find youngest */ + else + { + /*one more voice in use*/ + count++; + /* is this the oldest? (lowest start order) */ + if ((pState->state != EAS_STATE_STOPPING) && (pState->startOrder < startOrder)) + { + /* remember this one */ + stealState = pState; + /* remember the oldest so far */ + startOrder = pState->startOrder; + } + /* is this the youngest? (highest start order) */ + if (pState->startOrder >= youngest) + { + youngest = pState->startOrder; + } + } + } + + /* if there are too many voices active, stop the oldest one */ + if (count > PCM_STREAM_THRESHOLD) + { + //printf("stealing!!!\n"); + /* make sure we got one, although we should always have one at this point */ + if (stealState != NULL) + { + //flag this as stopping, so it will get shut off + stealState->state = EAS_STATE_STOPPING; + } + } + + /* if there are no available open streams (we won't likely see this, due to stealing) */ + if (foundState == NULL) + return NULL; + + /* save info */ + foundState->startOrder = youngest + 1; + foundState->fileHandle = fileHandle; + foundState->pCallback = pCallbackFunc; + foundState->cbInstData = cbInstData; + return foundState; +#else + /* find an empty slot*/ + for (i = 0; i < MAX_PCM_STREAMS; i++) + { + pState = &pEASData->pPCMStreams[i]; + if (pState->fileHandle != NULL) + continue; + + pState->fileHandle = fileHandle; + pState->pCallback = pCallbackFunc; + pState->cbInstData = cbInstData; + return pState; + } + return NULL; +#endif +} + +#ifdef _LOOKUP_SAMPLE_RATE +/*---------------------------------------------------------------------------- + * CalcBaseFreq() + *---------------------------------------------------------------------------- + * Purpose: + * Calculates the fractional phase increment for the sample rate converter + * + * Inputs: + * sampleRate - sample rate in samples/sec + * + * Outputs: + * Returns fractional sample rate with a 15-bit fraction + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static EAS_U32 CalcBaseFreq (EAS_U32 sampleRate) +{ + EAS_INT i; + + /* look up the conversion rate */ + for (i = 0; i < (EAS_INT)(SRC_CONV_RATE_ENTRIES); i ++) + { + if (srcConvRate[i][0] == sampleRate) + return srcConvRate[i][1]; + } + + /* if not found in table, do it the long way */ + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_WARNING, "Sample rate %u not in table, calculating by division\n", sampleRate); */ } + + return (SRC_RATE_MULTIPLER * (EAS_U32) sampleRate) >> 15; +} +#endif + +/*---------------------------------------------------------------------------- + * InitPCMStream() + *---------------------------------------------------------------------------- + * Purpose: + * Start an ADPCM stream playback. Decodes the header, preps the engine. + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static EAS_RESULT InitPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState) +{ + + /* initialize the data structure */ + pState->bytesLeft = pState->byteCount; + pState->phase = 0; + pState->srcByte = 0; + pState->decoderL.acc = 0; + pState->decoderL.output = 0; + pState->decoderL.x0 = pState->decoderL.x1 = 0; + pState->decoderL.step = 0; + pState->decoderR.acc = 0; + pState->decoderR.output = 0; + pState->decoderR.x0 = pState->decoderR.x1 = 0; + pState->decoderR.step = 0; + pState->hiNibble = EAS_FALSE; + pState->pitch = 0; + pState->blockCount = 0; + pState->gainLeft = PCM_DEFAULT_GAIN_SETTING; +// pState->currentGainLeft = PCM_DEFAULT_GAIN_SETTING; + pState->envValue = 0; + pState->envState = PCM_ENV_START; + +#if (NUM_OUTPUT_CHANNELS == 2) + pState->gainRight = PCM_DEFAULT_GAIN_SETTING; +// pState->currentGainRight = PCM_DEFAULT_GAIN_SETTING; +#endif + pState->state = EAS_STATE_READY; + + /* initialize the decoder */ + if (pState->pDecoder->pfInit) + return (*pState->pDecoder->pfInit)(pEASData, pState); + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * RenderPCMStream() + *---------------------------------------------------------------------------- + * Purpose: + * Decodes a buffer of ADPCM data. + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static EAS_RESULT RenderPCMStream (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 numSamples) +{ + EAS_RESULT result; + EAS_U32 phaseInc; + EAS_I32 gainLeft, gainIncLeft; + EAS_I32 *pOut; + EAS_I32 temp; + EAS_U32 utemp; + +#if (NUM_OUTPUT_CHANNELS == 2) + EAS_I32 gainRight, gainIncRight; +#endif + +#if 0 + printf("env data: AR = %d, DR = %d, SL = %d, SR = %d, RR = %d\n", + ((pState->envData >> 12) & 0x0F), + ((pState->envData >> 16) & 0x0F), + ((pState->envData >> 8) & 0x0F), + ((pState->envData >> 28) & 0x0F), + ((pState->envData >> 20) & 0x0F)); +#endif + + if (pState->envState == PCM_ENV_START) + { + //printf("env start\n"); + utemp = ((pState->envData >> 12) & 0x0F); + //if fastest rate, attack is already completed + //do the same for slowest rate, since that allows zero to be passed for default envelope + if (utemp == 0x0F || utemp == 0x00) + { + //start envelope at full + pState->envValue = (32768<<7); + //jump right into decay + utemp = ((pState->envData >> 16) & 0x0F); + pState->envScale = getDecayScale(utemp); + pState->envState = PCM_ENV_DECAY; + pState->currentGainLeft = (EAS_I16) FMUL_15x15(pState->gainLeft, pState->volume); + pState->currentGainRight = (EAS_I16) FMUL_15x15(pState->gainRight, pState->volume); + } + //else attack has a ramp + else + { + //start the envelope very low + pState->envValue = (2<<7); + pState->currentGainLeft = 0; + pState->currentGainRight = 0; + //get envelope attack scaling value + pState->envScale = getAttackIncrement(utemp); + //go to attack state + pState->envState = PCM_ENV_ATTACK; + } + } + if (pState->envState == PCM_ENV_ATTACK) + { + //printf("env attack, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); + //update envelope value + pState->envValue = pState->envValue + (pState->envScale << 7); + //check envelope level and update state if needed + if (pState->envValue >= (32768<<7)) + { + pState->envValue = (32768<<7); + utemp = ((pState->envData >> 16) & 0x0F); + pState->envScale = getDecayScale(utemp); + pState->envState = PCM_ENV_DECAY; + } + } + else if (pState->envState == PCM_ENV_DECAY) + { + //printf("env decay, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); + //update envelope value + pState->envValue = (pState->envValue * pState->envScale)>>9; + //check envelope level against sustain level and update state if needed + utemp = ((pState->envData >> 8) & 0x0F); + if (utemp == (EAS_U32)0x0F) + utemp = (2<<7); + else + { + utemp = ((32769<<7) >> (utemp>>1)); + } + if (pState->envValue <= utemp) + { + utemp = ((pState->envData >> 28) & 0x0F); + pState->envScale = getDecayScale(utemp); //getSustainScale(utemp); + pState->envState = PCM_ENV_SUSTAIN; + } + } + else if (pState->envState == PCM_ENV_SUSTAIN) + { + //printf("env sustain, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); + //update envelope value + pState->envValue = (pState->envValue * pState->envScale)>>9; + //check envelope level against bottom level and update state if needed + if (pState->envValue <= (2<<7)) + { + //no more decay + pState->envScale = 512; + pState->envState = PCM_ENV_END; + } + } + else if (pState->envState == PCM_ENV_RELEASE) + { + //printf("env release, env value = %d, env scale = %d\n",pState->envValue>>7,pState->envScale); + //update envelope value + pState->envValue = (pState->envValue * pState->envScale)>>9; + //check envelope level against bottom level and update state if needed + if (pState->envValue <= (2<<7)) + { + //no more decay + pState->envScale = 512; + pState->envState = PCM_ENV_END; + } + } + else if (pState->envState == PCM_ENV_END) + { + //printf("env end\n"); + /* set state to stopping, already ramped down */ + pState->state = EAS_STATE_STOPPING; + } + + //pState->gainLeft = (EAS_U16)((pState->gainLeft * (pState->envValue>>7))>>15); + //pState->gainRight = (EAS_U16)((pState->gainRight * (pState->envValue>>7))>>15); + + /* gain to 32-bits to increase resolution on anti-zipper filter */ + /*lint -e{703} use shift for performance */ + gainLeft = (EAS_I32) pState->currentGainLeft << SYNTH_UPDATE_PERIOD_IN_BITS; +#if (NUM_OUTPUT_CHANNELS == 2) + /*lint -e{703} use shift for performance */ + gainRight = (EAS_I32) pState->currentGainRight << SYNTH_UPDATE_PERIOD_IN_BITS; +#endif + + /* calculate a new gain increment, gain target is zero if pausing */ + if ((pState->state == EAS_STATE_PAUSING) || (pState->state == EAS_STATE_PAUSED)) + { + gainIncLeft = -pState->currentGainLeft; +#if (NUM_OUTPUT_CHANNELS == 2) + gainIncRight= -pState->currentGainRight; +#endif + } + else + { + EAS_I32 gain = FMUL_15x15(pState->envValue >> 7, pState->volume); + gainIncLeft = FMUL_15x15(pState->gainLeft, gain) - pState->currentGainLeft; +#if (NUM_OUTPUT_CHANNELS == 2) + gainIncRight = FMUL_15x15(pState->gainRight, gain) - pState->currentGainRight; +#endif + } + + /* calculate phase increment */ + phaseInc = pState->basefreq; + + /* convert pitch cents to linear multiplier */ + if (pState->pitch) + { + temp = EAS_Calculate2toX(pState->pitch); + phaseInc = FMUL_15x15(phaseInc, temp); + } + phaseInc = phaseInc << pState->rateShift; + + /* pointer to mix buffer */ + pOut = pEASData->pMixBuffer; + + /* render a buffer of samples */ + while (numSamples--) + { + + /* interpolate an output sample */ + pState->decoderL.output = pState->decoderL.x0 + FMUL_15x15((pState->decoderL.x1 - pState->decoderL.x0), pState->phase & PHASE_FRAC_MASK); + + /* stereo output */ +#if (NUM_OUTPUT_CHANNELS == 2) + + /* stereo stream? */ + if (pState->flags & PCM_FLAGS_STEREO) + pState->decoderR.output = pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); + + /* gain scale and mix */ + /*lint -e{704} use shift instead of division */ + *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; + gainLeft += gainIncLeft; + + /*lint -e{704} use shift instead of division */ + if (pState->flags & PCM_FLAGS_STEREO) + *pOut++ += (pState->decoderR.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; + else + *pOut++ += (pState->decoderL.output * (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; + + gainRight += gainIncRight; + + /* mono output */ +#else + /* if stereo stream, decode right channel and mix to mono */ + if (pState->flags & PCM_FLAGS_STEREO) + { + pState->decoderR.output= pState->decoderR.x0 + FMUL_15x15((pState->decoderR.x1 - pState->decoderR.x0), pState->phase & PHASE_FRAC_MASK); + + /* for mono, sum stereo ADPCM to mono */ + /*lint -e{704} use shift instead of division */ + *pOut++ += ((pState->decoderL.output + pState->decoderR.output) * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; + } + else + /*lint -e{704} use shift instead of division */ + *pOut++ += (pState->decoderL.output * (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS)) >> PCM_MIXER_GUARD_BITS; + + gainLeft += gainIncLeft; +#endif + + /* advance phase accumulator */ + pState->phase += phaseInc; + + /* if integer part of phase accumulator is non-zero, advance to next sample */ + while (pState->phase & ~PHASE_FRAC_MASK) + { + pState->decoderL.x0 = pState->decoderL.x1; + pState->decoderR.x0 = pState->decoderR.x1; + + /* give the source a chance to continue the stream */ + if (!pState->bytesLeft && pState->pCallback && ((pState->flags & PCM_FLAGS_EMPTY) == 0)) + { + pState->flags |= PCM_FLAGS_EMPTY; + (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "RenderPCMStream: After empty callback, bytesLeft = %d\n", pState->bytesLeft); */ } + } + + /* decode the next sample */ + if ((result = (*pState->pDecoder->pfDecodeSample)(pEASData, pState)) != EAS_SUCCESS) + return result; + + /* adjust phase by one sample */ + pState->phase -= (1L << NUM_PHASE_FRAC_BITS); + } + + } + + /* save new gain */ + /*lint -e{704} use shift instead of division */ + pState->currentGainLeft = (EAS_I16) (gainLeft >> SYNTH_UPDATE_PERIOD_IN_BITS); + +#if (NUM_OUTPUT_CHANNELS == 2) + /*lint -e{704} use shift instead of division */ + pState->currentGainRight = (EAS_I16) (gainRight >> SYNTH_UPDATE_PERIOD_IN_BITS); +#endif + + /* if pausing, set new state and notify */ + if (pState->state == EAS_STATE_PAUSING) + { + pState->state = EAS_STATE_PAUSED; + if (pState->pCallback) + (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); + } + + /* if out of data, set stopped state and notify */ + if (pState->bytesLeft == 0 || pState->state == EAS_STATE_STOPPING) + { + pState->state = EAS_STATE_STOPPED; + + /* do callback unless the file has already been closed */ + if (pState->pCallback && pState->fileHandle) + (*pState->pCallback)(pEASData, pState->cbInstData, pState, pState->state); + } + + if (pState->state == EAS_STATE_READY) + pState->state = EAS_STATE_PLAY; + + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * LinearPCMDecode() + *---------------------------------------------------------------------------- + * Purpose: + * Decodes a PCM sample + * + * Inputs: + * + * + * Outputs: + * + * + * Side Effects: + * + *---------------------------------------------------------------------------- +*/ +static EAS_RESULT LinearPCMDecode (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState) +{ + EAS_RESULT result; + EAS_HW_DATA_HANDLE hwInstData; + + hwInstData = ((S_EAS_DATA*) pEASData)->hwInstData; + + /* if out of data, check for loop */ + if ((pState->bytesLeft == 0) && (pState->loopSamples != 0)) + { + if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, (EAS_I32) (pState->startPos + pState->loopLocation))) != EAS_SUCCESS) + return result; + pState->bytesLeft = pState->byteCount = (EAS_I32) pState->bytesLeftLoop; + pState->flags &= ~PCM_FLAGS_EMPTY; + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "LinearPCMDecode: Rewind file to %d, bytesLeft = %d\n", pState->startPos, pState->bytesLeft); */ } + } + + if (pState->bytesLeft) + { + + /* check format byte for 8-bit samples */ + if (pState->flags & PCM_FLAGS_8_BIT) + { + /* fetch left or mono sample */ + if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) + return result; + + /* if unsigned */ + if (pState->flags & PCM_FLAGS_UNSIGNED) + { + /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ + pState->decoderL.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); + } + else + { + /*lint -e{734} converting signed 8-bit to signed 16-bit */ + pState->decoderL.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); + } + pState->bytesLeft--; + + /* fetch right sample */ + if(pState->flags & PCM_FLAGS_STEREO) + { + if ((result = EAS_HWGetByte(hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) + return result; + + /* if unsigned */ + if (pState->flags & PCM_FLAGS_UNSIGNED) + { + /*lint -e{734} converting unsigned 8-bit to signed 16-bit */ + pState->decoderR.x1 = (EAS_PCM)(((EAS_PCM) pState->srcByte << 8) ^ 0x8000); + } + else + { + /*lint -e{734} converting signed 8-bit to signed 16-bit */ + pState->decoderR.x1 = (EAS_PCM)((EAS_PCM) pState->srcByte << 8); + } + pState->bytesLeft--; + } + } + + /* must be 16-bit samples */ + else + { + //unsigned 16 bit currently not supported + if (pState->flags & PCM_FLAGS_UNSIGNED) + { + return EAS_ERROR_INVALID_PCM_TYPE; + } + + /* fetch left or mono sample */ + if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderL.x1, EAS_FALSE)) != EAS_SUCCESS) + return result; + pState->bytesLeft -= 2; + + /* fetch right sample */ + if(pState->flags & PCM_FLAGS_STEREO) + { + if ((result = EAS_HWGetWord(hwInstData, pState->fileHandle, &pState->decoderR.x1, EAS_FALSE)) != EAS_SUCCESS) + return result; + pState->bytesLeft -= 2; + } + } + } + + /* no more data, force zero samples */ + else + pState->decoderL.x1 = pState->decoderR.x1 = 0; + + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * LinearPCMLocate() + *---------------------------------------------------------------------------- + * Purpose: + * Locate in a linear PCM stream + *---------------------------------------------------------------------------- +*/ +static EAS_RESULT LinearPCMLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time) +{ + EAS_RESULT result; + EAS_I32 temp; + EAS_I32 secs, msecs; + EAS_INT shift; + + /* calculate size of sample frame */ + if (pState->flags & PCM_FLAGS_8_BIT) + shift = 0; + else + shift = 1; + if (pState->flags & PCM_FLAGS_STEREO) + shift++; + + /* break down into secs and msecs */ + secs = time / 1000; + msecs = time - (secs * 1000); + + /* calculate sample number fraction from msecs */ + temp = (msecs * pState->sampleRate); + temp = (temp >> 10) + ((temp * 49) >> 21); + + /* add integer sample count */ + temp += secs * pState->sampleRate; + + /* calculate the position based on sample frame size */ + /*lint -e{703} use shift for performance */ + temp <<= shift; + + /* past end of sample? */ + if (temp > (EAS_I32) pState->loopStart) + { + /* if not looped, flag error */ + if (pState->loopSamples == 0) + { + pState->bytesLeft = 0; + pState->flags |= PCM_FLAGS_EMPTY; + return EAS_ERROR_LOCATE_BEYOND_END; + } + + /* looped sample - calculate position in loop */ + while (temp > (EAS_I32) pState->loopStart) + temp -= (EAS_I32) pState->loopStart; + } + + /* seek to new position */ + if ((result = EAS_PESeek(pEASData, pState, &temp)) != EAS_SUCCESS) + return result; + + /* reset state */ + if ((pState->state != EAS_STATE_PAUSING) && (pState->state != EAS_STATE_PAUSED)) + pState->state = EAS_STATE_READY; + + return EAS_SUCCESS; +} + +/*---------------------------------------------------------------------------- + * EAS_PESeek + *---------------------------------------------------------------------------- + * Purpose: + * Locate to a particular byte in a PCM stream + *---------------------------------------------------------------------------- + * This bit is tricky because the chunks may not be contiguous, + * so we have to rely on the parser to position in the file. We + * do this by seeking to the end of each chunk and simulating an + * empty buffer condition until we get to where we want to go. + * + * A better solution would be a parser API for re-positioning, + * but there isn't time at the moment to re-factor all the + * parsers to support a new API. + *---------------------------------------------------------------------------- +*/ +EAS_RESULT EAS_PESeek (S_EAS_DATA *pEASData, S_PCM_STATE *pState, EAS_I32 *pLocation) +{ + EAS_RESULT result; + + /* seek to start of audio */ + if ((result = EAS_HWFileSeek(pEASData->hwInstData, pState->fileHandle, pState->startPos)) != EAS_SUCCESS) + { + pState->state = EAS_STATE_ERROR; + return result; + } + pState->bytesLeft = pState->bytesLeftLoop; + + /* skip through chunks until we find the right chunk */ + while (*pLocation > (EAS_I32) pState->bytesLeft) + { + /* seek to end of audio chunk */ + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", pState->bytesLeft); */ } + if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, pState->bytesLeft)) != EAS_SUCCESS) + { + pState->state = EAS_STATE_ERROR; + return result; + } + *pLocation -= pState->bytesLeft; + pState->bytesLeft = 0; + pState->flags |= PCM_FLAGS_EMPTY; + + /* retrieve more data */ + if (pState->pCallback) + (*pState->pCallback)(pEASData, pState->cbInstData, pState, EAS_STATE_EMPTY); + + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: bytesLeft=%d, byte location = %d\n", pState->bytesLeft, *pLocation); */ } + + /* no more samples */ + if (pState->bytesLeft == 0) + return EAS_ERROR_LOCATE_BEYOND_END; + } + + /* seek to new offset in current chunk */ + if (*pLocation > 0) + { + { /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "EAS_PESeek: Seek to offset = %d\n", *pLocation); */ } + if ((result = EAS_HWFileSeekOfs(pEASData->hwInstData, pState->fileHandle, *pLocation)) != EAS_SUCCESS) + { + pState->state = EAS_STATE_ERROR; + return result; + } + + /* if not streamed, calculate number of bytes left */ + if (pState->flags & PCM_FLAGS_STREAMING) + pState->bytesLeft = 0x7fffffff; + else + pState->bytesLeft -= *pLocation; + } + return EAS_SUCCESS; +} + -- cgit v1.2.3