1482 lines
46 KiB
C
1482 lines
46 KiB
C
/*----------------------------------------------------------------------------
|
|
*
|
|
* 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;
|
|
}
|
|
|