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