From 8606982590bb3c124cc4d17fbe15aa722803201e Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Wed, 27 Jan 2010 08:40:48 +0000 Subject: Moved the Shorten decoder inside the SAGA engine, as it's the only one using it (and it's still unfinished, too) svn-id: r47592 --- engines/saga/module.mk | 1 + engines/saga/shorten.cpp | 531 ++++++++++++++++++++++++++++++++++++++++++++ engines/saga/shorten.h | 63 ++++++ engines/saga/sndres.cpp | 8 +- sound/decoders/shorten.cpp | 533 --------------------------------------------- sound/decoders/shorten.h | 68 ------ sound/module.mk | 1 - 7 files changed, 599 insertions(+), 606 deletions(-) create mode 100644 engines/saga/shorten.cpp create mode 100644 engines/saga/shorten.h delete mode 100644 sound/decoders/shorten.cpp delete mode 100644 sound/decoders/shorten.h diff --git a/engines/saga/module.mk b/engines/saga/module.mk index c1c29d2a79..9c10fa5bda 100644 --- a/engines/saga/module.mk +++ b/engines/saga/module.mk @@ -29,6 +29,7 @@ MODULE_OBJS := \ scene.o \ script.o \ sfuncs.o \ + shorten.o \ sndres.o \ sound.o \ sprite.o \ diff --git a/engines/saga/shorten.cpp b/engines/saga/shorten.cpp new file mode 100644 index 0000000000..17f52fe924 --- /dev/null +++ b/engines/saga/shorten.cpp @@ -0,0 +1,531 @@ +/* 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 "saga/shorten.h" + +#ifdef SOUND_SHORTEN_H + +// Based on etree's Shorten tool, version 3.6.1 +// http://etree.org/shnutils/shorten/ + +// FIXME: This doesn't work yet correctly + +#include "common/endian.h" +#include "common/util.h" + +#include "sound/mixer.h" +#include "sound/decoders/raw.h" + +namespace Saga { + +#define MASKTABSIZE 33 +#define MAX_SUPPORTED_VERSION 3 +#define DEFAULT_BLOCK_SIZE 256 + +enum kShortenTypes { + kTypeAU1 = 0, // lossless ulaw + kTypeS8 = 1, // signed 8 bit + kTypeU8 = 2, // unsigned 8 bit + kTypeS16HL = 3, // signed 16 bit shorts: high-low + kTypeU16HL = 4, // unsigned 16 bit shorts: high-low + kTypeS16LH = 5, // signed 16 bit shorts: low-high + kTypeU16LH = 6, // unsigned 16 bit shorts: low-high + kTypeULaw = 7, // lossy ulaw + kTypeAU2 = 8, // new ulaw with zero mapping + kTypeAU3 = 9, // lossless alaw + kTypeALaw = 10, // lossy alaw + kTypeWAV = 11, // WAV + kTypeAIFF = 12, // AIFF + kTypeEOF = 13, + kTypeGenericULaw = 128, + kTypeGenericALaw = 129 +}; + +enum kShortenCommands { + kCmdDiff0 = 0, + kCmdDiff1 = 1, + kCmdDiff2 = 2, + kCmdDiff3 = 3, + kCmdQuit = 4, + kCmdBlockSize = 5, + kCmdBitShift = 6, + kCmdQLPC = 7, + kCmdZero = 8, + kCmdVerbatim = 9 +}; + +#ifndef M_LN2 +#define M_LN2 0.69314718055994530942 +#endif + +// --------------------------------------------------------------------------- + +class ShortenGolombReader { +public: + ShortenGolombReader(Common::ReadStream *stream, int version); + ~ShortenGolombReader() {} + uint32 getUint32(uint32 numBits); // UINT_GET + int32 getURice(uint32 numBits); // uvar_get + int32 getSRice(uint32 numBits); // var_get +private: + int _version; + uint32 _nbitget; + uint32 _buf; + uint32 _masktab[MASKTABSIZE]; + Common::ReadStream *_stream; +}; + +ShortenGolombReader::ShortenGolombReader(Common::ReadStream *stream, int version) { + _stream = stream; + _version = version; + uint32 val = 0; + _masktab[0] = 0; + _nbitget = 0; + _buf = 0; + + for (int i = 1; i < MASKTABSIZE; i++) { + val <<= 1; + val |= 1; + _masktab[i] = val; + } +} + +int32 ShortenGolombReader::getURice(uint32 numBits) { + int32 result = 0; + + if (!_nbitget) { + _buf = _stream->readUint32BE(); + _nbitget = 32; + } + + for (result = 0; !(_buf & (1L << --_nbitget)); result++) { + if (!_nbitget) { + _buf = _stream->readUint32BE(); + _nbitget = 32; + } + } + + while (numBits != 0) { + if (_nbitget >= numBits) { + result = (result << numBits) | ((_buf >> (_nbitget - numBits)) & _masktab[numBits]); + _nbitget -= numBits; + numBits = 0; + } else { + result = (result << _nbitget) | (_buf & _masktab[_nbitget]); + _buf = _stream->readUint32BE(); + numBits -= _nbitget; + _nbitget = 32; + } + } + + return result; +} + +int32 ShortenGolombReader::getSRice(uint32 numBits) { + uint32 uvar = (uint32) getURice(numBits + 1); + return (uvar & 1) ? (int32) ~(uvar >> 1) : (int32) (uvar >> 1); +} + +uint32 ShortenGolombReader::getUint32(uint32 numBits) { + return (_version == 0) ? (uint32)getURice(numBits) : (uint32)getURice(getURice(2)); +} + +// --------------------------------------------------------------------------- + +byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, byte &flags) { + int32 *buffer[2], *offset[2]; // up to 2 channels + byte *unpackedBuffer = 0; + byte *pBuf = unpackedBuffer; + int prevSize = 0; + int32 *lpc = 0; + + ShortenGolombReader *gReader; + uint32 i, j, version, mean, type, channels, blockSize; + uint32 maxLPC = 0, lpcqOffset = 0; + int32 bitShift = 0, wrap = 0; + flags = 0; + size = 0; + + // Read header + byte magic[4]; + stream.read(magic, 4); + if (memcmp(magic, "ajkg", 4) != 0) { + warning("loadShortenFromStream: No 'ajkg' header"); + return NULL; + } + + version = stream.readByte(); + + if (version > MAX_SUPPORTED_VERSION) { + warning("loadShortenFromStream: Can't decode version %d, maximum supported version is %d", version, MAX_SUPPORTED_VERSION); + return NULL; + } + + mean = (version < 2) ? 0 : 4; + + gReader = new ShortenGolombReader(&stream, version); + + // Get file type + type = gReader->getUint32(4); + + switch (type) { + case kTypeS8: + break; + case kTypeU8: + flags |= Audio::FLAG_UNSIGNED; + break; + case kTypeS16LH: + flags |= Audio::FLAG_LITTLE_ENDIAN; + case kTypeS16HL: + flags |= Audio::FLAG_16BITS; + break; + case kTypeU16LH: + flags |= Audio::FLAG_LITTLE_ENDIAN; + case kTypeU16HL: + flags |= Audio::FLAG_16BITS; + flags |= Audio::FLAG_UNSIGNED; + break; + case kTypeWAV: + // TODO: Perhaps implement this if we find WAV Shorten encoded files + warning("loadShortenFromStream: Type WAV is not supported"); + delete gReader; + return NULL; + case kTypeAIFF: + // TODO: Perhaps implement this if we find AIFF Shorten encoded files + warning("loadShortenFromStream: Type AIFF is not supported"); + delete gReader; + return NULL; + case kTypeAU1: + case kTypeAU2: + case kTypeAU3: + case kTypeULaw: + case kTypeALaw: + case kTypeEOF: + case kTypeGenericULaw: + case kTypeGenericALaw: + default: + warning("loadShortenFromStream: Type %d is not supported", type); + delete gReader; + return NULL; + } + + // Get channels + channels = gReader->getUint32(0); + if (channels != 1 && channels != 2) { + warning("loadShortenFromStream: Only 1 or 2 channels are supported, stream contains %d channels", channels); + delete gReader; + return NULL; + } + + // Get block size + if (version > 0) { + blockSize = gReader->getUint32((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2)); + maxLPC = gReader->getUint32(2); + mean = gReader->getUint32(0); + uint32 skipBytes = gReader->getUint32(1); + if (skipBytes > 0) { + prevSize = size; + size += skipBytes; + unpackedBuffer = (byte *) realloc(unpackedBuffer, size); + pBuf = unpackedBuffer + prevSize; + for (i = 0; i < skipBytes; i++) { + *pBuf++ = gReader->getUint32(7) & 0xFF; + } + } + } else { + blockSize = DEFAULT_BLOCK_SIZE; + } + + wrap = MAX(3, maxLPC); + + // Initialize buffers + for (i = 0; i < channels; i++) { + buffer[i] = (int32 *)malloc((blockSize + wrap) * 4); + offset[i] = (int32 *)malloc((MAX(1, mean)) * 4); + memset(buffer[i], 0, (blockSize + wrap) * 4); + memset(offset[i], 0, (MAX(1, mean)) * 4); + } + + if (maxLPC > 0) + lpc = (int32 *) malloc(maxLPC * 4); + + if (version > 1) + lpcqOffset = 1 << 5; + + // Init offset + int32 offsetMean = 0; + uint32 blocks = MAX(1, mean); + + if (type == kTypeU8) + offsetMean = 0x80; + else if (type == kTypeU16HL || type == kTypeU16LH) + offsetMean = 0x8000; + + for (uint32 channel = 0; channel < channels; channel++) + for (uint32 block = 0; block < blocks; block++) + offset[channel][block] = offsetMean; + + + uint32 curChannel = 0, cmd = 0; + + // Parse Shorten commands + while (true) { + cmd = gReader->getURice(2); + + if (cmd == kCmdQuit) + break; + + switch (cmd) { + case kCmdZero: + case kCmdDiff0: + case kCmdDiff1: + case kCmdDiff2: + case kCmdDiff3: + case kCmdQLPC: + { + int32 channelOffset = 0, energy = 0; + uint32 lpcNum = 0; + + if (cmd != kCmdZero) { + energy = gReader->getURice(3); + // hack for version 0 + if (version == 0) + energy--; + } + + // Find mean offset (code duplicated below) + if (mean == 0) { + channelOffset = offset[curChannel][0]; + } else { + int32 sum = (version < 2) ? 0 : mean / 2; + for (i = 0; i < mean; i++) + sum += offset[curChannel][i]; + + channelOffset = sum / mean; + + if (version >= 2 && bitShift > 0) + channelOffset = (channelOffset >> (bitShift - 1)) >> 1; + } + + // FIXME: The original code in this bit tries to modify memory outside of the array (negative indices) + // in cases kCmdDiff1, kCmdDiff2 and kCmdDiff3 + // I've removed those invalid writes, since they happen all the time (even when curChannel is 0) + switch (cmd) { + case kCmdZero: + for (i = 0; i < blockSize; i++) + buffer[curChannel][i] = 0; + break; + case kCmdDiff0: + for (i = 0; i < blockSize; i++) + buffer[curChannel][i] = gReader->getSRice(energy) + channelOffset; + break; + case kCmdDiff1: + gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) + for (i = 1; i < blockSize; i++) + buffer[curChannel][i] = gReader->getSRice(energy) + buffer[curChannel][i - 1]; + break; + case kCmdDiff2: + gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) + gReader->getSRice(energy); // i = 1 (to fix invalid table/memory access) + for (i = 2; i < blockSize; i++) + buffer[curChannel][i] = gReader->getSRice(energy) + 2 * buffer[curChannel][i - 1] - buffer[curChannel][i - 2]; + break; + case kCmdDiff3: + gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) + gReader->getSRice(energy); // i = 1 (to fix invalid table/memory access) + gReader->getSRice(energy); // i = 2 (to fix invalid table/memory access) + for (i = 3; i < blockSize; i++) + buffer[curChannel][i] = gReader->getSRice(energy) + 3 * (buffer[curChannel][i - 1] - buffer[curChannel][i - 2]) + buffer[curChannel][i - 3]; + break; + case kCmdQLPC: + lpcNum = gReader->getURice(2); + + // Safeguard: if maxLPC < lpcNum, realloc the lpc buffer + if (maxLPC < lpcNum) { + warning("Safeguard: maxLPC < lpcNum (should never happen)"); + maxLPC = lpcNum; + lpc = (int32 *) realloc(lpc, maxLPC * 4); + } + + for (i = 0; i < lpcNum; i++) + lpc[i] = gReader->getSRice(5); + + for (i = 0; i < lpcNum; i++) + buffer[curChannel][i - lpcNum] -= channelOffset; + + for (i = 0; i < blockSize; i++) { + int32 sum = lpcqOffset; + for (j = 0; j < lpcNum; j++) { + // FIXME: The original code did an invalid memory access here + // (if i and j are 0, the array index requested is -1) + // I've removed those invalid writes, since they happen all the time (even when curChannel is 0) + if (i <= j) // ignore invalid table/memory access + continue; + sum += lpc[j] * buffer[curChannel][i - j - 1]; + } + buffer[curChannel][i] = gReader->getSRice(energy) + (sum >> 5); + } + + if (channelOffset > 0) + for (i = 0; i < blockSize; i++) + buffer[curChannel][i] += channelOffset; + + break; + } + + // Store mean value, if appropriate (duplicated code from above) + if (mean > 0) { + int32 sum = (version < 2) ? 0 : blockSize / 2; + for (i = 0; i < blockSize; i++) + sum += buffer[curChannel][i]; + + for (i = 1; i < mean; i++) + offset[curChannel][i - 1] = offset[curChannel][i]; + + offset[curChannel][mean - 1] = sum / blockSize; + + if (version >= 2 && bitShift > 0) + offset[curChannel][mean - 1] = offset[curChannel][mean - 1] << bitShift; + } + + + // Do the wrap + // FIXME: removed for now, as this corrupts the heap, because it + // accesses negative array indices + //for (int32 k = -wrap; k < 0; k++) + // buffer[curChannel][k] = buffer[curChannel][k + blockSize]; + + // Fix bitshift + if (bitShift > 0) { + for (i = 0; i < blockSize; i++) + buffer[curChannel][i] <<= bitShift; + } + + if (curChannel == channels - 1) { + int dataSize = (flags & Audio::FLAG_16BITS) ? 2 : 1; + int limit = (flags & Audio::FLAG_16BITS) ? 32767 : 127; + limit = (flags & Audio::FLAG_UNSIGNED) ? limit * 2 + 1 : limit; + + prevSize = size; + size += (blockSize * dataSize); + unpackedBuffer = (byte *) realloc(unpackedBuffer, size); + pBuf = unpackedBuffer + prevSize; + + if (flags & Audio::FLAG_16BITS) { + for (i = 0; i < blockSize; i++) { + for (j = 0; j < channels; j++) { + int16 val = (int16)(MIN(buffer[j][i], limit) & 0xFFFF); + // values are written in LE + *pBuf++ = (byte) (val & 0xFF); + *pBuf++ = (byte) ((val >> 8) & 0xFF); + } + } + } else { + for (i = 0; i < blockSize; i++) + for (j = 0; j < channels; j++) + *pBuf++ = (byte)(MIN(buffer[j][i], limit) & 0xFF); + } + } + curChannel = (curChannel + 1) % channels; + + } + break; + case kCmdBlockSize: + blockSize = gReader->getUint32((uint32)log((double) blockSize / M_LN2)); + break; + case kCmdBitShift: + bitShift = gReader->getURice(2); + break; + case kCmdVerbatim: + { + + uint32 vLen = (uint32)gReader->getURice(5); + prevSize = size; + size += vLen; + unpackedBuffer = (byte *) realloc(unpackedBuffer, size); + pBuf = unpackedBuffer + prevSize; + + while (vLen--) { + *pBuf++ = (byte)(gReader->getURice(8) & 0xFF); + } + + } + break; + default: + warning("loadShortenFromStream: Unknown command: %d", cmd); + + // Cleanup + for (i = 0; i < channels; i++) { + free(buffer[i]); + free(offset[i]); + } + + if (maxLPC > 0) + free(lpc); + + if (size > 0) + free(unpackedBuffer); + + delete gReader; + return NULL; + break; + } + } + + // Rate is always 44100Hz + rate = 44100; + + // Cleanup + for (i = 0; i < channels; i++) { + free(buffer[i]); + free(offset[i]); + } + + if (maxLPC > 0) + free(lpc); + + if (size > 0) + free(unpackedBuffer); + + delete gReader; + return unpackedBuffer; +} + +Audio::AudioStream *makeShortenStream(Common::SeekableReadStream &stream) { + int size, rate; + byte *data, flags; + data = loadShortenFromStream(stream, size, rate, flags); + + if (!data) + return 0; + + // Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES. + return Audio::makeRawMemoryStream(data, size, rate, flags); +} + +} // End of namespace Audio + +#endif // defined(SOUND_SHORTEN_H) + diff --git a/engines/saga/shorten.h b/engines/saga/shorten.h new file mode 100644 index 0000000000..368d2ac985 --- /dev/null +++ b/engines/saga/shorten.h @@ -0,0 +1,63 @@ +/* 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$ + * + */ + +// The code in this file is currently only used in SAGA2. +// So when it is disabled, we will skip compiling it. +#if !(defined(ENABLE_SAGA2)) + +#else + +#ifndef SOUND_SHORTEN_H +#define SOUND_SHORTEN_H + +#include "common/scummsys.h" +#include "common/stream.h" + +#include "sound/audiostream.h" + +namespace Saga { + +/** + * Try to load a Shorten file from the given stream. Returns true if + * successful. In that case, the stream's seek position will be set to the + * start of the audio data, and size, rate and flags contain information + * necessary for playback. + */ +byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, byte &flags); + +/** + * Try to load a Shorten file from the given stream and create an AudioStream + * from that data. + * + * This function uses loadShortenFromStream() internally. + */ +Audio::AudioStream *makeShortenStream(Common::ReadStream &stream); + +} // End of namespace Audio + +#endif + +#endif // engine and dynamic plugins guard + diff --git a/engines/saga/sndres.cpp b/engines/saga/sndres.cpp index c4852f31c3..be65312963 100644 --- a/engines/saga/sndres.cpp +++ b/engines/saga/sndres.cpp @@ -37,12 +37,12 @@ #include "sound/audiostream.h" #include "sound/decoders/adpcm.h" #include "sound/decoders/aiff.h" -#ifdef ENABLE_SAGA2 -#include "sound/decoders/shorten.h" -#endif #include "sound/decoders/raw.h" #include "sound/decoders/voc.h" #include "sound/decoders/wave.h" +#ifdef ENABLE_SAGA2 +#include "saga/shorten.h" +#endif namespace Saga { @@ -323,7 +323,7 @@ bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buff result = Audio::loadAIFFFromStream(readS, size, rate, buffer.flags); #ifdef ENABLE_SAGA2 } else if (resourceType == kSoundShorten) { - result = Audio::loadShortenFromStream(readS, size, rate, buffer.flags); + result = loadShortenFromStream(readS, size, rate, buffer.flags); #endif } else if (resourceType == kSoundVOC) { data = Audio::loadVOCFromStream(readS, size, rate); diff --git a/sound/decoders/shorten.cpp b/sound/decoders/shorten.cpp deleted file mode 100644 index fc97f71b56..0000000000 --- a/sound/decoders/shorten.cpp +++ /dev/null @@ -1,533 +0,0 @@ -/* 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 "sound/decoders/shorten.h" - -#ifdef SOUND_SHORTEN_H - -// Based on etree's Shorten tool, version 3.6.1 -// http://etree.org/shnutils/shorten/ - -// FIXME: This doesn't work yet correctly - -#include "common/endian.h" -#include "common/util.h" -#include "common/stream.h" - -#include "sound/audiostream.h" -#include "sound/mixer.h" -#include "sound/decoders/raw.h" - -namespace Audio { - -#define MASKTABSIZE 33 -#define MAX_SUPPORTED_VERSION 3 -#define DEFAULT_BLOCK_SIZE 256 - -enum kShortenTypes { - kTypeAU1 = 0, // lossless ulaw - kTypeS8 = 1, // signed 8 bit - kTypeU8 = 2, // unsigned 8 bit - kTypeS16HL = 3, // signed 16 bit shorts: high-low - kTypeU16HL = 4, // unsigned 16 bit shorts: high-low - kTypeS16LH = 5, // signed 16 bit shorts: low-high - kTypeU16LH = 6, // unsigned 16 bit shorts: low-high - kTypeULaw = 7, // lossy ulaw - kTypeAU2 = 8, // new ulaw with zero mapping - kTypeAU3 = 9, // lossless alaw - kTypeALaw = 10, // lossy alaw - kTypeWAV = 11, // WAV - kTypeAIFF = 12, // AIFF - kTypeEOF = 13, - kTypeGenericULaw = 128, - kTypeGenericALaw = 129 -}; - -enum kShortenCommands { - kCmdDiff0 = 0, - kCmdDiff1 = 1, - kCmdDiff2 = 2, - kCmdDiff3 = 3, - kCmdQuit = 4, - kCmdBlockSize = 5, - kCmdBitShift = 6, - kCmdQLPC = 7, - kCmdZero = 8, - kCmdVerbatim = 9 -}; - -#ifndef M_LN2 -#define M_LN2 0.69314718055994530942 -#endif - -// --------------------------------------------------------------------------- - -class ShortenGolombReader { -public: - ShortenGolombReader(Common::ReadStream *stream, int version); - ~ShortenGolombReader() {} - uint32 getUint32(uint32 numBits); // UINT_GET - int32 getURice(uint32 numBits); // uvar_get - int32 getSRice(uint32 numBits); // var_get -private: - int _version; - uint32 _nbitget; - uint32 _buf; - uint32 _masktab[MASKTABSIZE]; - Common::ReadStream *_stream; -}; - -ShortenGolombReader::ShortenGolombReader(Common::ReadStream *stream, int version) { - _stream = stream; - _version = version; - uint32 val = 0; - _masktab[0] = 0; - _nbitget = 0; - _buf = 0; - - for (int i = 1; i < MASKTABSIZE; i++) { - val <<= 1; - val |= 1; - _masktab[i] = val; - } -} - -int32 ShortenGolombReader::getURice(uint32 numBits) { - int32 result = 0; - - if (!_nbitget) { - _buf = _stream->readUint32BE(); - _nbitget = 32; - } - - for (result = 0; !(_buf & (1L << --_nbitget)); result++) { - if (!_nbitget) { - _buf = _stream->readUint32BE(); - _nbitget = 32; - } - } - - while (numBits != 0) { - if (_nbitget >= numBits) { - result = (result << numBits) | ((_buf >> (_nbitget - numBits)) & _masktab[numBits]); - _nbitget -= numBits; - numBits = 0; - } else { - result = (result << _nbitget) | (_buf & _masktab[_nbitget]); - _buf = _stream->readUint32BE(); - numBits -= _nbitget; - _nbitget = 32; - } - } - - return result; -} - -int32 ShortenGolombReader::getSRice(uint32 numBits) { - uint32 uvar = (uint32) getURice(numBits + 1); - return (uvar & 1) ? (int32) ~(uvar >> 1) : (int32) (uvar >> 1); -} - -uint32 ShortenGolombReader::getUint32(uint32 numBits) { - return (_version == 0) ? (uint32)getURice(numBits) : (uint32)getURice(getURice(2)); -} - -// --------------------------------------------------------------------------- - -byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, byte &flags) { - int32 *buffer[2], *offset[2]; // up to 2 channels - byte *unpackedBuffer = 0; - byte *pBuf = unpackedBuffer; - int prevSize = 0; - int32 *lpc = 0; - - ShortenGolombReader *gReader; - uint32 i, j, version, mean, type, channels, blockSize; - uint32 maxLPC = 0, lpcqOffset = 0; - int32 bitShift = 0, wrap = 0; - flags = 0; - size = 0; - - // Read header - byte magic[4]; - stream.read(magic, 4); - if (memcmp(magic, "ajkg", 4) != 0) { - warning("loadShortenFromStream: No 'ajkg' header"); - return NULL; - } - - version = stream.readByte(); - - if (version > MAX_SUPPORTED_VERSION) { - warning("loadShortenFromStream: Can't decode version %d, maximum supported version is %d", version, MAX_SUPPORTED_VERSION); - return NULL; - } - - mean = (version < 2) ? 0 : 4; - - gReader = new ShortenGolombReader(&stream, version); - - // Get file type - type = gReader->getUint32(4); - - switch (type) { - case kTypeS8: - break; - case kTypeU8: - flags |= Audio::FLAG_UNSIGNED; - break; - case kTypeS16LH: - flags |= Audio::FLAG_LITTLE_ENDIAN; - case kTypeS16HL: - flags |= Audio::FLAG_16BITS; - break; - case kTypeU16LH: - flags |= Audio::FLAG_LITTLE_ENDIAN; - case kTypeU16HL: - flags |= Audio::FLAG_16BITS; - flags |= Audio::FLAG_UNSIGNED; - break; - case kTypeWAV: - // TODO: Perhaps implement this if we find WAV Shorten encoded files - warning("loadShortenFromStream: Type WAV is not supported"); - delete gReader; - return NULL; - case kTypeAIFF: - // TODO: Perhaps implement this if we find AIFF Shorten encoded files - warning("loadShortenFromStream: Type AIFF is not supported"); - delete gReader; - return NULL; - case kTypeAU1: - case kTypeAU2: - case kTypeAU3: - case kTypeULaw: - case kTypeALaw: - case kTypeEOF: - case kTypeGenericULaw: - case kTypeGenericALaw: - default: - warning("loadShortenFromStream: Type %d is not supported", type); - delete gReader; - return NULL; - } - - // Get channels - channels = gReader->getUint32(0); - if (channels != 1 && channels != 2) { - warning("loadShortenFromStream: Only 1 or 2 channels are supported, stream contains %d channels", channels); - delete gReader; - return NULL; - } - - // Get block size - if (version > 0) { - blockSize = gReader->getUint32((int) (log((double) DEFAULT_BLOCK_SIZE) / M_LN2)); - maxLPC = gReader->getUint32(2); - mean = gReader->getUint32(0); - uint32 skipBytes = gReader->getUint32(1); - if (skipBytes > 0) { - prevSize = size; - size += skipBytes; - unpackedBuffer = (byte *) realloc(unpackedBuffer, size); - pBuf = unpackedBuffer + prevSize; - for (i = 0; i < skipBytes; i++) { - *pBuf++ = gReader->getUint32(7) & 0xFF; - } - } - } else { - blockSize = DEFAULT_BLOCK_SIZE; - } - - wrap = MAX(3, maxLPC); - - // Initialize buffers - for (i = 0; i < channels; i++) { - buffer[i] = (int32 *)malloc((blockSize + wrap) * 4); - offset[i] = (int32 *)malloc((MAX(1, mean)) * 4); - memset(buffer[i], 0, (blockSize + wrap) * 4); - memset(offset[i], 0, (MAX(1, mean)) * 4); - } - - if (maxLPC > 0) - lpc = (int32 *) malloc(maxLPC * 4); - - if (version > 1) - lpcqOffset = 1 << 5; - - // Init offset - int32 offsetMean = 0; - uint32 blocks = MAX(1, mean); - - if (type == kTypeU8) - offsetMean = 0x80; - else if (type == kTypeU16HL || type == kTypeU16LH) - offsetMean = 0x8000; - - for (uint32 channel = 0; channel < channels; channel++) - for (uint32 block = 0; block < blocks; block++) - offset[channel][block] = offsetMean; - - - uint32 curChannel = 0, cmd = 0; - - // Parse Shorten commands - while (true) { - cmd = gReader->getURice(2); - - if (cmd == kCmdQuit) - break; - - switch (cmd) { - case kCmdZero: - case kCmdDiff0: - case kCmdDiff1: - case kCmdDiff2: - case kCmdDiff3: - case kCmdQLPC: - { - int32 channelOffset = 0, energy = 0; - uint32 lpcNum = 0; - - if (cmd != kCmdZero) { - energy = gReader->getURice(3); - // hack for version 0 - if (version == 0) - energy--; - } - - // Find mean offset (code duplicated below) - if (mean == 0) { - channelOffset = offset[curChannel][0]; - } else { - int32 sum = (version < 2) ? 0 : mean / 2; - for (i = 0; i < mean; i++) - sum += offset[curChannel][i]; - - channelOffset = sum / mean; - - if (version >= 2 && bitShift > 0) - channelOffset = (channelOffset >> (bitShift - 1)) >> 1; - } - - // FIXME: The original code in this bit tries to modify memory outside of the array (negative indices) - // in cases kCmdDiff1, kCmdDiff2 and kCmdDiff3 - // I've removed those invalid writes, since they happen all the time (even when curChannel is 0) - switch (cmd) { - case kCmdZero: - for (i = 0; i < blockSize; i++) - buffer[curChannel][i] = 0; - break; - case kCmdDiff0: - for (i = 0; i < blockSize; i++) - buffer[curChannel][i] = gReader->getSRice(energy) + channelOffset; - break; - case kCmdDiff1: - gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) - for (i = 1; i < blockSize; i++) - buffer[curChannel][i] = gReader->getSRice(energy) + buffer[curChannel][i - 1]; - break; - case kCmdDiff2: - gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) - gReader->getSRice(energy); // i = 1 (to fix invalid table/memory access) - for (i = 2; i < blockSize; i++) - buffer[curChannel][i] = gReader->getSRice(energy) + 2 * buffer[curChannel][i - 1] - buffer[curChannel][i - 2]; - break; - case kCmdDiff3: - gReader->getSRice(energy); // i = 0 (to fix invalid table/memory access) - gReader->getSRice(energy); // i = 1 (to fix invalid table/memory access) - gReader->getSRice(energy); // i = 2 (to fix invalid table/memory access) - for (i = 3; i < blockSize; i++) - buffer[curChannel][i] = gReader->getSRice(energy) + 3 * (buffer[curChannel][i - 1] - buffer[curChannel][i - 2]) + buffer[curChannel][i - 3]; - break; - case kCmdQLPC: - lpcNum = gReader->getURice(2); - - // Safeguard: if maxLPC < lpcNum, realloc the lpc buffer - if (maxLPC < lpcNum) { - warning("Safeguard: maxLPC < lpcNum (should never happen)"); - maxLPC = lpcNum; - lpc = (int32 *) realloc(lpc, maxLPC * 4); - } - - for (i = 0; i < lpcNum; i++) - lpc[i] = gReader->getSRice(5); - - for (i = 0; i < lpcNum; i++) - buffer[curChannel][i - lpcNum] -= channelOffset; - - for (i = 0; i < blockSize; i++) { - int32 sum = lpcqOffset; - for (j = 0; j < lpcNum; j++) { - // FIXME: The original code did an invalid memory access here - // (if i and j are 0, the array index requested is -1) - // I've removed those invalid writes, since they happen all the time (even when curChannel is 0) - if (i <= j) // ignore invalid table/memory access - continue; - sum += lpc[j] * buffer[curChannel][i - j - 1]; - } - buffer[curChannel][i] = gReader->getSRice(energy) + (sum >> 5); - } - - if (channelOffset > 0) - for (i = 0; i < blockSize; i++) - buffer[curChannel][i] += channelOffset; - - break; - } - - // Store mean value, if appropriate (duplicated code from above) - if (mean > 0) { - int32 sum = (version < 2) ? 0 : blockSize / 2; - for (i = 0; i < blockSize; i++) - sum += buffer[curChannel][i]; - - for (i = 1; i < mean; i++) - offset[curChannel][i - 1] = offset[curChannel][i]; - - offset[curChannel][mean - 1] = sum / blockSize; - - if (version >= 2 && bitShift > 0) - offset[curChannel][mean - 1] = offset[curChannel][mean - 1] << bitShift; - } - - - // Do the wrap - // FIXME: removed for now, as this corrupts the heap, because it - // accesses negative array indices - //for (int32 k = -wrap; k < 0; k++) - // buffer[curChannel][k] = buffer[curChannel][k + blockSize]; - - // Fix bitshift - if (bitShift > 0) { - for (i = 0; i < blockSize; i++) - buffer[curChannel][i] <<= bitShift; - } - - if (curChannel == channels - 1) { - int dataSize = (flags & Audio::FLAG_16BITS) ? 2 : 1; - int limit = (flags & Audio::FLAG_16BITS) ? 32767 : 127; - limit = (flags & Audio::FLAG_UNSIGNED) ? limit * 2 + 1 : limit; - - prevSize = size; - size += (blockSize * dataSize); - unpackedBuffer = (byte *) realloc(unpackedBuffer, size); - pBuf = unpackedBuffer + prevSize; - - if (flags & Audio::FLAG_16BITS) { - for (i = 0; i < blockSize; i++) { - for (j = 0; j < channels; j++) { - int16 val = (int16)(MIN(buffer[j][i], limit) & 0xFFFF); - // values are written in LE - *pBuf++ = (byte) (val & 0xFF); - *pBuf++ = (byte) ((val >> 8) & 0xFF); - } - } - } else { - for (i = 0; i < blockSize; i++) - for (j = 0; j < channels; j++) - *pBuf++ = (byte)(MIN(buffer[j][i], limit) & 0xFF); - } - } - curChannel = (curChannel + 1) % channels; - - } - break; - case kCmdBlockSize: - blockSize = gReader->getUint32((uint32)log((double) blockSize / M_LN2)); - break; - case kCmdBitShift: - bitShift = gReader->getURice(2); - break; - case kCmdVerbatim: - { - - uint32 vLen = (uint32)gReader->getURice(5); - prevSize = size; - size += vLen; - unpackedBuffer = (byte *) realloc(unpackedBuffer, size); - pBuf = unpackedBuffer + prevSize; - - while (vLen--) { - *pBuf++ = (byte)(gReader->getURice(8) & 0xFF); - } - - } - break; - default: - warning("loadShortenFromStream: Unknown command: %d", cmd); - - // Cleanup - for (i = 0; i < channels; i++) { - free(buffer[i]); - free(offset[i]); - } - - if (maxLPC > 0) - free(lpc); - - if (size > 0) - free(unpackedBuffer); - - delete gReader; - return NULL; - break; - } - } - - // Rate is always 44100Hz - rate = 44100; - - // Cleanup - for (i = 0; i < channels; i++) { - free(buffer[i]); - free(offset[i]); - } - - if (maxLPC > 0) - free(lpc); - - if (size > 0) - free(unpackedBuffer); - - delete gReader; - return unpackedBuffer; -} - -AudioStream *makeShortenStream(Common::SeekableReadStream &stream) { - int size, rate; - byte *data, flags; - data = loadShortenFromStream(stream, size, rate, flags); - - if (!data) - return 0; - - // Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES. - return makeRawMemoryStream(data, size, rate, flags); -} - -} // End of namespace Audio - -#endif // defined(SOUND_SHORTEN_H) - diff --git a/sound/decoders/shorten.h b/sound/decoders/shorten.h deleted file mode 100644 index bc9f229687..0000000000 --- a/sound/decoders/shorten.h +++ /dev/null @@ -1,68 +0,0 @@ -/* 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$ - * - */ - -// The code in this file is currently only used in SAGA2. -// So when it is disabled, we will skip compiling it. -// We also enable this code for ScummVM builds including support -// for dynamic engine plugins. -// If you plan to use this code in another engine, you will have -// to add the proper define check here. -#if !(defined(ENABLE_SAGA2) || defined(DYNAMIC_MODULES)) - -#else - -#ifndef SOUND_SHORTEN_H -#define SOUND_SHORTEN_H - -#include "common/scummsys.h" - -namespace Common { class ReadStream; } - -namespace Audio { - -class AudioStream; - -/** - * Try to load a Shorten file from the given stream. Returns true if - * successful. In that case, the stream's seek position will be set to the - * start of the audio data, and size, rate and flags contain information - * necessary for playback. - */ -byte *loadShortenFromStream(Common::ReadStream &stream, int &size, int &rate, byte &flags); - -/** - * Try to load a Shorten file from the given stream and create an AudioStream - * from that data. - * - * This function uses loadShortenFromStream() internally. - */ -AudioStream *makeShortenStream(Common::ReadStream &stream); - -} // End of namespace Audio - -#endif - -#endif // engine and dynamic plugins guard - diff --git a/sound/module.mk b/sound/module.mk index 1506d46c80..10cf090325 100644 --- a/sound/module.mk +++ b/sound/module.mk @@ -19,7 +19,6 @@ MODULE_OBJS := \ decoders/iff_sound.o \ decoders/mp3.o \ decoders/raw.o \ - decoders/shorten.o \ decoders/vag.o \ decoders/voc.o \ decoders/vorbis.o \ -- cgit v1.2.3