/*---------------------------------------------------------------------------- | |
* | |
* File: | |
* eas_imaadpcm.c | |
* | |
* Contents and purpose: | |
* Implements the IMA ADPCM decoder | |
* | |
* 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: 847 $ | |
* $Date: 2007-08-27 21:30:08 -0700 (Mon, 27 Aug 2007) $ | |
*---------------------------------------------------------------------------- | |
*/ | |
#include "eas_data.h" | |
#include "eas_host.h" | |
#include "eas_pcm.h" | |
#include "eas_math.h" | |
#include "eas_report.h" | |
// #define _DEBUG_IMA_ADPCM_LOCATE | |
/*---------------------------------------------------------------------------- | |
* externs | |
*---------------------------------------------------------------------------- | |
*/ | |
extern const EAS_I16 imaIndexTable[]; | |
extern const EAS_I16 imaStepSizeTable[]; | |
/*---------------------------------------------------------------------------- | |
* prototypes | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT IMADecoderInit (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState); | |
static EAS_RESULT IMADecoderSample (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState); | |
static void IMADecoderADPCM (S_DECODER_STATE *pState, EAS_U8 nibble); | |
static EAS_RESULT IMADecoderLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time); | |
/*---------------------------------------------------------------------------- | |
* IMA ADPCM Decoder interface | |
*---------------------------------------------------------------------------- | |
*/ | |
const S_DECODER_INTERFACE IMADecoder = | |
{ | |
IMADecoderInit, | |
IMADecoderSample, | |
IMADecoderLocate | |
}; | |
/*---------------------------------------------------------------------------- | |
* IMADecoderInit() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Initializes the IMA ADPCM decoder | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
/*lint -esym(715, pEASData) common decoder interface - pEASData not used */ | |
static EAS_RESULT IMADecoderInit (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState) | |
{ | |
pState->decoderL.step = 0; | |
pState->decoderR.step = 0; | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* IMADecoderSample() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Decodes an IMA ADPCM sample | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT IMADecoderSample (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState) | |
{ | |
EAS_RESULT result; | |
EAS_I16 sTemp; | |
/* if high nibble, decode */ | |
if (pState->hiNibble) | |
{ | |
IMADecoderADPCM(&pState->decoderL, (EAS_U8)(pState->srcByte >> 4)); | |
pState->hiNibble = EAS_FALSE; | |
} | |
/* low nibble, need to fetch another byte */ | |
else | |
{ | |
/* check for loop */ | |
if ((pState->bytesLeft == 0) && (pState->loopSamples != 0)) | |
{ | |
/* seek to start of loop */ | |
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->blockCount = 0; | |
pState->flags &= ~PCM_FLAGS_EMPTY; | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "IMADecoderSample: Rewind file to %d, bytesLeft = %d\n", pState->startPos, pState->bytesLeft); */ } | |
} | |
/* if start of block, fetch new predictor and step index */ | |
if ((pState->blockSize != 0) && (pState->blockCount == 0) && (pState->bytesLeft != 0)) | |
{ | |
/* get predicted sample for left channel */ | |
if ((result = EAS_HWGetWord(pEASData->hwInstData, pState->fileHandle, &sTemp, EAS_FALSE)) != EAS_SUCCESS) | |
return result; | |
#ifdef _DEBUG_IMA_ADPCM | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Predictor: Was %d, now %d\n", pState->decoderL.acc, sTemp); */ } | |
#endif | |
pState->decoderL.acc = pState->decoderL.x1 = sTemp; | |
/* get step index for left channel - upper 8 bits are reserved */ | |
if ((result = EAS_HWGetWord(pEASData->hwInstData, pState->fileHandle, &sTemp, EAS_FALSE)) != EAS_SUCCESS) | |
return result; | |
#ifdef _DEBUG_IMA_ADPCM | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Step: Was %d, now %d\n", pState->decoderL.step, sTemp); */ } | |
#endif | |
pState->decoderL.step = sTemp & 0xff; | |
if (pState->flags & PCM_FLAGS_STEREO) | |
{ | |
/* get predicted sample for right channel */ | |
if ((result = EAS_HWGetWord(pEASData->hwInstData, pState->fileHandle, &sTemp, EAS_FALSE)) != EAS_SUCCESS) | |
return result; | |
pState->decoderR.acc = pState->decoderR.x1 = sTemp; | |
/* get step index for right channel - upper 8 bits are reserved */ | |
if ((result = EAS_HWGetWord(pEASData->hwInstData, pState->fileHandle, &sTemp, EAS_FALSE)) != EAS_SUCCESS) | |
return result; | |
#ifdef _DEBUG_IMA_ADPCM | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "Step: Was %d, now %d\n", pState->decoderR.step, sTemp); */ } | |
#endif | |
pState->decoderR.step = sTemp & 0xff; | |
pState->blockCount = pState->blockSize - 8; | |
pState->bytesLeft -= 8; | |
} | |
else | |
{ | |
pState->blockCount = pState->blockSize - 4; | |
pState->bytesLeft -= 4; | |
} | |
} | |
else | |
{ | |
/* get another ADPCM data pair */ | |
if (pState->bytesLeft) | |
{ | |
if ((result = EAS_HWGetByte(pEASData->hwInstData, pState->fileHandle, &pState->srcByte)) != EAS_SUCCESS) | |
return result; | |
/* decode the low nibble */ | |
pState->bytesLeft--; | |
pState->blockCount--; | |
IMADecoderADPCM(&pState->decoderL, (EAS_U8)(pState->srcByte & 0x0f)); | |
if (pState->flags & PCM_FLAGS_STEREO) | |
IMADecoderADPCM(&pState->decoderR, (EAS_U8)(pState->srcByte >> 4)); | |
else | |
pState->hiNibble = EAS_TRUE; | |
} | |
/* out of ADPCM data, generate enough samples to fill buffer */ | |
else | |
{ | |
pState->decoderL.x1 = pState->decoderL.x0; | |
pState->decoderR.x1 = pState->decoderR.x0; | |
} | |
} | |
} | |
return EAS_SUCCESS; | |
} | |
/*---------------------------------------------------------------------------- | |
* IMADecoderADPCM() | |
*---------------------------------------------------------------------------- | |
* Purpose: | |
* Decodes an IMA ADPCM sample | |
* | |
* Inputs: | |
* | |
* | |
* Outputs: | |
* | |
* | |
* Side Effects: | |
* | |
*---------------------------------------------------------------------------- | |
*/ | |
static void IMADecoderADPCM (S_DECODER_STATE *pState, EAS_U8 nibble) | |
{ | |
EAS_INT delta; | |
EAS_INT stepSize; | |
/* get stepsize from table */ | |
stepSize = imaStepSizeTable[pState->step]; | |
/* delta = (abs(delta) + 0.5) * step / 4 */ | |
delta = 0; | |
if (nibble & 4) | |
delta += stepSize; | |
if (nibble & 2) | |
/*lint -e{702} use shift for performance */ | |
delta += stepSize >> 1; | |
if (nibble & 1) | |
/*lint -e{702} use shift for performance */ | |
delta += stepSize >> 2; | |
/*lint -e{702} use shift for performance */ | |
delta += stepSize >> 3; | |
/* integrate the delta */ | |
if (nibble & 8) | |
pState->acc -= delta; | |
else | |
pState->acc += delta; | |
/* saturate */ | |
if (pState->acc > 32767) | |
pState->acc = 32767; | |
if (pState->acc < -32768) | |
pState->acc = -32768; | |
pState->x1 = (EAS_PCM) pState->acc; | |
/* compute new step size */ | |
pState->step += imaIndexTable[nibble]; | |
if (pState->step < 0) | |
pState->step = 0; | |
if (pState->step > 88) | |
pState->step = 88; | |
#ifdef _DEBUG_IMA_ADPCM | |
{ /* dpp: EAS_ReportEx(_EAS_SEVERITY_DETAIL, "In=%u, Pred=%d, Step=%d\n", nibble, pState->acc, imaStepSizeTable[pState->step]); */ } | |
#endif | |
} | |
/*---------------------------------------------------------------------------- | |
* IMADecoderLocate() | |
*---------------------------------------------------------------------------- | |
* Locate in an IMA ADPCM stream | |
*---------------------------------------------------------------------------- | |
*/ | |
static EAS_RESULT IMADecoderLocate (EAS_DATA_HANDLE pEASData, S_PCM_STATE *pState, EAS_I32 time) | |
{ | |
EAS_RESULT result; | |
EAS_I32 temp; | |
EAS_I32 samplesPerBlock; | |
EAS_I32 secs, msecs; | |
/* no need to calculate if time is zero */ | |
if (time == 0) | |
temp = 0; | |
/* not zero */ | |
else | |
{ | |
/* can't seek if not a blocked file */ | |
if (pState->blockSize == 0) | |
return EAS_ERROR_FEATURE_NOT_AVAILABLE; | |
/* calculate number of samples per block */ | |
if (pState->flags & PCM_FLAGS_STEREO) | |
samplesPerBlock = pState->blockSize - 7; | |
else | |
samplesPerBlock = (pState->blockSize << 1) - 7; | |
/* 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; | |
#ifdef _DEBUG_IMA_ADPCM_LOCATE | |
EAS_ReportEx(_EAS_SEVERITY_NOFILTER, 0x2380b977, 0x00000006 , time, temp); | |
#endif | |
/* for looped samples, calculate position in the loop */ | |
if ((temp > pState->byteCount) && (pState->loopSamples != 0)) | |
{ | |
EAS_I32 numBlocks; | |
EAS_I32 samplesPerLoop; | |
EAS_I32 samplesInLastBlock; | |
numBlocks = (EAS_I32) (pState->loopStart / pState->blockSize); | |
samplesInLastBlock = (EAS_I32) pState->loopStart - (numBlocks * pState->blockSize); | |
if (samplesInLastBlock) | |
{ | |
if (pState->flags & PCM_FLAGS_STEREO) | |
samplesInLastBlock = samplesInLastBlock - 7; | |
else | |
/*lint -e{703} use shift for performance */ | |
samplesInLastBlock = (samplesInLastBlock << 1) - 7; | |
} | |
samplesPerLoop = numBlocks * samplesPerBlock + samplesInLastBlock; | |
temp = temp % samplesPerLoop; | |
#ifdef _DEBUG_IMA_ADPCM_LOCATE | |
EAS_ReportEx(_EAS_SEVERITY_NOFILTER, 0x2380b977, 0x00000007 , numBlocks, samplesPerLoop, samplesInLastBlock, temp); | |
#endif | |
} | |
/* find start of block for requested sample */ | |
temp = (temp / samplesPerBlock) * pState->blockSize; | |
#ifdef _DEBUG_IMA_ADPCM_LOCATE | |
EAS_ReportEx(_EAS_SEVERITY_NOFILTER, 0x2380b977, 0x00000008 , temp); | |
#endif | |
} | |
/* seek to new location */ | |
if ((result = EAS_PESeek(pEASData, pState, &temp)) != EAS_SUCCESS) | |
return result; | |
#ifdef _DEBUG_IMA_ADPCM_LOCATE | |
EAS_ReportEx(_EAS_SEVERITY_NOFILTER, 0x2380b977, 0x00000009 , pState->bytesLeft); | |
#endif | |
/* reset state */ | |
pState->blockCount = 0; | |
pState->hiNibble = EAS_FALSE; | |
if ((pState->state != EAS_STATE_PAUSING) && (pState->state != EAS_STATE_PAUSED)) | |
pState->state = EAS_STATE_READY; | |
return EAS_SUCCESS; | |
} | |