You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1531 lines
41 KiB
1531 lines
41 KiB
//========= 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; |
|
}
|
|
|