From 83faac5a6df014d145dd3358ac010c2affc91fee Mon Sep 17 00:00:00 2001 From: Volker Fischer Date: Sat, 9 Aug 2008 14:35:44 +0000 Subject: [PATCH] added MS-ADPCM --- src/audiocompr.cpp | 300 ++++++++++++++++++++++++++++++++++++++++++--- src/audiocompr.h | 59 +++++++-- src/util.cpp | 2 +- 3 files changed, 335 insertions(+), 26 deletions(-) diff --git a/src/audiocompr.cpp b/src/audiocompr.cpp index 33079000..496abac7 100755 --- a/src/audiocompr.cpp +++ b/src/audiocompr.cpp @@ -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 in 1999-2004 + * This code is based on the Open-Source implementation of IMA-ADPCM / MS-ADPCM + * written by Erik de Castro Lopo 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 CAudioCompression::Encode ( const CVector& 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 CAudioCompression::Encode ( const CVector& vecsAud case CT_IMAADPCM: return ImaAdpcm.Encode ( vecsAudio ); // IMA-ADPCM + case CT_MSADPCM: + return MsAdpcm.Encode ( vecsAudio ); // MS-ADPCM + default: return CVector ( 0 ); } @@ -105,6 +112,9 @@ CVector CAudioCompression::Decode ( const CVector& vecbyAd case CT_IMAADPCM: return ImaAdpcm.Decode ( vecbyAdpcm ); // IMA-ADPCM + case CT_MSADPCM: + return MsAdpcm.Decode ( vecbyAdpcm ); // MS-ADPCM + default: return CVector ( 0 ); } @@ -112,7 +122,10 @@ CVector CAudioCompression::Decode ( const CVector& 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 CImaAdpcm::Encode ( const CVector& vecsAudio ) CVector 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 CImaAdpcm::Encode ( const CVector& 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 CImaAdpcm::Encode ( const CVector& 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 CImaAdpcm::Decode ( const CVector& 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 CImaAdpcm::Decode ( const CVector& 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 CImaAdpcm::Decode ( const CVector& 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 CImaAdpcm::Decode ( const CVector& 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 CMsAdpcm::Encode ( const CVector& vecsAudio ) +{ + CVector vecsAudioTemp; + CVector 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 CMsAdpcm::Decode ( const CVector& vecbyAdpcm ) +{ + CVector 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& 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; +} diff --git a/src/audiocompr.h b/src/audiocompr.h index 51dac471..97985703 100755 --- a/src/audiocompr.h +++ b/src/audiocompr.h @@ -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 Encode ( const CVector& vecsAudio ); + CVector Decode ( const CVector& vecbyAdpcm ); + +protected: + int iAudSize; + int iAdpcmSize; + + void ChoosePredictor ( const CVector& 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_ ) */ diff --git a/src/util.cpp b/src/util.cpp index 4f82beb0..23041296 100755 --- a/src/util.cpp +++ b/src/util.cpp @@ -300,7 +300,7 @@ CAboutDlg::CAboutDlg ( QWidget* parent ) : QDialog ( parent ) "
  • Qt cross-platform application framework: http://trolltech.com
  • " "
  • Audio reverberation code: by Perry R. Cook and Gary P. Scavone, " "1995 - 2004 (taken from \"The Synthesis ToolKit in C++ (STK)\")
  • " - "
  • IMA-ADPCM: by Erik de Castro Lopo
  • " + "
  • ADPCM coders by Erik de Castro Lopo
  • " "
  • Parts from Dream DRM Receiver by Volker Fischer and Alexander " "Kurpiers: http://drm.sf.net
  • " ""