added MS-ADPCM

This commit is contained in:
Volker Fischer 2008-08-09 14:35:44 +00:00
parent 441e320e79
commit 83faac5a6d
3 changed files with 335 additions and 26 deletions

View file

@ -4,8 +4,8 @@
* Author(s):
* Volker Fischer, Erik de Castro Lopo
*
* This code is based on the Open-Source implementation of IMA-ADPCM written
* by Erik de Castro Lopo <erikd[at-#]mega-nerd[dot*]com> in 1999-2004
* This code is based on the Open-Source implementation of IMA-ADPCM / MS-ADPCM
* written by Erik de Castro Lopo <erikd[at-#]mega-nerd[dot*]com> in 1999-2004
*
* Changes:
* - only support for one channel
@ -46,7 +46,11 @@ int CAudioCompression::Init ( const int iNewAudioLen,
case CT_IMAADPCM:
return ImaAdpcm.Init ( iNewAudioLen );
default: return 0;
case CT_MSADPCM:
return MsAdpcm.Init ( iNewAudioLen );
default:
return 0;
}
}
@ -60,7 +64,7 @@ CVector<unsigned char> CAudioCompression::Encode ( const CVector<short>& vecsAud
for ( int i = 0; i < iAudSize; i++ )
{
vecbyOut[2 * i] = vecsAudio[i] & 0xFF;
vecbyOut[2 * i] = vecsAudio[i] & 0xFF;
vecbyOut[2 * i + 1] = ( vecsAudio[i] >> 8 ) & 0xFF;
}
return vecbyOut;
@ -72,6 +76,9 @@ CVector<unsigned char> CAudioCompression::Encode ( const CVector<short>& vecsAud
case CT_IMAADPCM:
return ImaAdpcm.Encode ( vecsAudio ); // IMA-ADPCM
case CT_MSADPCM:
return MsAdpcm.Encode ( vecsAudio ); // MS-ADPCM
default:
return CVector<unsigned char> ( 0 );
}
@ -105,6 +112,9 @@ CVector<short> CAudioCompression::Decode ( const CVector<unsigned char>& vecbyAd
case CT_IMAADPCM:
return ImaAdpcm.Decode ( vecbyAdpcm ); // IMA-ADPCM
case CT_MSADPCM:
return MsAdpcm.Decode ( vecbyAdpcm ); // MS-ADPCM
default:
return CVector<short> ( 0 );
}
@ -112,7 +122,10 @@ CVector<short> CAudioCompression::Decode ( const CVector<unsigned char>& vecbyAd
}
/* IMA-ADPCM implementation ------------------------------------------------- */
/******************************************************************************\
* IMA-ADPCM implementation *
\******************************************************************************/
int CImaAdpcm::Init ( const int iNewAudioLen )
{
// set lengths for audio and compressed data
@ -132,10 +145,11 @@ CVector<unsigned char> CImaAdpcm::Encode ( const CVector<short>& vecsAudio )
CVector<unsigned char> vecbyAdpcmTemp;
// init size
vecbyAdpcm.Init ( iAdpcmSize );
vecbyAdpcm.Init ( iAdpcmSize );
vecbyAdpcmTemp.Init ( iAudSize );
/* encode the block header ----------------------------------------------- */
/* Encode the block header ---------------------------------------------- */
vecbyAdpcm[0] = vecsAudio[0] & 0xFF;
vecbyAdpcm[1] = ( vecsAudio[0] >> 8 ) & 0xFF;
vecbyAdpcm[2] = iStepindEnc;
@ -143,7 +157,7 @@ CVector<unsigned char> CImaAdpcm::Encode ( const CVector<short>& vecsAudio )
int iPrevAudio = vecsAudio[0];
/* encode the samples as 4 bit ------------------------------------------- */
/* Encode the samples as 4 bit ------------------------------------------ */
for ( i = 1; i < iAudSize; i++ )
{
// init diff and step
@ -187,15 +201,15 @@ CVector<unsigned char> CImaAdpcm::Encode ( const CVector<short>& vecsAudio )
iStepindEnc += ima_indx_adjust[bytecode];
// check that values do not exceed the bounds
iPrevAudio = CheckBounds ( iPrevAudio, _MINSHORT, _MAXSHORT );
iPrevAudio = CheckBounds ( iPrevAudio, _MINSHORT, _MAXSHORT );
iStepindEnc = CheckBounds ( iStepindEnc, 0, IMA_STEP_SIZE_TAB_LEN - 1 );
// use the input buffer as an intermediate result buffer
// use a temporary buffer as an intermediate result buffer
vecbyAdpcmTemp[i] = bytecode;
}
/* pack the 4 bit encoded samples ---------------------------------------- */
/* Pack the 4 bit encoded samples --------------------------------------- */
// The first encoded audio sample is in header
vecbyAdpcm[3] = vecbyAdpcmTemp[1] & 0x0F;
@ -216,7 +230,7 @@ CVector<short> CImaAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
vecsAudio.Init ( iAudSize );
/* read and check the block header --------------------------------------- */
/* Read the block header ------------------------------------------------ */
int current = vecbyAdpcm[0] | ( vecbyAdpcm[1] << 8 );
if ( current & 0x8000 )
{
@ -230,10 +244,10 @@ CVector<short> CImaAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
vecsAudio[0] = current;
/* --------------------------------------------------------------------------
pull apart the packed 4 bit samples and store them in their correct sample
positions */
// The first encoded audio sample is in header
/* -------------------------------------------------------------------------
pull apart the packed 4 bit samples and store them in their correct
sample positions */
// the first encoded audio sample is in header
vecsAudio[1] = vecbyAdpcm[3] & 0x0F;
for ( i = 4; i < iAdpcmSize; i++ )
@ -244,7 +258,7 @@ CVector<short> CImaAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
}
/* decode the encoded 4 bit samples -------------------------------------- */
/* Decode the encoded 4 bit samples ------------------------------------- */
for ( i = 1; i < iAudSize; i++ )
{
const short bytecode = vecsAudio[i] & 0xF ;
@ -286,3 +300,255 @@ CVector<short> CImaAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
return vecsAudio;
}
/******************************************************************************\
* MS-ADPCM implementation *
\******************************************************************************/
/*
MS ADPCM block layout:
byte purpose
0 block predictor [0..6]
1,2 initial idelta (positive)
3,4 sample 1
5,6 sample 0
7..n packed bytecodes
*/
int CMsAdpcm::Init ( const int iNewAudioLen )
{
// set lengths for audio and compressed data
iAudSize = iNewAudioLen;
iAdpcmSize = 7 /* bytes header */ + (int) ceil (
(double) ( iAudSize - 2 /* first two samples are in header */ ) / 2 );
return iAdpcmSize;
}
CVector<unsigned char> CMsAdpcm::Encode ( const CVector<short>& vecsAudio )
{
CVector<short> vecsAudioTemp;
CVector<unsigned char> vecbyAdpcm;
// init size
vecsAudioTemp.Init ( iAudSize );
vecbyAdpcm.Init ( iAdpcmSize );
// copy input vector (because we want to overwrite it)
vecsAudioTemp = vecsAudio;
// choose predictor
int bpred;
int idelta;
ChoosePredictor ( vecsAudio, bpred, idelta );
/* Encode the block header ---------------------------------------------- */
vecbyAdpcm[0] = bpred;
vecbyAdpcm[1] = idelta & 0xFF;
vecbyAdpcm[2] = ( idelta >> 8 ) & 0xFF;
vecbyAdpcm[3] = vecsAudio[1] & 0xFF;
vecbyAdpcm[4] = ( vecsAudio[1] >> 8 ) & 0xFF;
vecbyAdpcm[5] = vecsAudio[0] & 0xFF;
vecbyAdpcm[6] = ( vecsAudio[0] >> 8 ) & 0xFF;
/* Encode the samples as 4 bit ------------------------------------------ */
unsigned int blockindx = 7;
unsigned char byte = 0;
for ( int k = 2; k < iAudSize; k++ )
{
const int predict = ( vecsAudioTemp[k - 1] * ms_AdaptCoeff1[bpred] +
vecsAudioTemp[k - 2] * ms_AdaptCoeff2[bpred] ) >> 8;
int errordelta = ( vecsAudio[k] - predict ) / idelta;
if ( errordelta < -8 )
{
errordelta = -8 ;
}
else
{
if (errordelta > 7)
{
errordelta = 7;
}
}
int newsamp = predict + ( idelta * errordelta );
if ( newsamp > 32767 )
{
newsamp = 32767;
}
else
{
if ( newsamp < -32768 )
{
newsamp = -32768;
}
}
if ( errordelta < 0 )
{
errordelta += 0x10;
}
byte = ( byte << 4 ) | ( errordelta & 0xF );
if ( k % 2 )
{
vecbyAdpcm[blockindx++] = byte;
byte = 0;
}
idelta = ( idelta * ms_AdaptationTable[errordelta] ) >> 8;
if ( idelta < 16 )
{
idelta = 16;
}
vecsAudioTemp[k] = newsamp;
}
return vecbyAdpcm;
}
CVector<short> CMsAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
{
CVector<short> vecsAudio;
short bytecode;
vecsAudio.Init ( iAudSize );
/* Read the block header ------------------------------------------------ */
short bpred = vecbyAdpcm[0];
if ( bpred >= 7 )
{
// no valid MS ADPCM stream, do not decode
return vecsAudio;
}
short chan_idelta = vecbyAdpcm[1] | ( vecbyAdpcm[2] << 8 );
vecsAudio[1] = vecbyAdpcm[3] | ( vecbyAdpcm[4] << 8 );
vecsAudio[0] = vecbyAdpcm[5] | ( vecbyAdpcm[6] << 8 );
/* -------------------------------------------------------------------------
pull apart the packed 4 bit samples and store them in their correct
sample positions */
for ( int i = 7; i < iAdpcmSize; i++ )
{
bytecode = vecbyAdpcm[i];
vecsAudio[2 * i - 12] = ( bytecode >> 4 ) & 0x0F;
vecsAudio[2 * i - 11] = bytecode & 0x0F;
}
/* Decode the encoded 4 bit samples ------------------------------------- */
for ( int k = 2; k < iAudSize; k ++ )
{
bytecode = vecsAudio[k] & 0xF;
// compute next Adaptive Scale Factor (ASF)
int idelta = chan_idelta;
// => / 256 => FIXED_POINT_ADAPTATION_BASE == 256
chan_idelta = ( ms_AdaptationTable[bytecode] * idelta ) >> 8;
if ( chan_idelta < 16 )
{
chan_idelta = 16;
}
if ( bytecode & 0x8 )
{
bytecode -= 0x10;
}
// => / 256 => FIXED_POINT_COEFF_BASE == 256
const int predict = ( ( vecsAudio[k - 1] * ms_AdaptCoeff1[bpred] ) +
( vecsAudio[k - 2] * ms_AdaptCoeff2[bpred] ) ) >> 8;
int current = ( bytecode * idelta ) + predict;
if ( current > 32767 )
{
current = 32767 ;
}
else
{
if ( current < -32768 )
{
current = -32768;
}
}
vecsAudio[k] = current;
}
return vecsAudio;
}
void CMsAdpcm::ChoosePredictor ( const CVector<short>& vecsAudio,
int& block_pred,
int& idelta )
{
/*
Choosing the block predictor:
Each block requires a predictor and an idelta for each channel. The
predictor is in the range [0..6] which is an index into the two AdaptCoeff
tables. The predictor is chosen by trying all of the possible predictors on
a small set of samples at the beginning of the block. The predictor with the
smallest average abs (idelta) is chosen as the best predictor for this
block. The value of idelta is chosen to to give a 4 bit code value of +/- 4
(approx. half the max. code value). If the average abs (idelta) is zero, the
sixth predictor is chosen. If the value of idelta is less then 16 it is set
to 16.
*/
unsigned int best_bpred = 0;
unsigned int best_idelta = 0;
/* Microsoft uses an IDELTA_COUNT (number of sample pairs used to choose
best predictor) value of 3. The best possible results would be obtained
by using all the samples to choose the predictor. */
unsigned int idelta_count = min ( MSADPCM_IDELTA_COUNT, vecsAudio.Size() - 1 );
for ( unsigned int bpred = 0; bpred < MSADPCM_ADAPT_COEFF_COUNT; bpred++ )
{
unsigned int idelta_sum = 0 ;
for ( unsigned int k = 2 ; k < 2 + idelta_count ; k++ )
{
idelta_sum += abs ( vecsAudio[k] -
( ( vecsAudio[k - 1] * ms_AdaptCoeff1[bpred] +
vecsAudio[k - 2] * ms_AdaptCoeff2[bpred] ) >> 8 ) );
}
idelta_sum /= ( 4 * idelta_count );
if ( bpred == 0 || idelta_sum < best_idelta )
{
best_bpred = bpred;
best_idelta = idelta_sum;
}
if ( !idelta_sum )
{
best_bpred = bpred;
best_idelta = 16;
break;
}
}
if ( best_idelta < 16 )
{
best_idelta = 16;
}
block_pred = best_bpred;
idelta = best_idelta;
return;
}

View file

@ -31,18 +31,20 @@
/* Definitions ****************************************************************/
// tables
// tables IMA-ADPCM
#define IMA_INDX_ADJUST_TAB_LEN 16
static int ima_indx_adjust[IMA_INDX_ADJUST_TAB_LEN] =
{ -1, -1, -1, -1, /* +0 - +3, decrease the step size */
2, 4, 6, 8, /* +4 - +7, increase the step size */
-1, -1, -1, -1, /* -0 - -3, decrease the step size */
2, 4, 6, 8, /* -4 - -7, increase the step size */
{
-1, -1, -1, -1, /* +0 - +3, decrease the step size */
2, 4, 6, 8, /* +4 - +7, increase the step size */
-1, -1, -1, -1, /* -0 - -3, decrease the step size */
2, 4, 6, 8, /* -4 - -7, increase the step size */
};
#define IMA_STEP_SIZE_TAB_LEN 89
static int ima_step_size[IMA_STEP_SIZE_TAB_LEN] =
{ 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 19, 21, 23, 25, 28, 31, 34, 37, 41, 45,
{
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,
@ -51,6 +53,26 @@ static int ima_step_size[IMA_STEP_SIZE_TAB_LEN] =
32767
};
// tables MS-ADPCM
#define MSADPCM_IDELTA_COUNT 20
#define MSADPCM_ADAPT_COEFF_COUNT 7
static int ms_AdaptCoeff1[MSADPCM_ADAPT_COEFF_COUNT] =
{
256, 512, 0, 192, 240, 460, 392
};
static int ms_AdaptCoeff2[MSADPCM_ADAPT_COEFF_COUNT] =
{
0, -256, 0, 64, 0, -208, -232
};
static int ms_AdaptationTable[] =
{
230, 230, 230, 230, 307, 409, 512, 614,
768, 614, 512, 409, 307, 230, 230, 230
};
/* Classes ********************************************************************/
/* IMA-ADPCM ---------------------------------------------------------------- */
@ -87,11 +109,32 @@ protected:
};
/* MS-ADPCM ----------------------------------------------------------------- */
class CMsAdpcm
{
public:
CMsAdpcm() {}
virtual ~CMsAdpcm() {}
int Init ( const int iNewAudioLen );
CVector<unsigned char> Encode ( const CVector<short>& vecsAudio );
CVector<short> Decode ( const CVector<unsigned char>& vecbyAdpcm );
protected:
int iAudSize;
int iAdpcmSize;
void ChoosePredictor ( const CVector<short>& vecsAudio,
int& block_pred,
int& idelta );
};
/* Audio compression class -------------------------------------------------- */
class CAudioCompression
{
public:
enum EAudComprType { CT_NONE, CT_IMAADPCM };
enum EAudComprType { CT_NONE, CT_IMAADPCM, CT_MSADPCM };
CAudioCompression() {}
virtual ~CAudioCompression() {}
@ -104,8 +147,8 @@ public:
protected:
EAudComprType eAudComprType;
CImaAdpcm ImaAdpcm;
CMsAdpcm MsAdpcm;
int iCodeSize;
};
#endif /* !defined ( AUDIOCOMPR_H_OIHGE76GEKJH3249_GEG98EG3_43441912__INCLUDED_ ) */

View file

@ -300,7 +300,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent )
"<li>Qt cross-platform application framework: <i>http://trolltech.com</li>"
"<li>Audio reverberation code: by Perry R. Cook and Gary P. Scavone, "
"1995 - 2004 (taken from \"The Synthesis ToolKit in C++ (STK)\")</li>"
"<li>IMA-ADPCM: by Erik de Castro Lopo</li>"
"<li>ADPCM coders by Erik de Castro Lopo</li>"
"<li>Parts from Dream DRM Receiver by Volker Fischer and Alexander "
"Kurpiers: <i>http://drm.sf.net</li>"
"</ul>"