//==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) 1992-1999 Microsoft Corporation // //--------------------------------------------------------------------------; // // imaadpcm.c // // Description: // This file contains encode and decode routines for the IMA's ADPCM // format. This format is the same format used in Intel's DVI standard. // Intel has made this algorithm public domain and the IMA has endorsed // this format as a standard for audio compression. // // Implementation notes: // // A previous distribution of this codec used a data format which did // not comply with the IMA standard. For stereo files, the interleaving // of left and right samples was incorrect: the IMA standard requires // that a DWORD of left-channel data be followed by a DWORD of right- // channel data, but the previous implementation of this codec // interleaved the data at the byte level, with the 4 LSBs being the // left channel data and the 4 MSBs being the right channel data. // For mono files, each pair of samples was reversed: the first sample // was stored in the 4 MSBs rather than the 4 LSBs. This problem is // fixed during the current release. Note: files compressed by the // old codec will sound distorted when played back with the new codec, // and vice versa. Please recompress these files with the new codec, // since they do not conform to the standard and will not be reproduced // correctly by hardware codecs, etc. // // A previous distribution of this codec had an implementation problem // which degraded the sound quality of the encoding. This was due to // the fact that the step index was not properly maintained between // conversions. This problem has been fixed in the current release. // // The codec has been speeded up considerably by breaking // the encode and decode routines into four separate routines each: // mono 8-bit, mono 16-bit, stereo 8-bit, and stereo 16-bit. This // approach is recommended for real-time conversion routines. // //==========================================================================; #include #include #include #include #include #include #include "codec.h" #include "imaadpcm.h" #include "debug.h" // // This array is used by imaadpcmNextStepIndex to determine the next step // index to use. The step index is an index to the step[] array, below. // const short next_step[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 step[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 }; #ifndef INLINE #define INLINE __inline #endif //--------------------------------------------------------------------------; // // DWORD pcmM08BytesToSamples // DWORD pcmM16BytesToSamples // DWORD pcmS08BytesToSamples // DWORD pcmS16BytesToSamples // // Description: // These functions return the number of samples in a buffer of PCM // of the specified format. For efficiency, it is declared INLINE. // Note that, depending on the optimization flags, it may not // actually be implemented as INLINE. Optimizing for speed (-Oxwt) // will generally obey the INLINE specification. // // Arguments: // DWORD cb: The length of the buffer, in bytes. // // Return (DWORD): The length of the buffer in samples. // //--------------------------------------------------------------------------; INLINE DWORD pcmM08BytesToSamples( DWORD cb ) { return cb; } INLINE DWORD pcmM16BytesToSamples( DWORD cb ) { return cb / ((DWORD)2); } INLINE DWORD pcmS08BytesToSamples( DWORD cb ) { return cb / ((DWORD)2); } INLINE DWORD pcmS16BytesToSamples( DWORD cb ) { return cb / ((DWORD)4); } #ifdef WIN32 // // This code assumes that the integer nPredictedSample is 32-bits wide!!! // // The following define replaces the pair of calls to the inline functions // imaadpcmSampleEncode() and imaadpcmSampleDecode which are called in the // encode routines. There is some redundancy between them which is exploited // in this define. Because there are two returns (nEncodedSample and // nPredictedSample), it is more efficient to use a #define rather than an // inline function which would require a pointer to one of the returns. // // Basically, nPredictedSample is calculated based on the lDifference value // already there, rather than regenerating it through imaadpcmSampleDecode(). // #define imaadpcmFastEncode(nEncodedSample,nPredictedSample,nInputSample,nStepSize) \ { \ LONG lDifference; \ \ 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; \ } #else //--------------------------------------------------------------------------; // // int imaadpcmSampleEncode // // Description: // This routine encodes a single ADPCM sample. For efficiency, it is // declared INLINE. Note that, depending on the optimization flags, // it may not actually be implemented as INLINE. Optimizing for speed // (-Oxwt) will generally obey the INLINE specification. // // Arguments: // int nInputSample: The sample to be encoded. // int nPredictedSample: The predicted value of nInputSample. // int nStepSize: The quantization step size for the difference between // nInputSample and nPredictedSample. // // Return (int): The 4-bit ADPCM encoded sample, which corresponds to the // quantized difference value. // //--------------------------------------------------------------------------; INLINE int imaadpcmSampleEncode ( int nInputSample, int nPredictedSample, int nStepSize ) { LONG lDifference; // difference may require 17 bits! int nEncodedSample; // // set sign bit (bit 3 of the encoded sample) based on sign of the // difference (nInputSample-nPredictedSample). Note that we want the // absolute value of the difference for the subsequent quantization. // lDifference = nInputSample - nPredictedSample; nEncodedSample = 0; if( lDifference<0 ) { nEncodedSample = 8; lDifference = -lDifference; } // // quantize lDifference sample // if( lDifference >= nStepSize ) { // Bit 2. nEncodedSample |= 4; lDifference -= nStepSize; } nStepSize >>= 1; if( lDifference >= nStepSize ) { // Bit 1. nEncodedSample |= 2; lDifference -= nStepSize; } nStepSize >>= 1; if( lDifference >= nStepSize ) { // Bit 0. nEncodedSample |= 1; } return (nEncodedSample); } #endif //--------------------------------------------------------------------------; // // int imaadpcmSampleDecode // // Description: // This routine decodes a single ADPCM sample. For efficiency, it is // declared INLINE. Note that, depending on the optimization flags, // it may not actually be implemented as INLINE. Optimizing for speed // (-Oxwt) will generally obey the INLINE specification. // // Arguments: // int nEncodedSample: The sample to be decoded. // int nPredictedSample: The predicted value of the sample (in PCM). // int nStepSize: The quantization step size used to encode the sample. // // Return (int): The decoded PCM sample. // //--------------------------------------------------------------------------; INLINE int imaadpcmSampleDecode ( int nEncodedSample, int nPredictedSample, int nStepSize ) { LONG lDifference; LONG lNewSample; // // calculate difference: // // lDifference = (nEncodedSample + 1/2) * nStepSize / 4 // lDifference = nStepSize>>3; if (nEncodedSample & 4) lDifference += nStepSize; if (nEncodedSample & 2) lDifference += nStepSize>>1; if (nEncodedSample & 1) lDifference += nStepSize>>2; // // If the 'sign bit' of the encoded nibble is set, then the // difference is negative... // if (nEncodedSample & 8) lDifference = -lDifference; // // adjust predicted sample based on calculated difference // lNewSample = nPredictedSample + lDifference; // // check for overflow and clamp if necessary to a 16 signed sample. // Note that this is optimized for the most common case, when we // don't have to clamp. // if( (long)(short)lNewSample == lNewSample ) { return (int)lNewSample; } // // Clamp. // if( lNewSample < -32768 ) return (int)-32768; else return (int)32767; } //--------------------------------------------------------------------------; // // int imaadpcmNextStepIndex // // Description: // This routine calculates the step index value to use for the next // encode, based on the current value of the step index and the current // encoded sample. For efficiency, it is declared INLINE. Note that, // depending on the optimization flags, it may not actually be // implemented as INLINE. Optimizing for speed (-Oxwt) will generally // obey the INLINE specification. // // Arguments: // int nEncodedSample: The current encoded ADPCM sample. // int nStepIndex: The step index value used to encode nEncodedSample. // // Return (int): The step index to use for the next sample. // //--------------------------------------------------------------------------; INLINE int imaadpcmNextStepIndex ( int nEncodedSample, int nStepIndex ) { // // compute new stepsize step // nStepIndex += next_step[nEncodedSample]; if (nStepIndex < 0) nStepIndex = 0; else if (nStepIndex > 88) nStepIndex = 88; return (nStepIndex); } //--------------------------------------------------------------------------; // // BOOL imaadpcmValidStepIndex // // Description: // This routine checks the step index value to make sure that it is // within the legal range. // // Arguments: // // int nStepIndex: The step index value. // // Return (BOOL): TRUE if the step index is valid; FALSE otherwise. // //--------------------------------------------------------------------------; INLINE BOOL imaadpcmValidStepIndex ( int nStepIndex ) { if( nStepIndex >= 0 && nStepIndex <= 88 ) return TRUE; else return FALSE; } //==========================================================================; // // DECODE ROUTINES // //==========================================================================; //--------------------------------------------------------------------------; // // DWORD imaadpcmDecode4Bit_M08 // DWORD imaadpcmDecode4Bit_M16 // DWORD imaadpcmDecode4Bit_S08 // DWORD imaadpcmDecode4Bit_S16 // // Description: // These functions decode a buffer of data from ADPCM to PCM in the // specified format. The appropriate function is called once for each // ACMDM_STREAM_CONVERT message received. Note that since these // functions must share the same prototype as the encoding functions // (see acmdStreamOpen() and acmdStreamConvert() in codec.c for more // details), not all the parameters are used by these routines. // // Arguments: // HPBYTE pbSrc: Pointer to the source buffer (ADPCM data). // DWORD cbSrcLength: The length of the source buffer (in bytes). // HPBYTE pbDst: Pointer to the destination buffer (PCM data). Note // that it is assumed that the destination buffer is // large enough to hold all the encoded data; see // acmdStreamSize() in codec.c for more details. // UINT nBlockAlignment: The block alignment of the ADPCM data (in // bytes). // UINT cSamplesPerBlock: The number of samples in each ADPCM block; // not used for decoding. // int *pnStepIndexL: Pointer to the step index value (left channel) // in the STREAMINSTANCE structure; not used for // decoding. // int *pnStepIndexR: Pointer to the step index value (right channel) // in the STREAMINSTANCE structure; not used for // decoding. // // Return (DWORD): The number of bytes used in the destination buffer. // //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmDecode4Bit_M08 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; UINT cbHeader; UINT cbBlockLength; BYTE bSample; int nStepSize; int nEncSample; int nPredSample; int nStepIndex; pbDstStart = pbDst; cbHeader = IMAADPCM_HEADER_LENGTH * 1; // 1 = number of channels. DPF(3,"Starting imaadpcmDecode4Bit_M08()."); // // // while (cbSrcLength >= cbHeader) { DWORD dwHeader; cbBlockLength = (UINT)min(cbSrcLength, nBlockAlignment); cbSrcLength -= cbBlockLength; cbBlockLength -= cbHeader; // // block header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSample = (int)(short)LOWORD(dwHeader); nStepIndex = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_M08: invalid step index."); return 0; } // // write out first sample // *pbDst++ = (BYTE)((nPredSample >> 8) + 128); // // // while (cbBlockLength--) { bSample = *pbSrc++; // // sample 1 // nEncSample = (bSample & (BYTE)0x0F); nStepSize = step[nStepIndex]; nPredSample = imaadpcmSampleDecode(nEncSample, nPredSample, nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample, nStepIndex); // // write out sample // *pbDst++ = (BYTE)((nPredSample >> 8) + 128); // // sample 2 // nEncSample = (bSample >> 4); nStepSize = step[nStepIndex]; nPredSample = imaadpcmSampleDecode(nEncSample, nPredSample, nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample, nStepIndex); // // write out sample // *pbDst++ = (BYTE)((nPredSample >> 8) + 128); } } // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmDecode4Bit_M08() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmDecode4Bit_M16 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; UINT cbHeader; UINT cbBlockLength; BYTE bSample; int nStepSize; int nEncSample; int nPredSample; int nStepIndex; pbDstStart = pbDst; cbHeader = IMAADPCM_HEADER_LENGTH * 1; // 1 = number of channels. DPF(3,"Starting imaadpcmDecode4Bit_M16()."); // // // while (cbSrcLength >= cbHeader) { DWORD dwHeader; cbBlockLength = (UINT)min(cbSrcLength, nBlockAlignment); cbSrcLength -= cbBlockLength; cbBlockLength -= cbHeader; // // block header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSample = (int)(short)LOWORD(dwHeader); nStepIndex = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_M16: invalid step index."); return 0; } // // write out first sample // *(short HUGE_T *)pbDst = (short)nPredSample; pbDst += sizeof(short); // // // while (cbBlockLength--) { bSample = *pbSrc++; // // sample 1 // nEncSample = (bSample & (BYTE)0x0F); nStepSize = step[nStepIndex]; nPredSample = imaadpcmSampleDecode(nEncSample, nPredSample, nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample, nStepIndex); // // write out sample // *(short HUGE_T *)pbDst = (short)nPredSample; pbDst += sizeof(short); // // sample 2 // nEncSample = (bSample >> 4); nStepSize = step[nStepIndex]; nPredSample = imaadpcmSampleDecode(nEncSample, nPredSample, nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample, nStepIndex); // // write out sample // *(short HUGE_T *)pbDst = (short)nPredSample; pbDst += sizeof(short); } } // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmDecode4Bit_M16() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmDecode4Bit_S08 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; UINT cbHeader; UINT cbBlockLength; int nStepSize; DWORD dwHeader; DWORD dwLeft; DWORD dwRight; int i; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; pbDstStart = pbDst; cbHeader = IMAADPCM_HEADER_LENGTH * 2; // 2 = number of channels. DPF(3,"Starting imaadpcmDecode4Bit_S08()."); // // // while( 0 != cbSrcLength ) { // // The data should always be block aligned. // ASSERT( cbSrcLength >= nBlockAlignment ); cbBlockLength = nBlockAlignment; cbSrcLength -= cbBlockLength; cbBlockLength -= cbHeader; // // LEFT channel header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSampleL = (int)(short)LOWORD(dwHeader); nStepIndexL = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_S08: invalid step index (L)."); return 0; } // // RIGHT channel header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSampleR = (int)(short)LOWORD(dwHeader); nStepIndexR = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_S08: invalid step index (R)."); return 0; } // // write out first sample // *pbDst++ = (BYTE)((nPredSampleL >> 8) + 128); *pbDst++ = (BYTE)((nPredSampleR >> 8) + 128); // // 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. // ASSERT( 0 == cbBlockLength%8 ); while( 0 != cbBlockLength ) { cbBlockLength -= 8; dwLeft = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); dwRight = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); for( i=8; i>0; i-- ) { // // LEFT channel // nEncSampleL = (dwLeft & 0x0F); nStepSize = step[nStepIndexL]; nPredSampleL = imaadpcmSampleDecode(nEncSampleL, nPredSampleL, nStepSize); nStepIndexL = imaadpcmNextStepIndex(nEncSampleL, nStepIndexL); // // RIGHT channel // nEncSampleR = (dwRight & 0x0F); nStepSize = step[nStepIndexR]; nPredSampleR = imaadpcmSampleDecode(nEncSampleR, nPredSampleR, nStepSize); nStepIndexR = imaadpcmNextStepIndex(nEncSampleR, nStepIndexR); // // write out sample // *pbDst++ = (BYTE)((nPredSampleL >> 8) + 128); *pbDst++ = (BYTE)((nPredSampleR >> 8) + 128); // // Shift the next input sample into the low-order 4 bits. // dwLeft >>= 4; dwRight >>= 4; } } } // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmDecode4Bit_S08() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmDecode4Bit_S16 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; UINT cbHeader; UINT cbBlockLength; int nStepSize; DWORD dwHeader; DWORD dwLeft; DWORD dwRight; int i; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; pbDstStart = pbDst; cbHeader = IMAADPCM_HEADER_LENGTH * 2; // 2 = number of channels. DPF(3,"Starting imaadpcmDecode4Bit_S16()."); // // // while( 0 != cbSrcLength ) { // // The data should always be block aligned. // ASSERT( cbSrcLength >= nBlockAlignment ); cbBlockLength = nBlockAlignment; cbSrcLength -= cbBlockLength; cbBlockLength -= cbHeader; // // LEFT channel header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSampleL = (int)(short)LOWORD(dwHeader); nStepIndexL = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_S16: invalid step index %u (L).", nStepIndexL); return 0; } // // RIGHT channel header // dwHeader = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); nPredSampleR = (int)(short)LOWORD(dwHeader); nStepIndexR = (int)(BYTE)HIWORD(dwHeader); if( !imaadpcmValidStepIndex(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. // DPF(1,"imaadpcmDecode4Bit_S16: invalid step index %u (R).",nStepIndexR); return 0; } // // write out first sample // *(DWORD HUGE_T *)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. // ASSERT( 0 == cbBlockLength%8 ); while( 0 != cbBlockLength ) { cbBlockLength -= 8; dwLeft = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); dwRight = *(DWORD HUGE_T *)pbSrc; pbSrc += sizeof(DWORD); for( i=8; i>0; i-- ) { // // LEFT channel // nEncSampleL = (dwLeft & 0x0F); nStepSize = step[nStepIndexL]; nPredSampleL = imaadpcmSampleDecode(nEncSampleL, nPredSampleL, nStepSize); nStepIndexL = imaadpcmNextStepIndex(nEncSampleL, nStepIndexL); // // RIGHT channel // nEncSampleR = (dwRight & 0x0F); nStepSize = step[nStepIndexR]; nPredSampleR = imaadpcmSampleDecode(nEncSampleR, nPredSampleR, nStepSize); nStepIndexR = imaadpcmNextStepIndex(nEncSampleR, nStepIndexR); // // write out sample // *(DWORD HUGE_T *)pbDst = MAKELONG(nPredSampleL, nPredSampleR); pbDst += sizeof(DWORD); // // Shift the next input sample into the low-order 4 bits. // dwLeft >>= 4; dwRight >>= 4; } } } // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmDecode4Bit_S16() //==========================================================================; // // ENCODE ROUTINES // //==========================================================================; //--------------------------------------------------------------------------; // // DWORD imaadpcmEncode4Bit_M08 // DWORD imaadpcmEncode4Bit_M16 // DWORD imaadpcmEncode4Bit_S08 // DWORD imaadpcmEncode4Bit_S16 // // Description: // These functions encode a buffer of data from PCM to ADPCM in the // specified format. The appropriate function is called once for each // ACMDM_STREAM_CONVERT message received. Note that since these // functions must share the same prototype as the decoding functions // (see acmdStreamOpen() and acmdStreamConvert() in codec.c for more // details), not all the parameters are used by these routines. // // Arguments: // HPBYTE pbSrc: Pointer to the source buffer (PCM data). // DWORD cbSrcLength: The length of the source buffer (in bytes). // HPBYTE pbDst: Pointer to the destination buffer (ADPCM data). Note // that it is assumed that the destination buffer is // large enough to hold all the encoded data; see // acmdStreamSize() in codec.c for more details. // UINT nBlockAlignment: The block alignment of the ADPCM data (in // bytes); not used for encoding. // UINT cSamplesPerBlock: The number of samples in each ADPCM block. // int *pnStepIndexL: Pointer to the step index value (left channel) // in the STREAMINSTANCE structure; this is used to // maintain the step index across converts. // int *pnStepIndexR: Pointer to the step index value (right channel) // in the STREAMINSTANCE structure; this is used to // maintain the step index across converts. It is only // used for stereo converts. // // Return (DWORD): The number of bytes used in the destination buffer. // //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmEncode4Bit_M08 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; DWORD cSrcSamples; UINT cBlockSamples; int nSample; int nStepSize; int nEncSample1; int nEncSample2; int nPredSample; int nStepIndex; pbDstStart = pbDst; cSrcSamples = pcmM08BytesToSamples(cbSrcLength); // // Restore the Step Index to that of the final convert of the previous // buffer. Remember to restore this value to psi->nStepIndexL. // nStepIndex = (*pnStepIndexL); // // // // while (0 != cSrcSamples) { cBlockSamples = (UINT)min(cSrcSamples, cSamplesPerBlock); cSrcSamples -= cBlockSamples; // // block header // nPredSample = ((short)*pbSrc++ - 128) << 8; cBlockSamples--; *(LONG HUGE_T *)pbDst = MAKELONG(nPredSample, nStepIndex); pbDst += 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( cBlockSamples>0 ) { // // sample 1 // nSample = ((short)*pbSrc++ - 128) << 8; cBlockSamples--; nStepSize = step[nStepIndex]; imaadpcmFastEncode(nEncSample1,nPredSample,nSample,nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample1, nStepIndex); // // sample 2 // nEncSample2 = 0; if( cBlockSamples>0 ) { nSample = ((short)*pbSrc++ - 128) << 8; cBlockSamples--; nStepSize = step[nStepIndex]; imaadpcmFastEncode(nEncSample2,nPredSample,nSample,nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample2, nStepIndex); } // // Write out encoded byte. // *pbDst++ = (BYTE)(nEncSample1 | (nEncSample2 << 4)); } } // // Restore the value of the Step Index, to be used on the next buffer. // (*pnStepIndexL) = nStepIndex; // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmEncode4Bit_M08() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmEncode4Bit_M16 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; DWORD cSrcSamples; UINT cBlockSamples; int nSample; int nStepSize; int nEncSample1; int nEncSample2; int nPredSample; int nStepIndex; pbDstStart = pbDst; cSrcSamples = pcmM16BytesToSamples(cbSrcLength); // // Restore the Step Index to that of the final convert of the previous // buffer. Remember to restore this value to psi->nStepIndexL. // nStepIndex = (*pnStepIndexL); // // // // while (0 != cSrcSamples) { cBlockSamples = (UINT)min(cSrcSamples, cSamplesPerBlock); cSrcSamples -= cBlockSamples; // // block header // nPredSample = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); cBlockSamples--; *(LONG HUGE_T *)pbDst = MAKELONG(nPredSample, nStepIndex); pbDst += 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( cBlockSamples>0 ) { // // sample 1 // nSample = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); cBlockSamples--; nStepSize = step[nStepIndex]; imaadpcmFastEncode(nEncSample1,nPredSample,nSample,nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample1, nStepIndex); // // sample 2 // nEncSample2 = 0; if( cBlockSamples>0 ) { nSample = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); cBlockSamples--; nStepSize = step[nStepIndex]; imaadpcmFastEncode(nEncSample2,nPredSample,nSample,nStepSize); nStepIndex = imaadpcmNextStepIndex(nEncSample2, nStepIndex); } // // Write out encoded byte. // *pbDst++ = (BYTE)(nEncSample1 | (nEncSample2 << 4)); } } // // Restore the value of the Step Index, to be used on the next buffer. // (*pnStepIndexL) = nStepIndex; // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmEncode4Bit_M16() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmEncode4Bit_S08 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; DWORD cSrcSamples; UINT cBlockSamples; int nSample; int nStepSize; DWORD dwLeft; DWORD dwRight; int i; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; pbDstStart = pbDst; cSrcSamples = pcmS08BytesToSamples(cbSrcLength); // // Restore the Step Index to that of the final convert of the previous // buffer. Remember to restore this value to psi->nStepIndexL,R. // nStepIndexL = (*pnStepIndexL); nStepIndexR = (*pnStepIndexR); // // // // while( 0 != cSrcSamples ) { // // The samples should always be block aligned. // ASSERT( cSrcSamples >= cSamplesPerBlock ); cBlockSamples = cSamplesPerBlock; cSrcSamples -= cBlockSamples; // // LEFT channel block header // nPredSampleL = ((short)*pbSrc++ - 128) << 8; *(LONG HUGE_T *)pbDst = MAKELONG(nPredSampleL, nStepIndexL); pbDst += sizeof(LONG); // // RIGHT channel block header // nPredSampleR = ((short)*pbSrc++ - 128) << 8; *(LONG HUGE_T *)pbDst = MAKELONG(nPredSampleR, nStepIndexR); pbDst += sizeof(LONG); cBlockSamples--; // One sample is in the header. // // 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. // ASSERT( 0 == cBlockSamples%8 ); while( 0 != cBlockSamples ) { cBlockSamples -= 8; dwLeft = 0; dwRight = 0; for( i=0; i<8; i++ ) { // // LEFT channel // nSample = ((short)*pbSrc++ - 128) << 8; nStepSize = step[nStepIndexL]; imaadpcmFastEncode(nEncSampleL,nPredSampleL,nSample,nStepSize); nStepIndexL = imaadpcmNextStepIndex(nEncSampleL, nStepIndexL); dwLeft |= ((DWORD)nEncSampleL) << 4*i; // // RIGHT channel // nSample = ((short)*pbSrc++ - 128) << 8; nStepSize = step[nStepIndexR]; imaadpcmFastEncode(nEncSampleR,nPredSampleR,nSample,nStepSize); nStepIndexR = imaadpcmNextStepIndex(nEncSampleR, nStepIndexR); dwRight |= ((DWORD)nEncSampleR) << 4*i; } // // Write out encoded DWORDs. // *(DWORD HUGE_T *)pbDst = dwLeft; pbDst += sizeof(DWORD); *(DWORD HUGE_T *)pbDst = dwRight; pbDst += sizeof(DWORD); } } // // Restore the value of the Step Index, to be used on the next buffer. // (*pnStepIndexL) = nStepIndexL; (*pnStepIndexR) = nStepIndexR; // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmEncode4Bit_S08() //--------------------------------------------------------------------------; //--------------------------------------------------------------------------; DWORD FNGLOBAL imaadpcmEncode4Bit_S16 ( HPBYTE pbSrc, DWORD cbSrcLength, HPBYTE pbDst, UINT nBlockAlignment, UINT cSamplesPerBlock, int * pnStepIndexL, int * pnStepIndexR ) { HPBYTE pbDstStart; DWORD cSrcSamples; UINT cBlockSamples; int nSample; int nStepSize; DWORD dwLeft; DWORD dwRight; int i; int nEncSampleL; int nPredSampleL; int nStepIndexL; int nEncSampleR; int nPredSampleR; int nStepIndexR; pbDstStart = pbDst; cSrcSamples = pcmS16BytesToSamples(cbSrcLength); // // Restore the Step Index to that of the final convert of the previous // buffer. Remember to restore this value to psi->nStepIndexL,R. // nStepIndexL = (*pnStepIndexL); nStepIndexR = (*pnStepIndexR); // // // // while( 0 != cSrcSamples ) { // // The samples should always be block aligned. // ASSERT( cSrcSamples >= cSamplesPerBlock ); cBlockSamples = cSamplesPerBlock; cSrcSamples -= cBlockSamples; // // LEFT channel block header // nPredSampleL = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); *(LONG HUGE_T *)pbDst = MAKELONG(nPredSampleL, nStepIndexL); pbDst += sizeof(LONG); // // RIGHT channel block header // nPredSampleR = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); *(LONG HUGE_T *)pbDst = MAKELONG(nPredSampleR, nStepIndexR); pbDst += sizeof(LONG); cBlockSamples--; // One sample is in the header. // // 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. // ASSERT( 0 == cBlockSamples%8 ); while( 0 != cBlockSamples ) { cBlockSamples -= 8; dwLeft = 0; dwRight = 0; for( i=0; i<8; i++ ) { // // LEFT channel // nSample = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); nStepSize = step[nStepIndexL]; imaadpcmFastEncode(nEncSampleL,nPredSampleL,nSample,nStepSize); nStepIndexL = imaadpcmNextStepIndex(nEncSampleL, nStepIndexL); dwLeft |= ((DWORD)nEncSampleL) << 4*i; // // RIGHT channel // nSample = *(short HUGE_T *)pbSrc; pbSrc += sizeof(short); nStepSize = step[nStepIndexR]; imaadpcmFastEncode(nEncSampleR,nPredSampleR,nSample,nStepSize); nStepIndexR = imaadpcmNextStepIndex(nEncSampleR, nStepIndexR); dwRight |= ((DWORD)nEncSampleR) << 4*i; } // // Write out encoded DWORDs. // *(DWORD HUGE_T *)pbDst = dwLeft; pbDst += sizeof(DWORD); *(DWORD HUGE_T *)pbDst = dwRight; pbDst += sizeof(DWORD); } } // // Restore the value of the Step Index, to be used on the next buffer. // (*pnStepIndexL) = nStepIndexL; (*pnStepIndexR) = nStepIndexR; // // We return the number of bytes used in the destination. This is // simply the difference in bytes from where we started. // return (DWORD)(pbDst - pbDstStart); } // imaadpcmEncode4Bit_S16()