/*****************************************************************************/ /* adpcm.cpp Copyright (c) Ladislav Zezula 2003 */ /*---------------------------------------------------------------------------*/ /* This module contains implementation of adpcm decompression method used by */ /* Storm.dll to decompress WAVE files. Thanks to Tom Amigo for releasing */ /* his sources. */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 11.03.03 1.00 Lad Splitted from Pkware.cpp */ /* 20.05.03 2.00 Lad Added compression */ /* 19.11.03 2.01 Dan Big endian handling */ /* 10.01.13 3.00 Lad Refactored, beautified, documented :-) */ /*****************************************************************************/ #include #include #include "adpcm.h" //----------------------------------------------------------------------------- // Tables necessary dor decompression static const int NextStepTable[] = { -1, 0, -1, 4, -1, 2, -1, 6, -1, 1, -1, 5, -1, 3, -1, 7, -1, 1, -1, 5, -1, 3, -1, 7, -1, 2, -1, 4, -1, 6, -1, 8 }; static const int StepSizeTable[] = { 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 }; //----------------------------------------------------------------------------- // Helper class for writing output ADPCM data class TADPCMStream { public: TADPCMStream(void * pvBuffer, size_t cbBuffer) { pbBufferEnd = (unsigned char *)pvBuffer + cbBuffer; pbBuffer = (unsigned char *)pvBuffer; } bool ReadByteSample(unsigned char & ByteSample) { // Check if there is enough space in the buffer if(pbBuffer >= pbBufferEnd) return false; ByteSample = *pbBuffer++; return true; } bool WriteByteSample(unsigned char ByteSample) { // Check if there is enough space in the buffer if(pbBuffer >= pbBufferEnd) return false; *pbBuffer++ = ByteSample; return true; } bool ReadWordSample(short & OneSample) { // Check if we have enough space in the output buffer if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short)) return false; // Write the sample OneSample = pbBuffer[0] + (((short)pbBuffer[1]) << 0x08); pbBuffer += sizeof(short); return true; } bool WriteWordSample(short OneSample) { // Check if we have enough space in the output buffer if((size_t)(pbBufferEnd - pbBuffer) < sizeof(short)) return false; // Write the sample *pbBuffer++ = (unsigned char)(OneSample & 0xFF); *pbBuffer++ = (unsigned char)(OneSample >> 0x08); return true; } int LengthProcessed(void * pvOutBuffer) { return (int)((unsigned char *)pbBuffer - (unsigned char *)pvOutBuffer); } unsigned char * pbBufferEnd; unsigned char * pbBuffer; }; //---------------------------------------------------------------------------- // Local functions static inline short GetNextStepIndex(int StepIndex, unsigned int EncodedSample) { // Get the next step index StepIndex = StepIndex + NextStepTable[EncodedSample & 0x1F]; // Don't make the step index overflow if(StepIndex < 0) StepIndex = 0; else if(StepIndex > 88) StepIndex = 88; return (short)StepIndex; } static inline int UpdatePredictedSample(int PredictedSample, int EncodedSample, int Difference, int BitMask = 0x40) { // Is the sign bit set? if(EncodedSample & BitMask) { PredictedSample -= Difference; if(PredictedSample <= -32768) PredictedSample = -32768; } else { PredictedSample += Difference; if(PredictedSample >= 32767) PredictedSample = 32767; } return PredictedSample; } static inline int DecodeSample(int PredictedSample, int EncodedSample, int StepSize, int Difference) { if(EncodedSample & 0x01) Difference += (StepSize >> 0); if(EncodedSample & 0x02) Difference += (StepSize >> 1); if(EncodedSample & 0x04) Difference += (StepSize >> 2); if(EncodedSample & 0x08) Difference += (StepSize >> 3); if(EncodedSample & 0x10) Difference += (StepSize >> 4); if(EncodedSample & 0x20) Difference += (StepSize >> 5); return UpdatePredictedSample(PredictedSample, EncodedSample, Difference); } //---------------------------------------------------------------------------- // Compression routine int CompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount, int CompressionLevel) { TADPCMStream os(pvOutBuffer, cbOutBuffer); // The output stream TADPCMStream is(pvInBuffer, cbInBuffer); // The input stream unsigned char BitShift = (unsigned char)(CompressionLevel - 1); short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT];// Predicted samples for each channel short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Step indexes for each channel short InputSample; // Input sample for the current channel int TotalStepSize; int ChannelIndex; int AbsDifference; int Difference; int MaxBitMask; int StepSize; // First byte in the output stream contains zero. The second one contains the compression level os.WriteByteSample(0); if(!os.WriteByteSample(BitShift)) return 2; // Set the initial step index for each channel PredictedSamples[0] = PredictedSamples[1] = 0; StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; // Next, InitialSample value for each channel follows for(int i = 0; i < ChannelCount; i++) { // Get the initial sample from the input stream if(!is.ReadWordSample(InputSample)) return os.LengthProcessed(pvOutBuffer); // Store the initial sample to our sample array PredictedSamples[i] = InputSample; // Also store the loaded sample to the output stream if(!os.WriteWordSample(InputSample)) return os.LengthProcessed(pvOutBuffer); } // Get the initial index ChannelIndex = ChannelCount - 1; // Now keep reading the input data as long as there is something in the input buffer while(is.ReadWordSample(InputSample)) { int EncodedSample = 0; // If we have two channels, we need to flip the channel index ChannelIndex = (ChannelIndex + 1) % ChannelCount; // Get the difference from the previous sample. // If the difference is negative, set the sign bit to the encoded sample AbsDifference = InputSample - PredictedSamples[ChannelIndex]; if(AbsDifference < 0) { AbsDifference = -AbsDifference; EncodedSample |= 0x40; } // If the difference is too low (higher that difference treshold), // write a step index modifier marker StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; if(AbsDifference < (StepSize >> CompressionLevel)) { if(StepIndexes[ChannelIndex] != 0) StepIndexes[ChannelIndex]--; os.WriteByteSample(0x80); } else { // If the difference is too high, write marker that // indicates increase in step size while(AbsDifference > (StepSize << 1)) { if(StepIndexes[ChannelIndex] >= 0x58) break; // Modify the step index StepIndexes[ChannelIndex] += 8; if(StepIndexes[ChannelIndex] > 0x58) StepIndexes[ChannelIndex] = 0x58; // Write the "modify step index" marker StepSize = StepSizeTable[StepIndexes[ChannelIndex]]; os.WriteByteSample(0x81); } // Get the limit bit value MaxBitMask = (1 << (BitShift - 1)); MaxBitMask = (MaxBitMask > 0x20) ? 0x20 : MaxBitMask; Difference = StepSize >> BitShift; TotalStepSize = 0; for(int BitVal = 0x01; BitVal <= MaxBitMask; BitVal <<= 1) { if((TotalStepSize + StepSize) <= AbsDifference) { TotalStepSize += StepSize; EncodedSample |= BitVal; } StepSize >>= 1; } PredictedSamples[ChannelIndex] = (short)UpdatePredictedSample(PredictedSamples[ChannelIndex], EncodedSample, Difference + TotalStepSize); // Write the encoded sample to the output stream if(!os.WriteByteSample((unsigned char)EncodedSample)) break; // Calculates the step index to use for the next encode StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndexes[ChannelIndex], EncodedSample); } } return os.LengthProcessed(pvOutBuffer); } //---------------------------------------------------------------------------- // Decompression routine int DecompressADPCM(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount) { TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream unsigned char EncodedSample; unsigned char BitShift; short PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; // Predicted sample for each channel short StepIndexes[MAX_ADPCM_CHANNEL_COUNT]; // Predicted step index for each channel int ChannelIndex; // Current channel index // Initialize the StepIndex for each channel PredictedSamples[0] = PredictedSamples[1] = 0; StepIndexes[0] = StepIndexes[1] = INITIAL_ADPCM_STEP_INDEX; // The first byte is always zero, the second one contains bit shift (compression level - 1) is.ReadByteSample(BitShift); is.ReadByteSample(BitShift); // Next, InitialSample value for each channel follows for(int i = 0; i < ChannelCount; i++) { // Get the initial sample from the input stream short InitialSample; // Attempt to read the initial sample if(!is.ReadWordSample(InitialSample)) return os.LengthProcessed(pvOutBuffer); // Store the initial sample to our sample array PredictedSamples[i] = InitialSample; // Also store the loaded sample to the output stream if(!os.WriteWordSample(InitialSample)) return os.LengthProcessed(pvOutBuffer); } // Get the initial index ChannelIndex = ChannelCount - 1; // Keep reading as long as there is something in the input buffer while(is.ReadByteSample(EncodedSample)) { // If we have two channels, we need to flip the channel index ChannelIndex = (ChannelIndex + 1) % ChannelCount; if(EncodedSample == 0x80) { if(StepIndexes[ChannelIndex] != 0) StepIndexes[ChannelIndex]--; if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) return os.LengthProcessed(pvOutBuffer); } else if(EncodedSample == 0x81) { // Modify the step index StepIndexes[ChannelIndex] += 8; if(StepIndexes[ChannelIndex] > 0x58) StepIndexes[ChannelIndex] = 0x58; // Next pass, keep going on the same channel ChannelIndex = (ChannelIndex + 1) % ChannelCount; } else { int StepIndex = StepIndexes[ChannelIndex]; int StepSize = StepSizeTable[StepIndex]; // Encode one sample PredictedSamples[ChannelIndex] = (short)DecodeSample(PredictedSamples[ChannelIndex], EncodedSample, StepSize, StepSize >> BitShift); // Write the decoded sample to the output stream if(!os.WriteWordSample(PredictedSamples[ChannelIndex])) break; // Calculates the step index to use for the next encode StepIndexes[ChannelIndex] = GetNextStepIndex(StepIndex, EncodedSample); } } // Return total bytes written since beginning of the output buffer return os.LengthProcessed(pvOutBuffer); } //----------------------------------------------------------------------------- // ADPCM decompression present in Starcraft I BETA typedef struct _ADPCM_DATA { const unsigned int * pValues; int BitCount; int field_8; int field_C; int field_10; } ADPCM_DATA, *PADPCM_DATA; static const unsigned int adpcm_values_2[] = {0x33, 0x66}; static const unsigned int adpcm_values_3[] = {0x3A, 0x3A, 0x50, 0x70}; static const unsigned int adpcm_values_4[] = {0x3A, 0x3A, 0x3A, 0x3A, 0x4D, 0x66, 0x80, 0x9A}; static const unsigned int adpcm_values_6[] = { 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x3A, 0x46, 0x53, 0x60, 0x6D, 0x7A, 0x86, 0x93, 0xA0, 0xAD, 0xBA, 0xC6, 0xD3, 0xE0, 0xED, 0xFA, 0x106 }; static const unsigned int * InitAdpcmData(PADPCM_DATA pData, unsigned char BitCount) { switch(BitCount) { case 2: pData->pValues = adpcm_values_2; break; case 3: pData->pValues = adpcm_values_3; break; case 4: pData->pValues = adpcm_values_4; break; default: pData->pValues = NULL; break; case 6: pData->pValues = adpcm_values_6; break; } pData->BitCount = BitCount; pData->field_C = 0x20000; pData->field_8 = 1 << BitCount; pData->field_10 = (1 << BitCount) / 2; return pData->pValues; } int DecompressADPCM_SC1B(void * pvOutBuffer, int cbOutBuffer, void * pvInBuffer, int cbInBuffer, int ChannelCount) { TADPCMStream os(pvOutBuffer, cbOutBuffer); // Output stream TADPCMStream is(pvInBuffer, cbInBuffer); // Input stream ADPCM_DATA AdpcmData; int LowBitValues[MAX_ADPCM_CHANNEL_COUNT]; int UpperBits[MAX_ADPCM_CHANNEL_COUNT]; int BitMasks[MAX_ADPCM_CHANNEL_COUNT]; int PredictedSamples[MAX_ADPCM_CHANNEL_COUNT]; int ChannelIndex; int ChannelIndexMax; int OutputSample; unsigned char BitCount; unsigned char EncodedSample; short InputValue16; int reg_eax; int Difference; // The first byte contains number of bits if(!is.ReadByteSample(BitCount)) return os.LengthProcessed(pvOutBuffer); if(!InitAdpcmData(&AdpcmData, BitCount)) return os.LengthProcessed(pvOutBuffer); assert(AdpcmData.pValues != NULL); // Init bit values for(int i = 0; i < ChannelCount; i++) { unsigned char OneByte; if(!is.ReadByteSample(OneByte)) return os.LengthProcessed(pvOutBuffer); LowBitValues[i] = OneByte & 0x01; UpperBits[i] = OneByte >> 1; } // for(int i = 0; i < ChannelCount; i++) { if(!is.ReadWordSample(InputValue16)) return os.LengthProcessed(pvOutBuffer); BitMasks[i] = InputValue16 << AdpcmData.BitCount; } // Next, InitialSample value for each channel follows for(int i = 0; i < ChannelCount; i++) { if(!is.ReadWordSample(InputValue16)) return os.LengthProcessed(pvOutBuffer); PredictedSamples[i] = InputValue16; os.WriteWordSample(InputValue16); } // Get the initial index ChannelIndexMax = ChannelCount - 1; ChannelIndex = 0; // Keep reading as long as there is something in the input buffer while(is.ReadByteSample(EncodedSample)) { reg_eax = ((PredictedSamples[ChannelIndex] * 3) << 3) - PredictedSamples[ChannelIndex]; PredictedSamples[ChannelIndex] = ((reg_eax * 10) + 0x80) >> 8; Difference = (((EncodedSample >> 1) + 1) * BitMasks[ChannelIndex] + AdpcmData.field_10) >> AdpcmData.BitCount; PredictedSamples[ChannelIndex] = UpdatePredictedSample(PredictedSamples[ChannelIndex], EncodedSample, Difference, 0x01); BitMasks[ChannelIndex] = (AdpcmData.pValues[EncodedSample >> 1] * BitMasks[ChannelIndex] + 0x80) >> 6; if(BitMasks[ChannelIndex] < AdpcmData.field_8) BitMasks[ChannelIndex] = AdpcmData.field_8; if(BitMasks[ChannelIndex] > AdpcmData.field_C) BitMasks[ChannelIndex] = AdpcmData.field_C; reg_eax = (cbInBuffer - is.LengthProcessed(pvInBuffer)) >> ChannelIndexMax; OutputSample = PredictedSamples[ChannelIndex]; if(reg_eax < UpperBits[ChannelIndex]) { if(LowBitValues[ChannelIndex]) { OutputSample += (UpperBits[ChannelIndex] - reg_eax); if(OutputSample > 32767) OutputSample = 32767; } else { OutputSample += (reg_eax - UpperBits[ChannelIndex]); if(OutputSample < -32768) OutputSample = -32768; } } // Write the word sample and swap channel os.WriteWordSample((short)(OutputSample)); ChannelIndex = (ChannelIndex + 1) % ChannelCount; } return os.LengthProcessed(pvOutBuffer); }