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