diff options
author | Eugene Sandulenko | 2005-05-03 20:36:07 +0000 |
---|---|---|
committer | Eugene Sandulenko | 2005-05-03 20:36:07 +0000 |
commit | a71d60686c529cc4f0fbc19791bfd7b6420e46d7 (patch) | |
tree | 107c7f8d19f704d1db103dc115b53e175f8ecd9c /sound | |
parent | 6101cfd71b8b79146a7d1de5df5d354faa029c46 (diff) | |
download | scummvm-rg350-a71d60686c529cc4f0fbc19791bfd7b6420e46d7.tar.gz scummvm-rg350-a71d60686c529cc4f0fbc19791bfd7b6420e46d7.tar.bz2 scummvm-rg350-a71d60686c529cc4f0fbc19791bfd7b6420e46d7.zip |
Moved Oki ADPCM aka VOX decoder from SAGA engine to common sound/ directory.
Implemented IMA ADPCM decoder.
svn-id: r17903
Diffstat (limited to 'sound')
-rw-r--r-- | sound/adpcm.cpp | 215 | ||||
-rw-r--r-- | sound/adpcm.h | 37 | ||||
-rw-r--r-- | sound/module.mk | 1 |
3 files changed, 253 insertions, 0 deletions
diff --git a/sound/adpcm.cpp b/sound/adpcm.cpp new file mode 100644 index 0000000000..18f600ed23 --- /dev/null +++ b/sound/adpcm.cpp @@ -0,0 +1,215 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005 The ScummVM project + * + * 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. + * + * $Header$ + * + */ + +#include "stdafx.h" +#include "common/stream.h" + +#include "sound/audiostream.h" +#include "sound/adpcm.h" + + +// Routines to convert 12 bit linear samples to the +// Dialogic or Oki ADPCM coding format aka VOX +class ADPCMInputStream : public AudioStream { +private: + bool _evenPos; + Common::SeekableReadStream *_stream; + typesADPCM _type; + + struct adpcmStatus { + int16 last; + int16 stepIndex; + } _status; + + int16 stepAdjust(byte); + int16 okiADPCMDecode(byte); + int16 imaADPCMDecode(byte); + + int readOkiBuffer(int16 *buffer, const int numSamples); + int readIMABuffer(int16 *buffer, const int numSamples); + +public: + ADPCMInputStream(Common::SeekableReadStream *stream, typesADPCM type); + ~ADPCMInputStream() {}; + + int readBuffer(int16 *buffer, const int numSamples); + + bool endOfData() const { return _stream->eos(); } + bool isStereo() const { return false; } + int getRate() const { return 22050; } +}; + + +ADPCMInputStream::ADPCMInputStream(Common::SeekableReadStream *stream, typesADPCM type) + : _stream(stream), _evenPos(true), _type(type) { + + _status.last = 0; + _status.stepIndex = 0; +} + +int ADPCMInputStream::readBuffer(int16 *buffer, const int numSamples) { + switch (_type) { + case kADPCMOki: + return readOkiBuffer(buffer, numSamples); + break; + case kADPCMIma: + return readIMABuffer(buffer, numSamples); + break; + default: + error("Unsupported ADPCM encoding"); + break; + } + return 0; +} + +int ADPCMInputStream::readOkiBuffer(int16 *buffer, const int numSamples) { + int samples; + + for (samples = 0; samples < numSamples && !_stream->eos(); samples++) { + // * 16 effectively converts 12-bit input to 16-bit output + if (_evenPos) { + buffer[samples] = okiADPCMDecode((_stream->readByte() >> 4) & 0x0f) * 16; + // Rewind back so we will reget byte later + _stream->seek(-1, SEEK_CUR); + } else { + buffer[samples] = okiADPCMDecode(_stream->readByte() & 0x0f) * 16; + } + _evenPos = !_evenPos; + } + return samples; +} + +int ADPCMInputStream::readIMABuffer(int16 *buffer, const int numSamples) { + int samples; + + for (samples = 0; samples < numSamples && !_stream->eos(); samples++) { + if (_evenPos) { + buffer[samples] = imaADPCMDecode((_stream->readByte() >> 4) & 0x0f); + // Rewind back so we will reget byte later + _stream->seek(-1, SEEK_CUR); + } else { + buffer[samples] = imaADPCMDecode(_stream->readByte() & 0x0f); + } + _evenPos = !_evenPos; + } + return samples; +} + +// adjust the step for use on the next sample. +int16 ADPCMInputStream::stepAdjust(byte code) { + static const int16 adjusts[] = {-1, -1, -1, -1, 2, 4, 6, 8}; + + return adjusts[code & 0x07]; +} + +static const int16 okiStepSize[49] = { + 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, 1408, + 1552 +}; + +// Decode Linear to ADPCM +int16 ADPCMInputStream::okiADPCMDecode(byte code) { + int16 diff, E, SS, samp; + + SS = okiStepSize[_status.stepIndex]; + E = SS/8; + if (code & 0x01) + E += SS/4; + if (code & 0x02) + E += SS/2; + if (code & 0x04) + E += SS; + diff = (code & 0x08) ? -E : E; + samp = _status.last + diff; + + // Clip the values to +/- 2^11 (supposed to be 12 bits) + if(samp > 2048) + samp = 2048; + if(samp < -2048) + samp = -2048; + + _status.last = samp; + _status.stepIndex += stepAdjust(code); + if(_status.stepIndex < 0) + _status.stepIndex = 0; + if(_status.stepIndex > 48) + _status.stepIndex = 48; + + return samp; +} + +AudioStream *makeADPCMStream(Common::SeekableReadStream &stream, typesADPCM type) { + AudioStream *audioStream = new ADPCMInputStream(&stream, type); + + return audioStream; +} + + +static const uint16 imaStepTable[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 +}; + + +int16 ADPCMInputStream::imaADPCMDecode(byte code) { + int diff, E, SS, samp; + + SS = imaStepTable[_status.stepIndex]; + + E = SS/8; + if (code & 0x01) + E += SS/4; + if (code & 0x02) + E += SS/2; + if (code & 0x04) + E += SS; + diff = (code & 0x08) ? -E : E; + samp = _status.last + diff; + + if(samp < -0x8000) + samp = -0x8000; + else if(samp > 0x7fff) + samp = 0x7fff; + + _status.last = samp; + _status.stepIndex += stepAdjust(code); + if(_status.stepIndex < 0) + _status.stepIndex = 0; + if(_status.stepIndex > 88) + _status.stepIndex = 88; + + return samp; +} diff --git a/sound/adpcm.h b/sound/adpcm.h new file mode 100644 index 0000000000..e5c51f36aa --- /dev/null +++ b/sound/adpcm.h @@ -0,0 +1,37 @@ +/* ScummVM - Scumm Interpreter + * Copyright (C) 2005 The ScummVM project + * + * 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. + * + * $Header$ + * + */ + +#ifndef SOUND_ADPCM_H +#define SOUND_ADPCM_H + +#include "stdafx.h" +#include "common/scummsys.h" + +class AudioStream; + +enum typesADPCM { + kADPCMOki, + kADPCMIma +}; + +AudioStream *makeADPCMStream(Common::SeekableReadStream &stream, typesADPCM type); + +#endif diff --git a/sound/module.mk b/sound/module.mk index e57d145b55..5d0297bb11 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -1,6 +1,7 @@ MODULE := sound MODULE_OBJS := \ + sound/adpcm.o \ sound/audiocd.o \ sound/audiostream.o \ sound/flac.o \ |