mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-27 00:26:50 +00:00
1532 lines
41 KiB
C++
1532 lines
41 KiB
C++
//========= 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;
|
|
}
|