jamulus/src/audiocompr.cpp
2009-02-22 11:07:18 +00:00

554 lines
16 KiB
C++
Executable file

/******************************************************************************\
* Copyright (c) 2004-2009
*
* Author(s):
* Volker Fischer, Erik de Castro Lopo
*
* 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
* - put 2 audio samples in header to get even number of audio samples encoded
*
******************************************************************************
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*
\******************************************************************************/
#include "audiocompr.h"
/* Implementation *************************************************************/
int CAudioCompression::Init ( const int iNewAudioLen,
const EAudComprType eNewAuCoTy )
{
eAudComprType = eNewAuCoTy;
switch ( eNewAuCoTy )
{
case CT_NONE:
return iCodeSize = 2 * iNewAudioLen; // short = 2 * byte
case CT_IMAADPCM:
return ImaAdpcm.Init ( iNewAudioLen );
case CT_MSADPCM:
return MsAdpcm.Init ( iNewAudioLen );
default:
return 0;
}
}
CVector<unsigned char> CAudioCompression::Encode ( const CVector<short>& vecsAudio )
{
if ( eAudComprType == CT_NONE )
{
// no compression, simply ship pure samples
CVector<unsigned char> vecbyOut ( iCodeSize );
const int iAudSize = iCodeSize / 2;
for ( int i = 0; i < iAudSize; i++ )
{
vecbyOut[2 * i] = vecsAudio[i] & 0xFF;
vecbyOut[2 * i + 1] = ( vecsAudio[i] >> 8 ) & 0xFF;
}
return vecbyOut;
}
else
{
switch ( eAudComprType )
{
case CT_IMAADPCM:
return ImaAdpcm.Encode ( vecsAudio ); // IMA-ADPCM
case CT_MSADPCM:
return MsAdpcm.Encode ( vecsAudio ); // MS-ADPCM
default:
return CVector<unsigned char> ( 0 );
}
}
}
CVector<short> CAudioCompression::Decode ( const CVector<unsigned char>& vecbyAdpcm )
{
if ( eAudComprType == CT_NONE )
{
// no compression, reassemble pure samples
const int iAudSize = iCodeSize / 2;
CVector<short> vecsOut ( iAudSize );
for ( int i = 0; i < iAudSize; i++ )
{
int current = vecbyAdpcm[2 * i] | ( vecbyAdpcm[2 * i + 1] << 8 );
if ( current & 0x8000 )
{
current -= 0x10000;
}
vecsOut[i] = (short) current;
}
return vecsOut;
}
else
{
switch ( eAudComprType )
{
case CT_IMAADPCM:
return ImaAdpcm.Decode ( vecbyAdpcm ); // IMA-ADPCM
case CT_MSADPCM:
return MsAdpcm.Decode ( vecbyAdpcm ); // MS-ADPCM
default:
return CVector<short> ( 0 );
}
}
}
/******************************************************************************\
* IMA-ADPCM implementation *
\******************************************************************************/
int CImaAdpcm::Init ( const int iNewAudioLen )
{
// set lengths for audio and compressed data
iAudSize = iNewAudioLen;
iAdpcmSize = 4 /* bytes header */ + (int) ceil (
(double) ( iAudSize - 2 /* first two samples are in header */ ) / 2 );
iStepindEnc = 0;
return iAdpcmSize;
}
CVector<unsigned char> CImaAdpcm::Encode ( const CVector<short>& vecsAudio )
{
int i;
CVector<unsigned char> vecbyAdpcm;
CVector<unsigned char> vecbyAdpcmTemp;
// init size
vecbyAdpcm.Init ( iAdpcmSize );
vecbyAdpcmTemp.Init ( iAudSize );
/* Encode the block header ---------------------------------------------- */
vecbyAdpcm[0] = vecsAudio[0] & 0xFF;
vecbyAdpcm[1] = ( vecsAudio[0] >> 8 ) & 0xFF;
vecbyAdpcm[2] = iStepindEnc;
int iPrevAudio = vecsAudio[0];
/* Encode the samples as 4 bit ------------------------------------------ */
for ( i = 1; i < iAudSize; i++ )
{
// init diff and step
int diff = vecsAudio[i] - iPrevAudio;
Q_ASSERT ( iStepindEnc < IMA_STEP_SIZE_TAB_LEN );
int step = ima_step_size[iStepindEnc];
short bytecode = 0;
int vpdiff = step >> 3;
if ( diff < 0 )
{
bytecode = 8;
diff = -diff;
}
short mask = 4;
while ( mask )
{
if ( diff >= step )
{
bytecode |= mask;
diff -= step;
vpdiff += step;
}
step >>= 1;
mask >>= 1;
}
if ( bytecode & 8 )
{
iPrevAudio -= vpdiff;
}
else
{
iPrevAudio += vpdiff;
}
// adjust step size
Q_ASSERT ( bytecode < IMA_INDX_ADJUST_TAB_LEN );
iStepindEnc += ima_indx_adjust[bytecode];
// check that values do not exceed the bounds
iPrevAudio = CheckBounds ( iPrevAudio, _MINSHORT, _MAXSHORT );
iStepindEnc = CheckBounds ( iStepindEnc, 0, IMA_STEP_SIZE_TAB_LEN - 1 );
// use a temporary buffer as an intermediate result buffer
vecbyAdpcmTemp[i] = bytecode;
}
/* Pack the 4 bit encoded samples --------------------------------------- */
// The first encoded audio sample is in header
vecbyAdpcm[3] = vecbyAdpcmTemp[1] & 0x0F;
for ( i = 4; i < iAdpcmSize; i++ )
{
vecbyAdpcm[i] = vecbyAdpcmTemp[2 * i - 6] & 0x0F;
vecbyAdpcm[i] |= ( vecbyAdpcmTemp[2 * i - 5] << 4 ) & 0xF0;
}
return vecbyAdpcm;
}
CVector<short> CImaAdpcm::Decode ( const CVector<unsigned char>& vecbyAdpcm )
{
int i;
CVector<short> vecsAudio;
vecsAudio.Init ( iAudSize );
/* Read the block header ------------------------------------------------ */
int current = vecbyAdpcm[0] | ( vecbyAdpcm[1] << 8 );
if ( current & 0x8000 )
{
current -= 0x10000;
}
// get and bound step index
int iStepindDec = CheckBounds ( vecbyAdpcm[2], 0, IMA_STEP_SIZE_TAB_LEN - 1 );
// set first sample which was delivered in the header
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
vecsAudio[1] = vecbyAdpcm[3] & 0x0F;
for ( i = 4; i < iAdpcmSize; i++ )
{
const short bytecode = vecbyAdpcm[i];
vecsAudio[2 * i - 6] = bytecode & 0x0F;
vecsAudio[2 * i - 5] = ( bytecode >> 4 ) & 0x0F;
}
/* Decode the encoded 4 bit samples ------------------------------------- */
for ( i = 1; i < iAudSize; i++ )
{
const short bytecode = vecsAudio[i] & 0xF;
Q_ASSERT ( iStepindDec < IMA_STEP_SIZE_TAB_LEN );
short step = ima_step_size[iStepindDec];
int current = vecsAudio[i - 1];
int diff = step >> 3;
if ( bytecode & 1 )
{
diff += step >> 2;
}
if ( bytecode & 2 )
{
diff += step >> 1;
}
if ( bytecode & 4 )
{
diff += step;
}
if ( bytecode & 8 )
{
diff = -diff;
}
current += diff;
Q_ASSERT ( bytecode < IMA_INDX_ADJUST_TAB_LEN );
iStepindDec += ima_indx_adjust[bytecode];
// check that values do not exceed the bounds
current = CheckBounds ( current, _MINSHORT, _MAXSHORT );
iStepindDec = CheckBounds ( iStepindDec, 0, IMA_STEP_SIZE_TAB_LEN - 1 );
vecsAudio[i] = current;
}
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;
}