diff options
Diffstat (limited to 'engines/zvision/sound')
-rw-r--r-- | engines/zvision/sound/zork_raw.cpp | 289 | ||||
-rw-r--r-- | engines/zvision/sound/zork_raw.h | 158 |
2 files changed, 447 insertions, 0 deletions
diff --git a/engines/zvision/sound/zork_raw.cpp b/engines/zvision/sound/zork_raw.cpp new file mode 100644 index 0000000000..a4d091f3c0 --- /dev/null +++ b/engines/zvision/sound/zork_raw.cpp @@ -0,0 +1,289 @@ +/* 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. + * + */ + +#include "common/scummsys.h" + +#include "zvision/zork_raw.h" + +#include "zvision/zvision.h" +#include "zvision/detection.h" +#include "zvision/utility.h" + +#include "common/file.h" +#include "common/str.h" +#include "common/stream.h" +#include "common/memstream.h" +#include "common/bufferedstream.h" +#include "common/util.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + + +namespace ZVision { + +const int16 RawChunkStream::_stepAdjustmentTable[8] = { -1, -1, -1, 1, 4, 7, 10, 12}; + +const int32 RawChunkStream::_amplitudeLookupTable[89] = {0x0007, 0x0008, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, + 0x0010, 0x0011, 0x0013, 0x0015, 0x0017, 0x0019, 0x001C, 0x001F, + 0x0022, 0x0025, 0x0029, 0x002D, 0x0032, 0x0037, 0x003C, 0x0042, + 0x0049, 0x0050, 0x0058, 0x0061, 0x006B, 0x0076, 0x0082, 0x008F, + 0x009D, 0x00AD, 0x00BE, 0x00D1, 0x00E6, 0x00FD, 0x0117, 0x0133, + 0x0151, 0x0173, 0x0198, 0x01C1, 0x01EE, 0x0220, 0x0256, 0x0292, + 0x02D4, 0x031C, 0x036C, 0x03C3, 0x0424, 0x048E, 0x0502, 0x0583, + 0x0610, 0x06AB, 0x0756, 0x0812, 0x08E0, 0x09C3, 0x0ABD, 0x0BD0, + 0x0CFF, 0x0E4C, 0x0FBA, 0x114C, 0x1307, 0x14EE, 0x1706, 0x1954, + 0x1BDC, 0x1EA5, 0x21B6, 0x2515, 0x28CA, 0x2CDF, 0x315B, 0x364B, + 0x3BB9, 0x41B2, 0x4844, 0x4F7E, 0x5771, 0x602F, 0x69CE, 0x7462, 0x7FFF + }; + +RawChunkStream::RawChunkStream(bool stereo) { + if (stereo) + _stereo = 1; + else + _stereo = 0; + + init(); +} + +void RawChunkStream::init() { + _lastSample[0].index = 0; + _lastSample[0].sample = 0; + _lastSample[1].index = 0; + _lastSample[1].sample = 0; +} + +RawChunkStream::RawChunk RawChunkStream::readNextChunk(Common::SeekableReadStream *stream) { + RawChunk tmp; + tmp.size = 0; + tmp.data = NULL; + + if (stream && (stream->size() == 0 || stream->eos())) + return tmp; + + tmp.size = (stream->size() - stream->pos()) * 2; + tmp.data = (int16 *)calloc(tmp.size, 1); + + readBuffer(tmp.data, stream, stream->size() - stream->pos()); + + return tmp; +} + +int RawChunkStream::readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples) { + int32 bytesRead = 0; + + // 0: Left, 1: Right + uint channel = 0; + + while (bytesRead < numSamples) { + byte encodedSample = stream->readByte(); + if (stream->eos()) { + return bytesRead; + } + bytesRead++; + + int16 index = _lastSample[channel].index; + uint32 lookUpSample = _amplitudeLookupTable[index]; + + int32 sample = 0; + + if (encodedSample & 0x40) + sample += lookUpSample; + if (encodedSample & 0x20) + sample += lookUpSample >> 1; + if (encodedSample & 0x10) + sample += lookUpSample >> 2; + if (encodedSample & 8) + sample += lookUpSample >> 3; + if (encodedSample & 4) + sample += lookUpSample >> 4; + if (encodedSample & 2) + sample += lookUpSample >> 5; + if (encodedSample & 1) + sample += lookUpSample >> 6; + if (encodedSample & 0x80) + sample = -sample; + + sample += _lastSample[channel].sample; + sample = CLIP<int32>(sample, -32768, 32767); + + buffer[bytesRead - 1] = (int16)sample; + + index += _stepAdjustmentTable[(encodedSample >> 4) & 7]; + index = CLIP<int16>(index, 0, 88); + + _lastSample[channel].sample = sample; + _lastSample[channel].index = index; + + // Increment and wrap the channel + channel = (channel + 1) & _stereo; + } + return bytesRead; +} + +const SoundParams RawZorkStream::_zNemSoundParamLookupTable[32] = {{'0', 0x1F40, false, false, false}, + {'1', 0x1F40, true, false, false}, + {'2', 0x1F40, false, false, true}, + {'3', 0x1F40, true, false, true}, + {'4', 0x2B11, false, false, false}, + {'5', 0x2B11, true, false, false}, + {'6', 0x2B11, false, false, true}, + {'7', 0x2B11, true, false, true}, + {'8', 0x5622, false, false, false}, + {'9', 0x5622, true, false, false}, + {'a', 0x5622, false, false, true}, + {'b', 0x5622, true, false, true}, + {'c', 0xAC44, false, false, false}, + {'d', 0xAC44, true, false, false}, + {'e', 0xAC44, false, false, true}, + {'f', 0xAC44, true, false, true}, + {'g', 0x1F40, false, true, false}, + {'h', 0x1F40, true, true, false}, + {'j', 0x1F40, false, true, true}, + {'k', 0x1F40, true, true, true}, + {'l', 0x2B11, false, true, false}, + {'m', 0x2B11, true, true, false}, + {'n', 0x2B11, false, true, true}, + {'p', 0x2B11, true, true, true}, + {'q', 0x5622, false, true, false}, + {'r', 0x5622, true, true, false}, + {'s', 0x5622, false, true, true}, + {'t', 0x5622, true, true, true}, + {'u', 0xAC44, false, true, false}, + {'v', 0xAC44, true, true, false}, + {'w', 0xAC44, false, true, true}, + {'x', 0xAC44, true, true, true} +}; + +const SoundParams RawZorkStream::_zgiSoundParamLookupTable[24] = {{'4', 0x2B11, false, false, false}, + {'5', 0x2B11, true, false, false}, + {'6', 0x2B11, false, false, true}, + {'7', 0x2B11, true, false, true}, + {'8', 0x5622, false, false, false}, + {'9', 0x5622, true, false, false}, + {'a', 0x5622, false, false, true}, + {'b', 0x5622, true, false, true}, + {'c', 0xAC44, false, false, false}, + {'d', 0xAC44, true, false, false}, + {'e', 0xAC44, false, false, true}, + {'f', 0xAC44, true, false, true}, + {'g', 0x2B11, false, true, false}, + {'h', 0x2B11, true, true, false}, + {'j', 0x2B11, false, true, true}, + {'k', 0x2B11, true, true, true}, + {'m', 0x5622, false, true, false}, + {'n', 0x5622, true, true, false}, + {'p', 0x5622, false, true, true}, + {'q', 0x5622, true, true, true}, + {'r', 0xAC44, false, true, false}, + {'s', 0xAC44, true, true, false}, + {'t', 0xAC44, false, true, true}, + {'u', 0xAC44, true, true, true} +}; + +RawZorkStream::RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream) + : _rate(rate), + _stereo(0), + _stream(stream, disposeStream), + _endOfData(false), + _streamReader(stereo) { + if (stereo) + _stereo = 1; + + // Calculate the total playtime of the stream + if (stereo) + _playtime = Audio::Timestamp(0, _stream->size() / 2, rate); + else + _playtime = Audio::Timestamp(0, _stream->size(), rate); +} + +int RawZorkStream::readBuffer(int16 *buffer, const int numSamples) { + + int32 bytesRead = _streamReader.readBuffer(buffer, _stream.get(), numSamples); + + if (_stream->eos()) + _endOfData = true; + + return bytesRead; +} + +bool RawZorkStream::rewind() { + _stream->seek(0, 0); + _stream->clearErr(); + _endOfData = false; + _streamReader.init(); + + return true; +} + +Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { + if (stereo) + assert(stream->size() % 2 == 0); + + return new RawZorkStream(rate, stereo, disposeAfterUse, stream); +} + +Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse) { + return makeRawZorkStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, stereo, DisposeAfterUse::YES); +} + +Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine) { + Common::File *file = new Common::File(); + assert(engine->getSearchManager()->openFile(*file, filePath)); + + Common::String fileName = getFileName(filePath); + fileName.toLowercase(); + + SoundParams soundParams; + + if (engine->getGameId() == GID_NEMESIS) { + for (int i = 0; i < 32; ++i) { + if (RawZorkStream::_zNemSoundParamLookupTable[i].identifier == (fileName[6])) + soundParams = RawZorkStream::_zNemSoundParamLookupTable[i]; + } + } else if (engine->getGameId() == GID_GRANDINQUISITOR) { + for (int i = 0; i < 24; ++i) { + if (RawZorkStream::_zgiSoundParamLookupTable[i].identifier == (fileName[7])) + soundParams = RawZorkStream::_zgiSoundParamLookupTable[i]; + } + } + + if (soundParams.packed) { + return makeRawZorkStream(wrapBufferedSeekableReadStream(file, 2048, DisposeAfterUse::YES), soundParams.rate, soundParams.stereo, DisposeAfterUse::YES); + } else { + byte flags = 0; + if (soundParams.bits16) + flags |= Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN; + if (soundParams.stereo) + flags |= Audio::FLAG_STEREO; + + return Audio::makeRawStream(file, soundParams.rate, flags, DisposeAfterUse::YES); + } +} + +} // End of namespace ZVision diff --git a/engines/zvision/sound/zork_raw.h b/engines/zvision/sound/zork_raw.h new file mode 100644 index 0000000000..a5e346dfbb --- /dev/null +++ b/engines/zvision/sound/zork_raw.h @@ -0,0 +1,158 @@ +/* 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. + * + */ + +#ifndef ZVISION_ZORK_RAW_H +#define ZVISION_ZORK_RAW_H + +#include "audio/audiostream.h" + + +namespace Common { +class SeekableReadStream; +} + +namespace ZVision { + +class ZVision; + +struct SoundParams { + char identifier; + uint32 rate; + bool stereo; + bool packed; + bool bits16; +}; + + +/** + * This is a ADPCM stream-reader, this class holds context for multi-chunk reading and no buffers. + */ +class RawChunkStream { +public: + RawChunkStream(bool stereo); + + ~RawChunkStream() { + } +private: + uint _stereo; + + /** + * Holds the frequency and index from the last sample + * 0 holds the left channel, 1 holds the right channel + */ + struct { + int32 sample; + int16 index; + } _lastSample[2]; + + static const int16 _stepAdjustmentTable[8]; + static const int32 _amplitudeLookupTable[89]; + +public: + + struct RawChunk { + int16 *data; + uint32 size; + }; + + void init(); + //Read next audio portion in new stream (needed for avi), return structure with buffer + RawChunk readNextChunk(Common::SeekableReadStream *stream); + //Read numSamples from stream to buffer + int readBuffer(int16 *buffer, Common::SeekableReadStream *stream, const int numSamples); +}; + +/** + * This is a stream, which allows for playing raw ADPCM data from a stream. + */ +class RawZorkStream : public Audio::RewindableAudioStream { +public: + RawZorkStream(uint32 rate, bool stereo, DisposeAfterUse::Flag disposeStream, Common::SeekableReadStream *stream); + + ~RawZorkStream() { + } + +public: + static const SoundParams _zNemSoundParamLookupTable[32]; + static const SoundParams _zgiSoundParamLookupTable[24]; + +private: + const int _rate; // Sample rate of stream + Audio::Timestamp _playtime; // Calculated total play time + Common::DisposablePtr<Common::SeekableReadStream> _stream; // Stream to read data from + bool _endOfData; // Whether the stream end has been reached + uint _stereo; + + RawChunkStream _streamReader; + +public: + int readBuffer(int16 *buffer, const int numSamples); + + bool isStereo() const { + return _stereo; + } + bool endOfData() const { + return _endOfData; + } + + int getRate() const { + return _rate; + } + Audio::Timestamp getLength() const { + return _playtime; + } + + bool rewind(); +}; + +/** + * Creates an audio stream, which plays from the given buffer. + * + * @param buffer Buffer to play from. + * @param size Size of the buffer in bytes. + * @param rate Rate of the sound data. + * @param dispose AfterUse Whether to free the buffer after use (with free!). + * @return The new SeekableAudioStream (or 0 on failure). + */ +Audio::RewindableAudioStream *makeRawZorkStream(const byte *buffer, uint32 size, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +/** + * Creates an audio stream, which plays from the given stream. + * + * @param stream Stream object to play from. + * @param rate Rate of the sound data. + * @param dispose AfterUse Whether to delete the stream after use. + * @return The new SeekableAudioStream (or 0 on failure). + */ +Audio::RewindableAudioStream *makeRawZorkStream(Common::SeekableReadStream *stream, + int rate, + bool stereo, + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); + +Audio::RewindableAudioStream *makeRawZorkStream(const Common::String &filePath, ZVision *engine); + +} // End of namespace ZVision + +#endif |