|
|
//========= Copyright Valve Corporation, All rights reserved. ============//
/***************************************************************************
* * Copyright (C) 2001 Microsoft Corporation. All Rights Reserved. * * File: imaadpcm.cpp * Content: IMA ADPCM CODEC. * History: * Date By Reason * ==== == ====== * 04/29/01 dereks Created. * 06/12/01 jharding Adapted for command-line encode * ****************************************************************************/
#include <stdio.h>
#include <wtypes.h>
#include <assert.h>
#include "imaadpcm.h"
// 1/2 the range of the searchable step indices
// for a particular block when optimizing on a
// per-block basis. Widening or narrowing this
// range may produce better/worse encodings.
// Experimentation may be necessary. Higher values
// cause each block to be encoded better, but may
// produce popping in particularly fast attacks across
// blocks, while smaller values limit the number
// of encodings you consider
#define STEPINDEXSEARCHRANGE (24)
/****************************************************************************
* * CImaAdpcmCodec * * Description: * Object constructor. * * Arguments: * (void) * * Returns: * (void) * ****************************************************************************/
//
// This array is used by NextStepIndex to determine the next step index to use.
// The step index is an index to the m_asStep[] array, below.
//
const short CImaAdpcmCodec::m_asNextStep[16] = { -1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 };
//
// This array contains the array of step sizes used to encode the ADPCM
// samples. The step index in each ADPCM block is an index to this array.
//
const short CImaAdpcmCodec::m_asStep[89] = { 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45, 50, 55, 60, 66, 73, 80, 88, 97, 107, 118, 130, 143, 157, 173, 190, 209, 230, 253, 279, 307, 337, 371, 408, 449, 494, 544, 598, 658, 724, 796, 876, 963, 1060, 1166, 1282, 1411, 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767 };
CImaAdpcmCodec::CImaAdpcmCodec ( void ) { }
/****************************************************************************
* * ~CImaAdpcmCodec * * Description: * Object destructor. * * Arguments: * (void) * * Returns: * (void) * ****************************************************************************/
CImaAdpcmCodec::~CImaAdpcmCodec ( void ) { }
/****************************************************************************
* * Initialize * * Description: * Initializes the object. * * Arguments: * LPCIMAADPCMWAVEFORMAT [in]: encoded data format. * BOOL [in]: TRUE to initialize the object as an encoder. * * Returns: * BOOL: TRUE on success. * ****************************************************************************/
BOOL CImaAdpcmCodec::Initialize ( LPCIMAADPCMWAVEFORMAT pwfxEncode, CODEC_MODE cmCodecMode ) { static const LPFNIMAADPCMCONVERT apfnConvert[2][2] = { { DecodeM16, DecodeS16 }, { EncodeM16, EncodeS16 } }; if(!IsValidImaAdpcmFormat(pwfxEncode)) { return FALSE; }
//
// Save the format data
//
m_wfxEncode = *pwfxEncode; m_cmCodecMode = cmCodecMode;
//
// Set up the conversion function
//
m_pfnConvert = apfnConvert[!(m_cmCodecMode == CODEC_MODE_DECODE)][m_wfxEncode.wfx.nChannels - 1];
//
// Initialize the stepping indices
//
m_nStepIndexL = m_nStepIndexR = 0;
return TRUE; }
/****************************************************************************
* * Convert * * Description: * Converts data from the source to destination format. * * Arguments: * LPCVOID [in]: source buffer. * LPVOID [out]: destination buffer. * UINT [in]: block count. * * Returns: * BOOL: TRUE on success. * ****************************************************************************/
BOOL CImaAdpcmCodec::Convert ( LPCVOID pvSrc, LPVOID pvDst, UINT cBlocks ) { // Array of decoders
static const LPFNIMAADPCMCONVERT apfnDecoders[2] = { DecodeM16, DecodeS16 };
// Both destination and source block sizes
DWORD dwSrcBlockSize = m_wfxEncode.wfx.nChannels * m_wfxEncode.wSamplesPerBlock * sizeof(short); DWORD dwDstBlockSize = m_wfxEncode.wfx.nBlockAlign;
// Zero out the output
ZeroMemory( pvDst, cBlocks * dwDstBlockSize );
switch( m_cmCodecMode ) { case CODEC_MODE_DECODE: // If we are decoding, just do it
return m_pfnConvert( (LPBYTE)pvSrc, (LPBYTE)pvDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR );
case CODEC_MODE_ENCODE_NORMAL: // Normal encode
// We have some output right now, so this becomes a separate case.
// Otherwise, it would be the same as CODEC_MODE_DECODE
{ printf("Using normal encoding...\n");
// Allocate temporary buffers
LPBYTE pvDecoded = new BYTE[cBlocks * dwSrcBlockSize]; if( !pvDecoded ) return FALSE;
// Find the decoder
LPFNIMAADPCMCONVERT pfnOppConvert; pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
// Encode the stream
if( !m_pfnConvert( (LPBYTE)pvSrc, (LPBYTE)pvDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) ) { delete[] pvDecoded; return FALSE; }
// Decode it back
if( !pfnOppConvert( (LPBYTE)pvDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) ) { delete[] pvDecoded; return FALSE; }
// Report the normal difference
printf( "Difference between original and decoded streams: 0x%I64x\n", CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize ) );
delete[] pvDecoded; } break;
case CODEC_MODE_ENCODE_OPTIMIZE_WHOLE_FILE: // Optimize whole file encode
// Encode the file with each possible starting step index
// and pick the best one
{ printf("Using whole file encoding...\n");
// Allocate temporary buffers
LPBYTE pvTempDst = new BYTE[cBlocks * dwDstBlockSize]; if(!pvTempDst) return FALSE;
LPBYTE pvDecoded = new BYTE[cBlocks * dwSrcBlockSize]; if(!pvDecoded) { delete[] pvTempDst; return FALSE; }
// Find the decoder
LPFNIMAADPCMCONVERT pfnOppConvert; pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
// Keep track of the best encoding, as well as the chosen step index
ULONGLONG ullLeastDiff = (ULONGLONG)-1; UINT uChosen = (UINT)-1;
// Encode the entire stream with each step index and choose the best one
for( UINT i = 0; i < ARRAYSIZE(m_asStep); ++i ) { ZeroMemory( pvTempDst, cBlocks * dwDstBlockSize ); ZeroMemory( pvDecoded, cBlocks * dwSrcBlockSize );
// Encode
m_nStepIndexL = m_nStepIndexR = i; if( !m_pfnConvert( (LPBYTE)pvSrc, pvTempDst, cBlocks, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) ) continue;
// Decode
if( !pfnOppConvert( pvTempDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) ) continue;
// Diff
ULONGLONG ullDiff = CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize ); if( ullDiff < ullLeastDiff ) { ullLeastDiff = ullDiff; uChosen = i; CopyMemory( (LPBYTE)pvDst, pvTempDst, cBlocks * dwDstBlockSize ); } }
// Report the optimized difference
printf( "Difference between original and decoded streams: 0x%I64x\n", ullLeastDiff ); printf( "Step index chosen: %d\n", uChosen );
delete[] pvTempDst; delete[] pvDecoded; } break;
case CODEC_MODE_ENCODE_OPTIMIZE_EACH_BLOCK: // Optimize per block encode
// Encode each block within the file with each
// possible starting step index and pick the
// best one for each block
{ printf("Using per-block encoding\n\n");
// Allocate temporary buffers
LPBYTE pvTempDst = new BYTE[dwDstBlockSize]; if( !pvTempDst ) return FALSE;
LPBYTE pvDecoded = new BYTE[dwSrcBlockSize]; if( !pvDecoded ) { delete[] pvTempDst; return FALSE; }
// Find the decoder
LPFNIMAADPCMCONVERT pfnOppConvert; pfnOppConvert = apfnDecoders[m_wfxEncode.wfx.nChannels - 1];
// We keep track of the best step index of the previous block
// This enables us to search a small range of values close to
// this value (the size of 2*STEPINDEXSEARCHRANGE)
// To begin, the previous block's best step index was -1.
INT iPreviousBestStepIndex = -1;
for( UINT c = 0; c < cBlocks; ++c ) { ULONGLONG ullLeastDiff = (ULONGLONG)-1; INT iThisBestStepIndex = -1; INT iStartIndex, iStopIndex;
// Setup the start/stop indices properly
if( iPreviousBestStepIndex == -1 ) { // If the previous best step index is -1,
// then we haven't yet encoded a block. Search
// through the entire range of step indices,
// rather than just in a limited range
iStartIndex = 0; iStopIndex = ARRAYSIZE( m_asStep ); } else { // Keep the range of indices to search limited
// to around the previously chosen step index
iStartIndex = iPreviousBestStepIndex - STEPINDEXSEARCHRANGE; iStopIndex = iPreviousBestStepIndex + STEPINDEXSEARCHRANGE + 1; }
// Try each step index in the searchable range and choose the best one
// for this block
for( INT i = iStartIndex; i < iStopIndex; ++i ) { // Don't consider anything out of range
if( i < 0 || i >= ARRAYSIZE( m_asStep ) ) continue;
// Zero out the temporary buffers
ZeroMemory( pvTempDst, dwDstBlockSize ); ZeroMemory( pvDecoded, dwSrcBlockSize );
// Encode
m_nStepIndexL = m_nStepIndexR = i; if( !m_pfnConvert( (LPBYTE)pvSrc + c*dwSrcBlockSize, pvTempDst, 1, m_wfxEncode.wfx.nBlockAlign, m_wfxEncode.wSamplesPerBlock, &m_nStepIndexL, &m_nStepIndexR ) ) continue;
// Decode
if( !pfnOppConvert( pvTempDst, pvDecoded, 1, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ) ) continue; // Diff
ULONGLONG ullDiff = CalcDifference( (LPBYTE)pvSrc + c*dwSrcBlockSize, pvDecoded, 1, cBlocks, dwSrcBlockSize ); if( ullDiff < ullLeastDiff ) { ullLeastDiff = ullDiff; iThisBestStepIndex = i; CopyMemory( (LPBYTE)pvDst + c*dwDstBlockSize, (LPBYTE)pvTempDst, dwDstBlockSize ); } }
// Save the best step index for this block
iPreviousBestStepIndex = iThisBestStepIndex; }
delete[] pvTempDst; delete[] pvDecoded;
// Report on the optimized difference
pvDecoded = new BYTE[cBlocks * dwSrcBlockSize]; pfnOppConvert( (LPBYTE)pvDst, pvDecoded, cBlocks, m_wfxEncode.wfx.nBlockAlign, XBOX_ADPCM_SAMPLES_PER_BLOCK, &m_nStepIndexL, &m_nStepIndexR ); printf( "Difference between original and decoded streams: 0x%I64x\n", CalcDifference( (LPBYTE)pvSrc, pvDecoded, cBlocks, cBlocks, dwSrcBlockSize ) );
delete[] pvDecoded; } break; }
return TRUE; }
/****************************************************************************
* * Reset * * Description: * Resets the conversion operation. * * Arguments: * (void) * * Returns: * (void) * ****************************************************************************/
void CImaAdpcmCodec::Reset ( void ) { //
// Reset the stepping indices
//
m_nStepIndexL = m_nStepIndexR = 0; }
/****************************************************************************
* * GetEncodeAlignment * * Description: * Gets the alignment of an encoded buffer. * * Arguments: * (void) * * Returns: * WORD: alignment, in bytes. * ****************************************************************************/
WORD CImaAdpcmCodec::GetEncodeAlignment ( void ) { return m_wfxEncode.wfx.nBlockAlign; }
/****************************************************************************
* * GetDecodeAlignment * * Description: * Gets the alignment of a decoded buffer. * * Arguments: * (void) * * Returns: * DWORD: alignment, in bytes. * ****************************************************************************/
WORD CImaAdpcmCodec::GetDecodeAlignment ( void ) { return m_wfxEncode.wSamplesPerBlock * m_wfxEncode.wfx.nChannels * IMAADPCM_PCM_BITS_PER_SAMPLE / 8; }
/****************************************************************************
* * CalculateEncodeAlignment * * Description: * Calculates an encoded data block alignment based on a PCM sample * count and an alignment multiplier. * * Arguments: * WORD [in]: channel count. * WORD [in]: PCM samples per block. * * Returns: * WORD: alignment, in bytes. * ****************************************************************************/
WORD CImaAdpcmCodec::CalculateEncodeAlignment ( WORD nChannels, WORD nSamplesPerBlock ) { const WORD nEncodedSampleBits = nChannels * IMAADPCM_BITS_PER_SAMPLE; const WORD nHeaderBytes = nChannels * IMAADPCM_HEADER_LENGTH; INT nBlockAlign;
//
// Calculate the raw block alignment that nSamplesPerBlock dictates. This
// value may include a partial encoded sample, so be sure to round up.
//
// Start with the samples-per-block, minus 1. The first sample is actually
// stored in the header.
//
nBlockAlign = nSamplesPerBlock - 1;
//
// Convert to encoded sample size
//
nBlockAlign *= nEncodedSampleBits; nBlockAlign += 7; nBlockAlign /= 8;
//
// The stereo encoder requires that there be at least two DWORDs to process
//
nBlockAlign += 7; nBlockAlign /= 8; nBlockAlign *= 8;
//
// Add the header
//
nBlockAlign += nHeaderBytes;
// We used an INT temporarily, but the final result should fit into a WORD
assert( nBlockAlign < 0xFFFF ); return (WORD)nBlockAlign; }
/****************************************************************************
* * CreatePcmFormat * * Description: * Creates a PCM format descriptor. * * Arguments: * WORD [in]: channel count. * DWORD [in]: sampling rate. * LPWAVEFORMATEX [out]: format descriptor. * * Returns: * (void) * ****************************************************************************/
void CImaAdpcmCodec::CreatePcmFormat ( WORD nChannels, DWORD nSamplesPerSec, LPWAVEFORMATEX pwfx ) { pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->nChannels = nChannels; pwfx->nSamplesPerSec = nSamplesPerSec; pwfx->nBlockAlign = nChannels * IMAADPCM_PCM_BITS_PER_SAMPLE / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; pwfx->wBitsPerSample = IMAADPCM_PCM_BITS_PER_SAMPLE; }
/****************************************************************************
* * CreateImaAdpcmFormat * * Description: * Creates an IMA ADPCM format descriptor. * * Arguments: * WORD [in]: channel count. * DWORD [in]: sampling rate. * LPIMAADPCMWAVEFORMAT [out]: format descriptor. * * Returns: * (void) * ****************************************************************************/
void CImaAdpcmCodec::CreateImaAdpcmFormat ( WORD nChannels, DWORD nSamplesPerSec, WORD nSamplesPerBlock, LPIMAADPCMWAVEFORMAT pwfx ) { pwfx->wfx.wFormatTag = WAVE_FORMAT_XBOX_ADPCM; pwfx->wfx.nChannels = nChannels; pwfx->wfx.nSamplesPerSec = nSamplesPerSec; pwfx->wfx.nBlockAlign = CalculateEncodeAlignment(nChannels, nSamplesPerBlock); pwfx->wfx.nAvgBytesPerSec = nSamplesPerSec * pwfx->wfx.nBlockAlign / nSamplesPerBlock; pwfx->wfx.wBitsPerSample = IMAADPCM_BITS_PER_SAMPLE; pwfx->wfx.cbSize = sizeof(*pwfx) - sizeof(pwfx->wfx); pwfx->wSamplesPerBlock = nSamplesPerBlock; }
/****************************************************************************
* * IsValidPcmFormat * * Description: * Validates a format structure. * * Arguments: * LPCWAVEFORMATEX [in]: format. * * Returns: * BOOL: TRUE on success. * ****************************************************************************/
BOOL CImaAdpcmCodec::IsValidPcmFormat ( LPCWAVEFORMATEX pwfx ) { if(WAVE_FORMAT_PCM != pwfx->wFormatTag) { return FALSE; } if((pwfx->nChannels < 1) || (pwfx->nChannels > IMAADPCM_MAX_CHANNELS)) { return FALSE; }
if(IMAADPCM_PCM_BITS_PER_SAMPLE != pwfx->wBitsPerSample) { return FALSE; }
if(pwfx->nChannels * pwfx->wBitsPerSample / 8 != pwfx->nBlockAlign) { return FALSE; }
if(pwfx->nBlockAlign * pwfx->nSamplesPerSec != pwfx->nAvgBytesPerSec) { return FALSE; }
return TRUE; }
/****************************************************************************
* * IsValidXboxAdpcmFormat * * Description: * Validates a format structure. * * Arguments: * LPCIMAADPCMWAVEFORMAT [in]: format. * * Returns: * BOOL: TRUE on success. * ****************************************************************************/
BOOL CImaAdpcmCodec::IsValidImaAdpcmFormat ( LPCIMAADPCMWAVEFORMAT pwfx ) { if(WAVE_FORMAT_XBOX_ADPCM != pwfx->wfx.wFormatTag) { return FALSE; }
if(sizeof(*pwfx) - sizeof(pwfx->wfx) != pwfx->wfx.cbSize) { return FALSE; } if((pwfx->wfx.nChannels < 1) || (pwfx->wfx.nChannels > IMAADPCM_MAX_CHANNELS)) { return FALSE; }
if(IMAADPCM_BITS_PER_SAMPLE != pwfx->wfx.wBitsPerSample) { return FALSE; }
if(CalculateEncodeAlignment(pwfx->wfx.nChannels, pwfx->wSamplesPerBlock) != pwfx->wfx.nBlockAlign) { return FALSE; }
return TRUE; }
/****************************************************************************
* * CalcDifference * * Description: * Calculates the error between two audio buffers. The error is clamped * at (ULONGLONG)-1. Also, the error of a block acts as a percentage of * the maximum possible contribution of a block. * * Arguments: * LPBYTE [in]: First buffer * LPBYTE [in]: Second buffer * UINT [in]: Number of blocks-worth to compare * UINT [in]: Total number of blocks being converted * DWORD [in]: Size of a single block in bytes * * Returns: * ULONGLONG: Difference of the two buffers * ****************************************************************************/ ULONGLONG CImaAdpcmCodec::CalcDifference(LPBYTE pvBuffer1, LPBYTE pvBuffer2, UINT cBlocks, UINT cTotalBlocks, DWORD dwBlockSize) { ULONGLONG ullDiff = 0;
// Each block worth of error can contribute a maximum of this value
const ULONGLONG ullMaxBlockContribution = ( (ULONGLONG)-1 / cTotalBlocks );
// The maximum error in a block is
// (2^16)^2 * m_wfxEncode.wSamplesPerBlock
// = ( 1 << 32 ) * m_wfxEncode.wSamplesPerBlock
const ULONGLONG ullMaxBlockDiff = ( (ULONGLONG)1 << 32 ) * m_wfxEncode.wSamplesPerBlock;
// Now we go through the buffers sample by sample and find the difference
// on a block-by-block basis. The factored difference of a block is
// ullBlockDiff / ullMaxBlockDiff * ullMaxBlockContribution
for( UINT i = 0; i < cBlocks; ++i ) { PSHORT pSamples1 = (PSHORT)(pvBuffer1 + i * dwBlockSize); PSHORT pSamples2 = (PSHORT)(pvBuffer2 + i * dwBlockSize); ULONGLONG ullBlockDiff = 0;
// Find the block difference
for( UINT j = 0; j < m_wfxEncode.wSamplesPerBlock; ++j ) { ULONGLONG ullSampleDiff = (ULONGLONG)(pSamples2[j]) - (ULONGLONG)(pSamples1[j]); ullBlockDiff += ( ullSampleDiff * ullSampleDiff ); }
// Assert that we didn't go over the maximum possible
assert( ullBlockDiff <= ullMaxBlockDiff );
// Add the contribution of this block to the error
ullDiff += (ULONGLONG)( ( (DOUBLE)ullBlockDiff / (DOUBLE)ullMaxBlockDiff ) * ullMaxBlockContribution ); }
assert( ullDiff <= cBlocks * ullMaxBlockContribution );
return ullDiff; }
/****************************************************************************
* * EncodeSample * * Description: * Encodes a sample. * * Arguments: * int [in]: the sample to be encoded. * LPINT [in/out]: the predicted value of the sample. * int [in]: the quantization step size used to encode the sample. * * Returns: * int: the encoded ADPCM sample. * ****************************************************************************/
int CImaAdpcmCodec::EncodeSample ( int nInputSample, LPINT pnPredictedSample, int nStepSize ) { int nPredictedSample; LONG lDifference; int nEncodedSample; nPredictedSample = *pnPredictedSample;
lDifference = nInputSample - nPredictedSample; nEncodedSample = 0;
if(lDifference < 0) { nEncodedSample = 8; lDifference = -lDifference; }
if(lDifference >= nStepSize) { nEncodedSample |= 4; lDifference -= nStepSize; }
nStepSize >>= 1;
if(lDifference >= nStepSize) { nEncodedSample |= 2; lDifference -= nStepSize; }
nStepSize >>= 1;
if(lDifference >= nStepSize) { nEncodedSample |= 1; lDifference -= nStepSize; }
if(nEncodedSample & 8) { nPredictedSample = nInputSample + lDifference - (nStepSize >> 1); } else { nPredictedSample = nInputSample - lDifference + (nStepSize >> 1); }
if(nPredictedSample > 32767) { nPredictedSample = 32767; } else if(nPredictedSample < -32768) { nPredictedSample = -32768; }
*pnPredictedSample = nPredictedSample; return nEncodedSample; }
/****************************************************************************
* * DecodeSample * * Description: * Decodes an encoded sample. * * Arguments: * int [in]: the sample to be decoded. * int [in]: the predicted value of the sample. * int [i]: the quantization step size used to encode the sample. * * Returns: * int: the decoded PCM sample. * ****************************************************************************/
int CImaAdpcmCodec::DecodeSample ( int nEncodedSample, int nPredictedSample, int nStepSize ) { LONG lDifference; LONG lNewSample;
lDifference = nStepSize >> 3;
if(nEncodedSample & 4) { lDifference += nStepSize; }
if(nEncodedSample & 2) { lDifference += nStepSize >> 1; }
if(nEncodedSample & 1) { lDifference += nStepSize >> 2; }
if(nEncodedSample & 8) { lDifference = -lDifference; }
lNewSample = nPredictedSample + lDifference;
if((LONG)(short)lNewSample != lNewSample) { if(lNewSample < -32768) { lNewSample = -32768; } else { lNewSample = 32767; } }
return (int)lNewSample; }
/****************************************************************************
* * Conversion Routines * * Description: * Converts a PCM buffer to ADPCM, or the reverse. * * Arguments: * LPBYTE [in]: source buffer. * LPBYTE [out]: destination buffer. * UINT [in]: block count. * UINT [in]: block alignment of the ADPCM data, in bytes. * UINT [in]: the number of samples in each ADPCM block (not used in * decoding). * LPINT [in/out]: left-channel stepping index. * LPINT [in/out]: right-channel stepping index. * * Returns: * BOOL: TRUE on success. * ****************************************************************************/
BOOL CImaAdpcmCodec::EncodeM16 ( LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT ) { LPBYTE pbBlock; UINT cSamples; int nSample; int nStepSize; int nEncSample1; int nEncSample2; int nPredSample; int nStepIndex;
//
// Save a local copy of the step index so we're not constantly
// dereferencing a pointer.
//
nStepIndex = *pnStepIndexL;
//
// Enter the main loop
//
while(cBlocks--) { pbBlock = pbDst; cSamples = cSamplesPerBlock - 1;
//
// Block header
//
nPredSample = *(short *)pbSrc; pbSrc += sizeof(short);
*(LONG *)pbBlock = MAKELONG(nPredSample, nStepIndex); pbBlock += sizeof(LONG);
//
// We have written the header for this block--now write the data
// chunk (which consists of a bunch of encoded nibbles). Note
// that if we don't have enough data to fill a complete byte, then
// we add a 0 nibble on the end.
//
while(cSamples) { //
// Sample 1
//
nSample = *(short *)pbSrc; pbSrc += sizeof(short); cSamples--;
nStepSize = m_asStep[nStepIndex]; nEncSample1 = EncodeSample(nSample, &nPredSample, nStepSize); nStepIndex = NextStepIndex(nEncSample1, nStepIndex);
//
// Sample 2
//
if(cSamples) { nSample = *(short *)pbSrc; pbSrc += sizeof(short); cSamples--;
nStepSize = m_asStep[nStepIndex]; nEncSample2 = EncodeSample(nSample, &nPredSample, nStepSize); nStepIndex = NextStepIndex(nEncSample2, nStepIndex); } else { nEncSample2 = 0; }
//
// Write out encoded byte.
//
*pbBlock++ = (BYTE)(nEncSample1 | (nEncSample2 << 4)); }
//
// Skip padding
//
pbDst += nBlockAlignment; }
//
// Restore the value of the step index to be used on the next buffer.
//
*pnStepIndexL = nStepIndex;
return TRUE; }
BOOL CImaAdpcmCodec::EncodeS16 ( LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT pnStepIndexL, LPINT pnStepIndexR ) { LPBYTE pbBlock; UINT cSamples; UINT cSubSamples; int nSample; int nStepSize; DWORD dwLeft; DWORD dwRight; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; UINT i;
//
// Save a local copy of the step indices so we're not constantly
// dereferencing a pointer.
//
nStepIndexL = *pnStepIndexL; nStepIndexR = *pnStepIndexR;
//
// Enter the main loop
//
while(cBlocks--) { pbBlock = pbDst; cSamples = cSamplesPerBlock - 1;
//
// LEFT channel block header
//
nPredSampleL = *(short *)pbSrc; pbSrc += sizeof(short);
*(LONG *)pbBlock = MAKELONG(nPredSampleL, nStepIndexL); pbBlock += sizeof(LONG);
//
// RIGHT channel block header
//
nPredSampleR = *(short *)pbSrc; pbSrc += sizeof(short);
*(LONG *)pbBlock = MAKELONG(nPredSampleR, nStepIndexR); pbBlock += sizeof(LONG);
//
// We have written the header for this block--now write the data
// chunk. This consists of 8 left samples (one DWORD of output)
// followed by 8 right samples (also one DWORD). Since the input
// samples are interleaved, we create the left and right DWORDs
// sample by sample, and then write them both out.
//
while(cSamples) { dwLeft = 0; dwRight = 0;
cSubSamples = min(cSamples, 8);
for(i = 0; i < cSubSamples; i++) { //
// LEFT channel
//
nSample = *(short *)pbSrc; pbSrc += sizeof(short);
nStepSize = m_asStep[nStepIndexL]; nEncSampleL = EncodeSample(nSample, &nPredSampleL, nStepSize);
nStepIndexL = NextStepIndex(nEncSampleL, nStepIndexL); dwLeft |= (DWORD)nEncSampleL << (4 * i);
//
// RIGHT channel
//
nSample = *(short *)pbSrc; pbSrc += sizeof(short);
nStepSize = m_asStep[nStepIndexR]; nEncSampleR = EncodeSample(nSample, &nPredSampleR, nStepSize);
nStepIndexR = NextStepIndex(nEncSampleR, nStepIndexR); dwRight |= (DWORD)nEncSampleR << (4 * i); }
//
// Write out encoded DWORDs.
//
*(LPDWORD)pbBlock = dwLeft; pbBlock += sizeof(DWORD);
*(LPDWORD)pbBlock = dwRight; pbBlock += sizeof(DWORD);
cSamples -= cSubSamples; }
//
// Skip padding
//
pbDst += nBlockAlignment; }
//
// Restore the value of the step index to be used on the next buffer.
//
*pnStepIndexL = nStepIndexL; *pnStepIndexR = nStepIndexR;
return TRUE;
}
BOOL CImaAdpcmCodec::DecodeM16 ( LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT, LPINT ) { BOOL fSuccess = TRUE; LPBYTE pbBlock; UINT cSamples; BYTE bSample; int nStepSize; int nEncSample; int nPredSample; int nStepIndex; DWORD dwHeader;
//
// Enter the main loop
//
while(cBlocks--) { pbBlock = pbSrc; cSamples = cSamplesPerBlock - 1; //
// Block header
//
dwHeader = *(LPDWORD)pbBlock; pbBlock += sizeof(DWORD);
nPredSample = (int)(short)LOWORD(dwHeader); nStepIndex = (int)(BYTE)HIWORD(dwHeader);
if(!ValidStepIndex(nStepIndex)) { //
// The step index is out of range - this is considered a fatal
// error as the input stream is corrupted. We fail by returning
// zero bytes converted.
//
fSuccess = FALSE; break; } //
// Write out first sample
//
*(short *)pbDst = (short)nPredSample; pbDst += sizeof(short);
//
// Enter the block loop
//
while(cSamples) { bSample = *pbBlock++;
//
// Sample 1
//
nEncSample = (bSample & (BYTE)0x0F); nStepSize = m_asStep[nStepIndex]; nPredSample = DecodeSample(nEncSample, nPredSample, nStepSize); nStepIndex = NextStepIndex(nEncSample, nStepIndex);
*(short *)pbDst = (short)nPredSample; pbDst += sizeof(short);
cSamples--;
//
// Sample 2
//
if(cSamples) { nEncSample = (bSample >> 4); nStepSize = m_asStep[nStepIndex]; nPredSample = DecodeSample(nEncSample, nPredSample, nStepSize); nStepIndex = NextStepIndex(nEncSample, nStepIndex);
*(short *)pbDst = (short)nPredSample; pbDst += sizeof(short);
cSamples--; } }
//
// Skip padding
//
pbSrc += nBlockAlignment; }
return fSuccess; }
BOOL CImaAdpcmCodec::DecodeS16 ( LPBYTE pbSrc, LPBYTE pbDst, UINT cBlocks, UINT nBlockAlignment, UINT cSamplesPerBlock, LPINT, LPINT ) { BOOL fSuccess = TRUE; LPBYTE pbBlock; UINT cSamples; UINT cSubSamples; int nStepSize; DWORD dwHeader; DWORD dwLeft; DWORD dwRight; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; UINT i;
//
// Enter the main loop
//
while(cBlocks--) { pbBlock = pbSrc; cSamples = cSamplesPerBlock - 1;
//
// LEFT channel header
//
dwHeader = *(LPDWORD)pbBlock; pbBlock += sizeof(DWORD); nPredSampleL = (int)(short)LOWORD(dwHeader); nStepIndexL = (int)(BYTE)HIWORD(dwHeader);
if(!ValidStepIndex(nStepIndexL)) { //
// The step index is out of range - this is considered a fatal
// error as the input stream is corrupted. We fail by returning
// zero bytes converted.
//
fSuccess = FALSE; break; } //
// RIGHT channel header
//
dwHeader = *(LPDWORD)pbBlock; pbBlock += sizeof(DWORD); nPredSampleR = (int)(short)LOWORD(dwHeader); nStepIndexR = (int)(BYTE)HIWORD(dwHeader);
if(!ValidStepIndex(nStepIndexR)) { //
// The step index is out of range - this is considered a fatal
// error as the input stream is corrupted. We fail by returning
// zero bytes converted.
//
fSuccess = FALSE; break; }
//
// Write out first sample
//
*(LPDWORD)pbDst = MAKELONG(nPredSampleL, nPredSampleR); pbDst += sizeof(DWORD);
//
// The first DWORD contains 4 left samples, the second DWORD
// contains 4 right samples. We process the source in 8-byte
// chunks to make it easy to interleave the output correctly.
//
while(cSamples) { dwLeft = *(LPDWORD)pbBlock; pbBlock += sizeof(DWORD); dwRight = *(LPDWORD)pbBlock; pbBlock += sizeof(DWORD);
cSubSamples = min(cSamples, 8); for(i = 0; i < cSubSamples; i++) { //
// LEFT channel
//
nEncSampleL = (dwLeft & 0x0F); nStepSize = m_asStep[nStepIndexL]; nPredSampleL = DecodeSample(nEncSampleL, nPredSampleL, nStepSize); nStepIndexL = NextStepIndex(nEncSampleL, nStepIndexL);
//
// RIGHT channel
//
nEncSampleR = (dwRight & 0x0F); nStepSize = m_asStep[nStepIndexR]; nPredSampleR = DecodeSample(nEncSampleR, nPredSampleR, nStepSize); nStepIndexR = NextStepIndex(nEncSampleR, nStepIndexR);
//
// Write out sample
//
*(LPDWORD)pbDst = MAKELONG(nPredSampleL, nPredSampleR); pbDst += sizeof(DWORD);
//
// Shift the next input sample into the low-order 4 bits.
//
dwLeft >>= 4; dwRight >>= 4; }
cSamples -= cSubSamples; }
//
// Skip padding
//
pbSrc += nBlockAlignment; }
return fSuccess; }
int XboxADPCMSize( int sampleCount, int channelCount, int sampleRate ) { CImaAdpcmCodec codec; IMAADPCMWAVEFORMAT wfxEncode;
// Create an APDCM format structure based off the source format
codec.CreateImaAdpcmFormat( (WORD)channelCount, sampleRate, XBOX_ADPCM_SAMPLES_PER_BLOCK, &wfxEncode );
// Calculate number of ADPCM blocks and length of ADPCM data
DWORD dwDestBlocks = sampleCount / XBOX_ADPCM_SAMPLES_PER_BLOCK; DWORD dwDestLength = dwDestBlocks * wfxEncode.wfx.nBlockAlign;
return dwDestLength; }
void Convert16ToXboxADPCM( const short *pInputBuffer, byte *pOutputBuffer, byte *pOutFormat, int sampleCount, int channelCount, int sampleRate ) { CImaAdpcmCodec codec; IMAADPCMWAVEFORMAT wfxEncode;
// Create an APDCM format structure based off the source format
codec.CreateImaAdpcmFormat( (WORD)channelCount, sampleRate, XBOX_ADPCM_SAMPLES_PER_BLOCK, &wfxEncode ); if ( pOutFormat ) { memcpy( pOutFormat, &wfxEncode, sizeof(wfxEncode) ); }
// Initialize the codec
if ( FALSE == codec.Initialize( &wfxEncode, CODEC_MODE_ENCODE_OPTIMIZE_EACH_BLOCK ) ) { printf( "Couldn't initialize codec.\n" ); return; }
// Convert the data
DWORD dwDestBlocks = sampleCount / XBOX_ADPCM_SAMPLES_PER_BLOCK; if ( FALSE == codec.Convert( (const byte *)pInputBuffer, pOutputBuffer, dwDestBlocks ) ) return; }
|