diff options
author | Max Horn | 2011-04-13 12:24:34 +0200 |
---|---|---|
committer | Max Horn | 2011-04-13 12:48:57 +0200 |
commit | 7607e351cc7b58efb90e14839bc4cd4af7e6371e (patch) | |
tree | 544e50720c951d52cb04f1c7b26b658a7b3e416d | |
parent | b9296a189e12c94e110ea15c490d9f46dcf4a802 (diff) | |
download | scummvm-rg350-7607e351cc7b58efb90e14839bc4cd4af7e6371e.tar.gz scummvm-rg350-7607e351cc7b58efb90e14839bc4cd4af7e6371e.tar.bz2 scummvm-rg350-7607e351cc7b58efb90e14839bc4cd4af7e6371e.zip |
TINSEL: Move custom ADPCM decoders to tinsel engine
-rw-r--r-- | audio/decoders/adpcm.cpp | 216 | ||||
-rw-r--r-- | audio/decoders/adpcm.h | 3 | ||||
-rw-r--r-- | engines/tinsel/adpcm.cpp | 168 | ||||
-rw-r--r-- | engines/tinsel/adpcm.h | 105 | ||||
-rw-r--r-- | engines/tinsel/module.mk | 1 | ||||
-rw-r--r-- | engines/tinsel/music.cpp | 5 | ||||
-rw-r--r-- | engines/tinsel/sound.cpp | 3 |
7 files changed, 279 insertions, 222 deletions
diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 1ade3dc0a1..2391bdcab7 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -337,219 +337,9 @@ int MS_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { } - #pragma mark - -class Tinsel_ADPCMStream : public ADPCMStream { -protected: - struct { - // Tinsel - double predictor; - double K0, K1; - double d0, d1; - } _status; - - void reset() { - ADPCMStream::reset(); - memset(&_status, 0, sizeof(_status)); - } - - int16 decodeTinsel(int16, double); - void readBufferTinselHeader(); - -public: - Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - - if (blockAlign == 0) - error("Tinsel_ADPCMStream(): blockAlign isn't specified"); - - if (channels != 1) - error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono"); - - memset(&_status, 0, sizeof(_status)); - } - -}; - -static const double TinselFilterTable[4][2] = { - {0, 0 }, - {0.9375, 0}, - {1.796875, -0.8125}, - {1.53125, -0.859375} -}; - -void Tinsel_ADPCMStream::readBufferTinselHeader() { - uint8 start = _stream->readByte(); - uint8 filterVal = (start & 0xC0) >> 6; - - if ((start & 0x20) != 0) { - //Lower 6 bit are negative - - // Negate - start = ~(start | 0xC0) + 1; - - _status.predictor = 1 << start; - } else { - // Lower 6 bit are positive - - // Truncate - start &= 0x1F; - - _status.predictor = ((double) 1.0) / (1 << start); - } - - _status.K0 = TinselFilterTable[filterVal][0]; - _status.K1 = TinselFilterTable[filterVal][1]; -} - -int16 Tinsel_ADPCMStream::decodeTinsel(int16 code, double eVal) { - double sample; - - sample = (double) code; - sample *= eVal * _status.predictor; - sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1); - - _status.d1 = _status.d0; - _status.d0 = sample; - - return (int16) CLIP<double>(sample, -32768.0, 32767.0); -} - -class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream { -public: - Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel4_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - uint16 data; - const double eVal = 1.142822265; - - samples = 0; - - assert(numSamples % 2 == 0); - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) { - // Read 1 byte = 8 bits = two 4 bit blocks - data = _stream->readByte(); - buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal); - buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal); - } - } - - return samples; -} - -class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream { -protected: - uint8 _chunkPos; - uint16 _chunkData; - - void reset() { - ADPCMStream::reset(); - _chunkPos = 0; - _chunkData = 0; - } - -public: - Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { - _chunkPos = 0; - _chunkData = 0; - } - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - const double eVal = 1.032226562; - - samples = 0; - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - _chunkPos = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) { - - switch (_chunkPos) { - case 0: - _chunkData = _stream->readByte(); - buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal); - break; - case 1: - _chunkData = (_chunkData << 8) | (_stream->readByte()); - buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal); - _blockPos[0]++; - break; - case 2: - _chunkData = (_chunkData << 8) | (_stream->readByte()); - buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal); - _blockPos[0]++; - break; - case 3: - _chunkData = (_chunkData << 8); - buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal); - _blockPos[0]++; - break; - } - - } - - } - - return samples; -} - -class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream { -public: - Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) - : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} - - virtual int readBuffer(int16 *buffer, const int numSamples); -}; - -int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { - int samples; - byte data; - const double eVal = 1.007843258; - - samples = 0; - - while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { - if (_blockPos[0] == _blockAlign) { - readBufferTinselHeader(); - _blockPos[0] = 0; - } - - for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) { - // Read 1 byte = 8 bits = one 8 bit block - data = _stream->readByte(); - buffer[samples] = decodeTinsel(data << 8, eVal); - } - } - - return samples; -} - - -#pragma mark - - #define DK3_READ_NIBBLE() \ do { \ if (_topNibble) { \ @@ -662,12 +452,6 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign, true); case kADPCMMS: return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel4: - return new Tinsel4_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel6: - return new Tinsel6_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); - case kADPCMTinsel8: - return new Tinsel8_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); case kADPCMIma: return new Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign); case kADPCMApple: diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h index 38ec870a27..a41da3ae77 100644 --- a/audio/decoders/adpcm.h +++ b/audio/decoders/adpcm.h @@ -56,9 +56,6 @@ enum typesADPCM { kADPCMMSIma, // Microsoft IMA ADPCM kADPCMMSImaLastExpress, // Microsoft IMA ADPCM (with inverted samples) kADPCMMS, // Microsoft ADPCM - kADPCMTinsel4, // 4-bit ADPCM used by the Tinsel engine - kADPCMTinsel6, // 6-bit ADPCM used by the Tinsel engine - kADPCMTinsel8, // 8-bit ADPCM used by the Tinsel engine kADPCMIma, // Standard IMA ADPCM kADPCMApple, // Apple QuickTime IMA ADPCM kADPCMDK3 // Duck DK3 IMA ADPCM diff --git a/engines/tinsel/adpcm.cpp b/engines/tinsel/adpcm.cpp new file mode 100644 index 0000000000..ec51d150dc --- /dev/null +++ b/engines/tinsel/adpcm.cpp @@ -0,0 +1,168 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#include "tinsel/adpcm.h" + +namespace Tinsel { + +static const double TinselFilterTable[4][2] = { + {0, 0 }, + {0.9375, 0}, + {1.796875, -0.8125}, + {1.53125, -0.859375} +}; + +void Tinsel_ADPCMStream::readBufferTinselHeader() { + uint8 start = _stream->readByte(); + uint8 filterVal = (start & 0xC0) >> 6; + + if ((start & 0x20) != 0) { + //Lower 6 bit are negative + + // Negate + start = ~(start | 0xC0) + 1; + + _status.predictor = 1 << start; + } else { + // Lower 6 bit are positive + + // Truncate + start &= 0x1F; + + _status.predictor = ((double) 1.0) / (1 << start); + } + + _status.K0 = TinselFilterTable[filterVal][0]; + _status.K1 = TinselFilterTable[filterVal][1]; +} + +int16 Tinsel_ADPCMStream::decodeTinsel(int16 code, double eVal) { + double sample; + + sample = (double) code; + sample *= eVal * _status.predictor; + sample += (_status.d0 * _status.K0) + (_status.d1 * _status.K1); + + _status.d1 = _status.d0; + _status.d0 = sample; + + return (int16) CLIP<double>(sample, -32768.0, 32767.0); +} + +int Tinsel4_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { + int samples; + uint16 data; + const double eVal = 1.142822265; + + samples = 0; + + assert(numSamples % 2 == 0); + + while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { + if (_blockPos[0] == _blockAlign) { + readBufferTinselHeader(); + _blockPos[0] = 0; + } + + for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2, _blockPos[0]++) { + // Read 1 byte = 8 bits = two 4 bit blocks + data = _stream->readByte(); + buffer[samples] = decodeTinsel((data << 8) & 0xF000, eVal); + buffer[samples+1] = decodeTinsel((data << 12) & 0xF000, eVal); + } + } + + return samples; +} + +int Tinsel6_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { + int samples; + const double eVal = 1.032226562; + + samples = 0; + + while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { + if (_blockPos[0] == _blockAlign) { + readBufferTinselHeader(); + _blockPos[0] = 0; + _chunkPos = 0; + } + + for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _chunkPos = (_chunkPos + 1) % 4) { + + switch (_chunkPos) { + case 0: + _chunkData = _stream->readByte(); + buffer[samples] = decodeTinsel((_chunkData << 8) & 0xFC00, eVal); + break; + case 1: + _chunkData = (_chunkData << 8) | (_stream->readByte()); + buffer[samples] = decodeTinsel((_chunkData << 6) & 0xFC00, eVal); + _blockPos[0]++; + break; + case 2: + _chunkData = (_chunkData << 8) | (_stream->readByte()); + buffer[samples] = decodeTinsel((_chunkData << 4) & 0xFC00, eVal); + _blockPos[0]++; + break; + case 3: + _chunkData = (_chunkData << 8); + buffer[samples] = decodeTinsel((_chunkData << 2) & 0xFC00, eVal); + _blockPos[0]++; + break; + } + + } + + } + + return samples; +} + +int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) { + int samples; + byte data; + const double eVal = 1.007843258; + + samples = 0; + + while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) { + if (_blockPos[0] == _blockAlign) { + readBufferTinselHeader(); + _blockPos[0] = 0; + } + + for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples++, _blockPos[0]++) { + // Read 1 byte = 8 bits = one 8 bit block + data = _stream->readByte(); + buffer[samples] = decodeTinsel(data << 8, eVal); + } + } + + return samples; +} + + +} // End of namespace Tinsel diff --git a/engines/tinsel/adpcm.h b/engines/tinsel/adpcm.h new file mode 100644 index 0000000000..79d537eef6 --- /dev/null +++ b/engines/tinsel/adpcm.h @@ -0,0 +1,105 @@ +/* ScummVM - Graphic Adventure Engine + * + * ScummVM is the legal property of its developers, whose names + * are too numerous to list here. Please refer to the COPYRIGHT + * file distributed with this source distribution. + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * $URL$ + * $Id$ + * + */ + +#ifndef TINSEL_ADPCM_H +#define TINSEL_ADPCM_H + +#include "audio/decoders/adpcm_intern.h" + +namespace Tinsel { + +class Tinsel_ADPCMStream : public Audio::ADPCMStream { +protected: + struct { + // Tinsel + double predictor; + double K0, K1; + double d0, d1; + } _status; + + void reset() { + ADPCMStream::reset(); + memset(&_status, 0, sizeof(_status)); + } + + int16 decodeTinsel(int16, double); + void readBufferTinselHeader(); + +public: + Tinsel_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + + if (blockAlign == 0) + error("Tinsel_ADPCMStream(): blockAlign isn't specified"); + + if (channels != 1) + error("Tinsel_ADPCMStream(): Tinsel ADPCM only supports mono"); + + memset(&_status, 0, sizeof(_status)); + } + +}; + +class Tinsel4_ADPCMStream : public Tinsel_ADPCMStream { +public: + Tinsel4_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + + virtual int readBuffer(int16 *buffer, const int numSamples); +}; + +class Tinsel6_ADPCMStream : public Tinsel_ADPCMStream { +protected: + uint8 _chunkPos; + uint16 _chunkData; + + void reset() { + ADPCMStream::reset(); + _chunkPos = 0; + _chunkData = 0; + } + +public: + Tinsel6_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) { + _chunkPos = 0; + _chunkData = 0; + } + + virtual int readBuffer(int16 *buffer, const int numSamples); +}; + +class Tinsel8_ADPCMStream : public Tinsel_ADPCMStream { +public: + Tinsel8_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign) + : Tinsel_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {} + + virtual int readBuffer(int16 *buffer, const int numSamples); +}; + + +} // End of namespace Tinsel + +#endif diff --git a/engines/tinsel/module.mk b/engines/tinsel/module.mk index 2778cec3df..2ab94b830a 100644 --- a/engines/tinsel/module.mk +++ b/engines/tinsel/module.mk @@ -2,6 +2,7 @@ MODULE := engines/tinsel MODULE_OBJS := \ actors.o \ + adpcm.o \ anim.o \ background.o \ bg.o \ diff --git a/engines/tinsel/music.cpp b/engines/tinsel/music.cpp index 49eacce9ae..12bcff829b 100644 --- a/engines/tinsel/music.cpp +++ b/engines/tinsel/music.cpp @@ -38,6 +38,7 @@ #include "common/file.h" #include "common/memstream.h" +#include "tinsel/adpcm.h" #include "tinsel/config.h" #include "tinsel/sound.h" #include "tinsel/music.h" @@ -785,8 +786,8 @@ bool PCMMusicPlayer::getNextChunk() { sampleStream = new Common::MemoryReadStream(buffer, sampleCLength, DisposeAfterUse::YES); delete _curChunk; - _curChunk = makeADPCMStream(sampleStream, DisposeAfterUse::YES, sampleCLength, - Audio::kADPCMTinsel8, 22050, 1, 32); + _curChunk = new Tinsel8_ADPCMStream(sampleStream, DisposeAfterUse::YES, sampleCLength, + 22050, 1, 32); _state = S_MID; return true; diff --git a/engines/tinsel/sound.cpp b/engines/tinsel/sound.cpp index a76d26eade..1fcdb4dcf9 100644 --- a/engines/tinsel/sound.cpp +++ b/engines/tinsel/sound.cpp @@ -26,6 +26,7 @@ #include "tinsel/sound.h" +#include "tinsel/adpcm.h" #include "tinsel/dw.h" #include "tinsel/config.h" #include "tinsel/music.h" @@ -319,7 +320,7 @@ bool SoundManager::playSample(int id, int sub, bool bLooped, int x, int y, int p #endif break; default: - sampleStream = Audio::makeADPCMStream(compressedStream, DisposeAfterUse::YES, sampleLen, Audio::kADPCMTinsel6, 22050, 1, 24); + sampleStream = new Tinsel6_ADPCMStream(compressedStream, DisposeAfterUse::YES, sampleLen, 22050, 1, 24); break; } |