diff options
Diffstat (limited to 'audio/decoders')
-rw-r--r-- | audio/decoders/3do.cpp | 343 | ||||
-rw-r--r-- | audio/decoders/3do.h | 158 | ||||
-rw-r--r-- | audio/decoders/adpcm.cpp | 30 | ||||
-rw-r--r-- | audio/decoders/adpcm.h | 21 | ||||
-rw-r--r-- | audio/decoders/aiff.cpp | 212 | ||||
-rw-r--r-- | audio/decoders/aiff.h | 16 | ||||
-rw-r--r-- | audio/decoders/codec.h | 7 | ||||
-rw-r--r-- | audio/decoders/mp3.cpp | 340 | ||||
-rw-r--r-- | audio/decoders/mp3.h | 21 | ||||
-rw-r--r-- | audio/decoders/quicktime.cpp | 9 | ||||
-rw-r--r-- | audio/decoders/raw.cpp | 20 | ||||
-rw-r--r-- | audio/decoders/raw.h | 12 | ||||
-rw-r--r-- | audio/decoders/wave.h | 10 |
13 files changed, 1020 insertions, 179 deletions
diff --git a/audio/decoders/3do.cpp b/audio/decoders/3do.cpp new file mode 100644 index 0000000000..6d558d4c8c --- /dev/null +++ b/audio/decoders/3do.cpp @@ -0,0 +1,343 @@ +/* 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/textconsole.h" +#include "common/stream.h" +#include "common/util.h" + +#include "audio/decoders/3do.h" +#include "audio/decoders/raw.h" +#include "audio/decoders/adpcm_intern.h" + +namespace Audio { + +// Reuses ADPCM table +#define audio_3DO_ADP4_stepSizeTable Ima_ADPCMStream::_imaTable +#define audio_3DO_ADP4_stepSizeIndex ADPCMStream::_stepAdjustTable + +RewindableAudioStream *make3DO_ADP4AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) { + if (stereo) { + warning("make3DO_ADP4Stream(): stereo currently not supported"); + return 0; + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 2 * 1000 / sampleRate; // 1 byte == 2 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_ADP4_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +Audio3DO_ADP4_Stream::Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_ADP4_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_ADP4_Stream::rewind() { + reset(); + return true; +} + +int16 Audio3DO_ADP4_Stream::decodeSample(byte compressedNibble) { + int16 currentStep = audio_3DO_ADP4_stepSizeTable[_curDecoderData.stepIndex]; + int32 decodedSample = _curDecoderData.lastSample; + int16 delta = currentStep >> 3; + + if (compressedNibble & 1) + delta += currentStep >> 2; + + if (compressedNibble & 2) + delta += currentStep >> 1; + + if (compressedNibble & 4) + delta += currentStep; + + if (compressedNibble & 8) { + decodedSample -= delta; + } else { + decodedSample += delta; + } + + _curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767); + + _curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07]; + _curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1); + + return _curDecoderData.lastSample; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_ADP4_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = 0; + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // in case caller requests an uneven amount of samples, we will return an even amount + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + + buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4); + decodedSamplesCount++; + buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f); + decodedSamplesCount++; + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +// ============================================================================ +static int16 audio_3DO_SDX2_SquareTable[256] = { + -32768,-32258,-31752,-31250,-30752,-30258,-29768,-29282,-28800,-28322, + -27848,-27378,-26912,-26450,-25992,-25538,-25088,-24642,-24200,-23762, + -23328,-22898,-22472,-22050,-21632,-21218,-20808,-20402,-20000,-19602, + -19208,-18818,-18432,-18050,-17672,-17298,-16928,-16562,-16200,-15842, + -15488,-15138,-14792,-14450,-14112,-13778,-13448,-13122,-12800,-12482, + -12168,-11858,-11552,-11250,-10952,-10658,-10368,-10082, -9800, -9522, + -9248, -8978, -8712, -8450, -8192, -7938, -7688, -7442, -7200, -6962, + -6728, -6498, -6272, -6050, -5832, -5618, -5408, -5202, -5000, -4802, + -4608, -4418, -4232, -4050, -3872, -3698, -3528, -3362, -3200, -3042, + -2888, -2738, -2592, -2450, -2312, -2178, -2048, -1922, -1800, -1682, + -1568, -1458, -1352, -1250, -1152, -1058, -968, -882, -800, -722, + -648, -578, -512, -450, -392, -338, -288, -242, -200, -162, + -128, -98, -72, -50, -32, -18, -8, -2, 0, 2, + 8, 18, 32, 50, 72, 98, 128, 162, 200, 242, + 288, 338, 392, 450, 512, 578, 648, 722, 800, 882, + 968, 1058, 1152, 1250, 1352, 1458, 1568, 1682, 1800, 1922, + 2048, 2178, 2312, 2450, 2592, 2738, 2888, 3042, 3200, 3362, + 3528, 3698, 3872, 4050, 4232, 4418, 4608, 4802, 5000, 5202, + 5408, 5618, 5832, 6050, 6272, 6498, 6728, 6962, 7200, 7442, + 7688, 7938, 8192, 8450, 8712, 8978, 9248, 9522, 9800, 10082, + 10368, 10658, 10952, 11250, 11552, 11858, 12168, 12482, 12800, 13122, + 13448, 13778, 14112, 14450, 14792, 15138, 15488, 15842, 16200, 16562, + 16928, 17298, 17672, 18050, 18432, 18818, 19208, 19602, 20000, 20402, + 20808, 21218, 21632, 22050, 22472, 22898, 23328, 23762, 24200, 24642, + 25088, 25538, 25992, 26450, 26912, 27378, 27848, 28322, 28800, 29282, + 29768, 30258, 30752, 31250, 31752, 32258 +}; + +Audio3DO_SDX2_Stream::Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) + : _sampleRate(sampleRate), _stereo(stereo), + _stream(stream, disposeAfterUse) { + + _callerDecoderData = persistentSpace; + memset(&_initialDecoderData, 0, sizeof(_initialDecoderData)); + _initialRead = true; + + reset(); +} + +void Audio3DO_SDX2_Stream::reset() { + memcpy(&_curDecoderData, &_initialDecoderData, sizeof(_curDecoderData)); + _streamBytesLeft = _stream->size(); + _stream->seek(0); +} + +bool Audio3DO_SDX2_Stream::rewind() { + reset(); + return true; +} + +// Writes the requested amount (or less) of samples into buffer and returns the amount of samples, that got written +int Audio3DO_SDX2_Stream::readBuffer(int16 *buffer, const int numSamples) { + int8 byteCache[AUDIO_3DO_CACHE_SIZE]; + int8 *byteCachePtr = NULL; + int byteCacheSize = 0; + int requestedBytesLeft = numSamples; // 1 byte per 16-bit sample + int decodedSamplesCount = 0; + + int8 compressedByte = 0; + uint8 squareTableOffset = 0; + int16 decodedSample = 0; + + if (endOfData()) + return 0; // no more bytes left + + if (_stereo) { + // We expect numSamples to be even in case of Stereo audio + assert((numSamples & 1) == 0); + } + + if (_callerDecoderData) { + // copy caller decoder data over + memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData)); + if (_initialRead) { + _initialRead = false; + memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData)); + } + } + + requestedBytesLeft = numSamples; + if (requestedBytesLeft > _streamBytesLeft) + requestedBytesLeft = _streamBytesLeft; // not enough bytes left + + // buffering, so that direct decoding of files and such runs way faster + while (requestedBytesLeft) { + if (requestedBytesLeft > AUDIO_3DO_CACHE_SIZE) { + byteCacheSize = AUDIO_3DO_CACHE_SIZE; + } else { + byteCacheSize = requestedBytesLeft; + } + + requestedBytesLeft -= byteCacheSize; + _streamBytesLeft -= byteCacheSize; + + // Fill our byte cache + _stream->read(byteCache, byteCacheSize); + + byteCachePtr = byteCache; + + if (!_stereo) { + // Mono + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } else { + // Stereo + while (byteCacheSize) { + compressedByte = *byteCachePtr++; + byteCacheSize--; + squareTableOffset = compressedByte + 128; + + if (!(decodedSamplesCount & 1)) { + // First channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample1 = 0; + + decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample1 = decodedSample; + } else { + // Second channel + if (!(compressedByte & 1)) + _curDecoderData.lastSample2 = 0; + + decodedSample = _curDecoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset]; + _curDecoderData.lastSample2 = decodedSample; + } + + buffer[decodedSamplesCount] = decodedSample; + decodedSamplesCount++; + } + } + } + + if (_callerDecoderData) { + // copy caller decoder data back + memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData)); + } + + return decodedSamplesCount; +} + +RewindableAudioStream *make3DO_SDX2AudioStream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, uint32 *audioLengthMSecsPtr, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) { + if (stereo) { + if (stream->size() & 1) { + warning("make3DO_SDX2Stream(): stereo data is uneven size"); + return 0; + } + } + + if (audioLengthMSecsPtr) { + // Caller requires the milliseconds of audio + uint32 audioLengthMSecs = stream->size() * 1000 / sampleRate; // 1 byte == 1 16-bit sample + if (stereo) { + audioLengthMSecs /= 2; + } + *audioLengthMSecsPtr = audioLengthMSecs; + } + + return new Audio3DO_SDX2_Stream(stream, sampleRate, stereo, disposeAfterUse, persistentSpace); +} + +} // End of namespace Audio diff --git a/audio/decoders/3do.h b/audio/decoders/3do.h new file mode 100644 index 0000000000..7524358543 --- /dev/null +++ b/audio/decoders/3do.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. + * + */ + +/** + * @file + * Sound decoder used in engines: + * - sherlock (3DO version of Serrated Scalpel) + */ + +#ifndef AUDIO_3DO_SDX2_H +#define AUDIO_3DO_SDX2_H + +#include "common/scummsys.h" +#include "common/types.h" +#include "common/substream.h" + +#include "audio/audiostream.h" +#include "audio/decoders/raw.h" + +namespace Common { +class SeekableReadStream; +} + +namespace Audio { + +class SeekableAudioStream; + +// amount of bytes to be used within the decoder classes as buffers +#define AUDIO_3DO_CACHE_SIZE 1024 + +// persistent spaces +struct audio_3DO_ADP4_PersistentSpace { + int16 lastSample; + int16 stepIndex; +}; + +struct audio_3DO_SDX2_PersistentSpace { + int16 lastSample1; + int16 lastSample2; +}; + +class Audio3DO_ADP4_Stream : public RewindableAudioStream { +public: + Audio3DO_ADP4_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr<Common::SeekableReadStream> _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_ADP4_PersistentSpace *_callerDecoderData; + audio_3DO_ADP4_PersistentSpace _initialDecoderData; + audio_3DO_ADP4_PersistentSpace _curDecoderData; + +private: + int16 decodeSample(byte compressedNibble); +}; + +class Audio3DO_SDX2_Stream : public RewindableAudioStream { +public: + Audio3DO_SDX2_Stream(Common::SeekableReadStream *stream, uint16 sampleRate, bool stereo, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpacePtr); + +protected: + const uint16 _sampleRate; + const bool _stereo; + + Common::DisposablePtr<Common::SeekableReadStream> _stream; + int32 _streamBytesLeft; + + void reset(); + bool rewind(); + bool endOfData() const { return (_stream->pos() >= _stream->size()); } + bool isStereo() const { return _stereo; } + int getRate() const { return _sampleRate; } + + int readBuffer(int16 *buffer, const int numSamples); + + bool _initialRead; + audio_3DO_SDX2_PersistentSpace *_callerDecoderData; + audio_3DO_SDX2_PersistentSpace _initialDecoderData; + audio_3DO_SDX2_PersistentSpace _curDecoderData; +}; + +/** + * Try to decode 3DO ADP4 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_ADP4AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL +); + +/** + * Try to decode 3DO SDX2 data from the given seekable stream and create a SeekableAudioStream + * from that data. + * + * @param stream the SeekableReadStream from which to read the 3DO SDX2 data + * @sampleRate sample rate + * @stereo if it's stereo or mono + * @audioLengthMSecsPtr pointer to a uint32 variable, that is supposed to get the length of the audio in milliseconds + * @disposeAfterUse disposeAfterUse whether to delete the stream after use + * @persistentSpacePtr pointer to the persistent space structure + * @return a new SeekableAudioStream, or NULL, if an error occurred + */ +RewindableAudioStream *make3DO_SDX2AudioStream( + Common::SeekableReadStream *stream, + uint16 sampleRate, + bool stereo, + uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds + DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES, + audio_3DO_SDX2_PersistentSpace *persistentSpacePtr = NULL +); + +} // End of namespace Audio + +#endif diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp index 2f710f759d..fe5eec5dcc 100644 --- a/audio/decoders/adpcm.cpp +++ b/audio/decoders/adpcm.cpp @@ -457,4 +457,34 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo } } +class PacketizedADPCMStream : public StatelessPacketizedAudioStream { +public: + PacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) : + StatelessPacketizedAudioStream(rate, channels), _type(type), _blockAlign(blockAlign) {} + +protected: + AudioStream *makeStream(Common::SeekableReadStream *data); + +private: + ADPCMType _type; + uint32 _blockAlign; +}; + +AudioStream *PacketizedADPCMStream::makeStream(Common::SeekableReadStream *data) { + return makeADPCMStream(data, DisposeAfterUse::YES, data->size(), _type, getRate(), getChannels(), _blockAlign); +} + +PacketizedAudioStream *makePacketizedADPCMStream(ADPCMType type, int rate, int channels, uint32 blockAlign) { + // Filter out types we can't support (they're not fully stateless) + switch (type) { + case kADPCMOki: + case kADPCMDVI: + return 0; + default: + break; + } + + return new PacketizedADPCMStream(type, rate, channels, blockAlign); +} + } // End of namespace Audio diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h index bf6e7f759d..650bc341b3 100644 --- a/audio/decoders/adpcm.h +++ b/audio/decoders/adpcm.h @@ -44,6 +44,7 @@ class SeekableReadStream; namespace Audio { +class PacketizedAudioStream; class RewindableAudioStream; // There are several types of ADPCM encoding, only some are supported here @@ -81,6 +82,26 @@ RewindableAudioStream *makeADPCMStream( int channels, uint32 blockAlign = 0); +/** + * Creates a PacketizedAudioStream that will automatically queue + * packets as individual AudioStreams like returned by makeADPCMStream. + * + * Due to the ADPCM types not necessarily supporting stateless + * streaming, OKI and DVI are not supported by this function + * and will return NULL. + * + * @param type the compression type used + * @param rate the sampling rate + * @param channels the number of channels + * @param blockAlign block alignment ??? + * @return The new PacketizedAudioStream or NULL, if the type isn't supported. + */ +PacketizedAudioStream *makePacketizedADPCMStream( + ADPCMType type, + int rate, + int channels, + uint32 blockAlign = 0); + } // End of namespace Audio #endif diff --git a/audio/decoders/aiff.cpp b/audio/decoders/aiff.cpp index b714721c02..e1949ebb07 100644 --- a/audio/decoders/aiff.cpp +++ b/audio/decoders/aiff.cpp @@ -24,16 +24,19 @@ * The code in this file is based on information found at * http://www.borg.com/~jglatt/tech/aiff.htm * - * We currently only implement uncompressed AIFF. If we ever need AIFF-C, SoX - * (http://sox.sourceforge.net) may be a good place to start from. + * Also partially based on libav's aiffdec.c */ +#include "common/debug.h" #include "common/endian.h" #include "common/stream.h" +#include "common/substream.h" #include "common/textconsole.h" +#include "audio/audiostream.h" #include "audio/decoders/aiff.h" #include "audio/decoders/raw.h" +#include "audio/decoders/3do.h" namespace Audio { @@ -62,23 +65,34 @@ uint32 readExtended(Common::SeekableReadStream &stream) { return mantissa; } -bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags) { - byte buf[4]; +// AIFF versions +static const uint32 kVersionAIFF = MKTAG('A', 'I', 'F', 'F'); +static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C'); - stream.read(buf, 4); - if (memcmp(buf, "FORM", 4) != 0) { - warning("loadAIFFFromStream: No 'FORM' header"); - return false; +// Codecs +static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original + +RewindableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) { + if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) { + warning("makeAIFFStream: No 'FORM' header"); + + if (disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + return 0; } - stream.readUint32BE(); + stream->readUint32BE(); // file size + + uint32 version = stream->readUint32BE(); - // This could be AIFC, but we don't handle that case. + if (version != kVersionAIFF && version != kVersionAIFC) { + warning("makeAIFFStream: No 'AIFF' or 'AIFC' header"); + + if (disposeAfterUse == DisposeAfterUse::YES) + delete stream; - stream.read(buf, 4); - if (memcmp(buf, "AIFF", 4) != 0) { - warning("loadAIFFFromStream: No 'AIFF' header"); - return false; + return 0; } // From here on, we only care about the COMM and SSND chunks, which are @@ -87,95 +101,131 @@ bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate bool foundCOMM = false; bool foundSSND = false; - uint16 numChannels = 0, bitsPerSample = 0; - uint32 numSampleFrames = 0, offset = 0, blockSize = 0, soundOffset = 0; + uint16 channels = 0, bitsPerSample = 0; + uint32 rate = 0; + uint32 codec = kCodecPCM; // AIFF default + Common::SeekableReadStream *dataStream = 0; - while (!(foundCOMM && foundSSND) && !stream.err() && !stream.eos()) { - uint32 length, pos; + while (!(foundCOMM && foundSSND) && !stream->err() && !stream->eos()) { + uint32 tag = stream->readUint32BE(); + uint32 length = stream->readUint32BE(); + uint32 pos = stream->pos(); - stream.read(buf, 4); - length = stream.readUint32BE(); - pos = stream.pos(); + if (stream->eos() || stream->err()) + break; - if (memcmp(buf, "COMM", 4) == 0) { + switch (tag) { + case MKTAG('C', 'O', 'M', 'M'): foundCOMM = true; - numChannels = stream.readUint16BE(); - numSampleFrames = stream.readUint32BE(); - bitsPerSample = stream.readUint16BE(); - rate = readExtended(stream); - size = numSampleFrames * numChannels * (bitsPerSample / 8); - } else if (memcmp(buf, "SSND", 4) == 0) { + channels = stream->readUint16BE(); + /* frameCount = */ stream->readUint32BE(); + bitsPerSample = stream->readUint16BE(); + rate = readExtended(*stream); + + if (version == kVersionAIFC) + codec = stream->readUint32BE(); + break; + case MKTAG('S', 'S', 'N', 'D'): foundSSND = true; - offset = stream.readUint32BE(); - blockSize = stream.readUint32BE(); - soundOffset = stream.pos(); + /* uint32 offset = */ stream->readUint32BE(); + /* uint32 blockAlign = */ stream->readUint32BE(); + dataStream = new Common::SeekableSubReadStream(stream, stream->pos(), stream->pos() + length - 8, disposeAfterUse); + break; + case MKTAG('F', 'V', 'E', 'R'): + switch (stream->readUint32BE()) { + case 0: + version = kVersionAIFF; + break; + case 0xA2805140: + version = kVersionAIFC; + break; + default: + warning("Unknown AIFF version chunk version"); + break; + } + break; + case MKTAG('w', 'a', 'v', 'e'): + warning("Found unhandled AIFF-C extra data chunk"); + + if (!dataStream && disposeAfterUse == DisposeAfterUse::YES) + delete stream; + + delete dataStream; + return 0; + default: + debug(1, "Skipping AIFF '%s' chunk", tag2str(tag)); + break; } - stream.seek(pos + length); + stream->seek(pos + length + (length & 1)); // ensure we're also word-aligned } if (!foundCOMM) { - warning("loadAIFFFromStream: Cound not find 'COMM' chunk"); - return false; - } - - if (!foundSSND) { - warning("loadAIFFFromStream: Cound not find 'SSND' chunk"); - return false; - } - - // We only implement a subset of the AIFF standard. - - if (numChannels < 1 || numChannels > 2) { - warning("loadAIFFFromStream: Only 1 or 2 channels are supported, not %d", numChannels); - return false; - } + warning("makeAIFFStream: Cound not find 'COMM' chunk"); - if (bitsPerSample != 8 && bitsPerSample != 16) { - warning("loadAIFFFromStream: Only 8 or 16 bits per sample are supported, not %d", bitsPerSample); - return false; - } + if (!dataStream && disposeAfterUse == DisposeAfterUse::YES) + delete stream; - if (offset != 0 || blockSize != 0) { - warning("loadAIFFFromStream: Block-aligned data is not supported"); - return false; + delete dataStream; + return 0; } - // Samples are always signed, and big endian. - - flags = 0; - if (bitsPerSample == 16) - flags |= Audio::FLAG_16BITS; - if (numChannels == 2) - flags |= Audio::FLAG_STEREO; - - stream.seek(soundOffset); - - // Stream now points at the sample data - - return true; -} - -SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, - DisposeAfterUse::Flag disposeAfterUse) { - int size, rate; - byte *data, flags; + if (!foundSSND) { + warning("makeAIFFStream: Cound not find 'SSND' chunk"); - if (!loadAIFFFromStream(*stream, size, rate, flags)) { if (disposeAfterUse == DisposeAfterUse::YES) delete stream; + return 0; } - data = (byte *)malloc(size); - assert(data); - stream->read(data, size); + // We only implement a subset of the AIFF standard. - if (disposeAfterUse == DisposeAfterUse::YES) - delete stream; + if (channels < 1 || channels > 2) { + warning("makeAIFFStream: Only 1 or 2 channels are supported, not %d", channels); + delete dataStream; + return 0; + } + + // Seek to the start of dataStream, required for at least FileStream + dataStream->seek(0); + + switch (codec) { + case kCodecPCM: + case MKTAG('t', 'w', 'o', 's'): + case MKTAG('s', 'o', 'w', 't'): { + // PCM samples are always signed. + byte rawFlags = 0; + if (bitsPerSample == 16) + rawFlags |= Audio::FLAG_16BITS; + if (channels == 2) + rawFlags |= Audio::FLAG_STEREO; + if (codec == MKTAG('s', 'o', 'w', 't')) + rawFlags |= Audio::FLAG_LITTLE_ENDIAN; + + return makeRawStream(dataStream, rate, rawFlags); + } + case MKTAG('i', 'm', 'a', '4'): + // TODO: Use QT IMA ADPCM + warning("Unhandled AIFF-C QT IMA ADPCM compression"); + break; + case MKTAG('Q', 'D', 'M', '2'): + // TODO: Need to figure out how to integrate this + // (But hopefully never needed) + warning("Unhandled AIFF-C QDM2 compression"); + break; + case MKTAG('A', 'D', 'P', '4'): + // ADP4 on 3DO + return make3DO_ADP4AudioStream(dataStream, rate, channels == 2); + case MKTAG('S', 'D', 'X', '2'): + // SDX2 on 3DO + return make3DO_SDX2AudioStream(dataStream, rate, channels == 2); + default: + warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec)); + } - // Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES. - return makeRawStream(data, size, rate, flags); + delete dataStream; + return 0; } } // End of namespace Audio diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h index afb0342cfd..3af2efb4c9 100644 --- a/audio/decoders/aiff.h +++ b/audio/decoders/aiff.h @@ -23,6 +23,7 @@ /** * @file * Sound decoder used in engines: + * - bbvs * - pegasus * - saga * - sci @@ -41,28 +42,17 @@ class SeekableReadStream; namespace Audio { -class SeekableAudioStream; - -/** - * Try to load an AIFF from the given seekable 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. Currently this function only supports uncompressed - * raw PCM. - */ -extern bool loadAIFFFromStream(Common::SeekableReadStream &stream, int &size, int &rate, byte &flags); +class RewindableAudioStream; /** * Try to load an AIFF from the given seekable stream and create an AudioStream * from that data. * - * This function uses loadAIFFFromStream() internally. - * * @param stream the SeekableReadStream from which to read the AIFF data * @param disposeAfterUse whether to delete the stream after use * @return a new SeekableAudioStream, or NULL, if an error occurred */ -SeekableAudioStream *makeAIFFStream( +RewindableAudioStream *makeAIFFStream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); diff --git a/audio/decoders/codec.h b/audio/decoders/codec.h index 93b6878dee..75910c0963 100644 --- a/audio/decoders/codec.h +++ b/audio/decoders/codec.h @@ -31,6 +31,13 @@ namespace Audio { class AudioStream; +/** + * @deprecated The old method of handling audio codecs that rely + * on the state remaining the same between calls. This should + * only be used for old code. + * + * DEPRECATED; USE PacketizedAudioStream INSTEAD! + */ class Codec { public: Codec() {} diff --git a/audio/decoders/mp3.cpp b/audio/decoders/mp3.cpp index c1b3faaeb1..36233a2e13 100644 --- a/audio/decoders/mp3.cpp +++ b/audio/decoders/mp3.cpp @@ -25,8 +25,11 @@ #ifdef USE_MAD #include "common/debug.h" +#include "common/mutex.h" #include "common/ptr.h" +#include "common/queue.h" #include "common/stream.h" +#include "common/substream.h" #include "common/textconsole.h" #include "common/util.h" @@ -45,107 +48,116 @@ namespace Audio { #pragma mark - -class MP3Stream : public SeekableAudioStream { +class BaseMP3Stream : public virtual AudioStream { +public: + BaseMP3Stream(); + virtual ~BaseMP3Stream(); + + bool endOfData() const { return _state == MP3_STATE_EOS; } + bool isStereo() const { return _channels == 2; } + int getRate() const { return _rate; } + protected: + void decodeMP3Data(Common::ReadStream &stream); + void readMP3Data(Common::ReadStream &stream); + + void initStream(Common::ReadStream &stream); + void readHeader(Common::ReadStream &stream); + void deinitStream(); + + int fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples); + enum State { MP3_STATE_INIT, // Need to init the decoder MP3_STATE_READY, // ready for processing data MP3_STATE_EOS // end of data reached (may need to loop) }; - Common::DisposablePtr<Common::SeekableReadStream> _inStream; - uint _posInFrame; State _state; - Timestamp _length; mad_timer_t _curTime; mad_stream _stream; mad_frame _frame; mad_synth _synth; + uint _channels; + uint _rate; + enum { BUFFER_SIZE = 5 * 8192 }; // This buffer contains a slab of input data byte _buf[BUFFER_SIZE + MAD_BUFFER_GUARD]; +}; +class MP3Stream : private BaseMP3Stream, public SeekableAudioStream { public: MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose); - ~MP3Stream(); int readBuffer(int16 *buffer, const int numSamples); - - bool endOfData() const { return _state == MP3_STATE_EOS; } - bool isStereo() const { return MAD_NCHANNELS(&_frame.header) == 2; } - int getRate() const { return _frame.header.samplerate; } - bool seek(const Timestamp &where); Timestamp getLength() const { return _length; } + protected: - void decodeMP3Data(); - void readMP3Data(); + Common::ScopedPtr<Common::SeekableReadStream> _inStream; - void initStream(); - void readHeader(); - void deinitStream(); + Timestamp _length; + +private: + static Common::SeekableReadStream *skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose); }; -MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) : - _inStream(inStream, dispose), +class PacketizedMP3Stream : private BaseMP3Stream, public PacketizedAudioStream { +public: + PacketizedMP3Stream(Common::SeekableReadStream &firstPacket); + PacketizedMP3Stream(uint channels, uint rate); + ~PacketizedMP3Stream(); + + // AudioStream API + int readBuffer(int16 *buffer, const int numSamples); + bool endOfStream() const; + + // PacketizedAudioStream API + void queuePacket(Common::SeekableReadStream *packet); + void finish(); + +private: + Common::Mutex _mutex; + Common::Queue<Common::SeekableReadStream *> _queue; + bool _finished; +}; + + +BaseMP3Stream::BaseMP3Stream() : _posInFrame(0), _state(MP3_STATE_INIT), - _length(0, 1000), _curTime(mad_timer_zero) { // The MAD_BUFFER_GUARD must always contain zeros (the reason // for this is that the Layer III Huffman decoder of libMAD // may read a few bytes beyond the end of the input buffer). memset(_buf + BUFFER_SIZE, 0, MAD_BUFFER_GUARD); - - // Calculate the length of the stream - initStream(); - - while (_state != MP3_STATE_EOS) - readHeader(); - - // To rule out any invalid sample rate to be encountered here, say in case the - // MP3 stream is invalid, we just check the MAD error code here. - // We need to assure this, since else we might trigger an assertion in Timestamp - // (When getRate() returns 0 or a negative number to be precise). - // Note that we allow "MAD_ERROR_BUFLEN" as error code here, since according - // to mad.h it is also set on EOF. - if ((_stream.error == MAD_ERROR_NONE || _stream.error == MAD_ERROR_BUFLEN) && getRate() > 0) - _length = Timestamp(mad_timer_count(_curTime, MAD_UNITS_MILLISECONDS), getRate()); - - deinitStream(); - - // Reinit stream - _state = MP3_STATE_INIT; - - // Decode the first chunk of data. This is necessary so that _frame - // is setup and isStereo() and getRate() return correct results. - decodeMP3Data(); } -MP3Stream::~MP3Stream() { +BaseMP3Stream::~BaseMP3Stream() { deinitStream(); } -void MP3Stream::decodeMP3Data() { +void BaseMP3Stream::decodeMP3Data(Common::ReadStream &stream) { do { if (_state == MP3_STATE_INIT) - initStream(); + initStream(stream); if (_state == MP3_STATE_EOS) return; // If necessary, load more data into the stream decoder if (_stream.error == MAD_ERROR_BUFLEN) - readMP3Data(); + readMP3Data(stream); while (_state == MP3_STATE_READY) { _stream.error = MAD_ERROR_NONE; @@ -179,11 +191,11 @@ void MP3Stream::decodeMP3Data() { _state = MP3_STATE_EOS; } -void MP3Stream::readMP3Data() { +void BaseMP3Stream::readMP3Data(Common::ReadStream &stream) { uint32 remaining = 0; // Give up immediately if we already used up all data in the stream - if (_inStream->eos()) { + if (stream.eos()) { _state = MP3_STATE_EOS; return; } @@ -198,7 +210,7 @@ void MP3Stream::readMP3Data() { } // Try to read the next block - uint32 size = _inStream->read(_buf + remaining, BUFFER_SIZE - remaining); + uint32 size = stream.read(_buf + remaining, BUFFER_SIZE - remaining); if (size <= 0) { _state = MP3_STATE_EOS; return; @@ -209,31 +221,7 @@ void MP3Stream::readMP3Data() { mad_stream_buffer(&_stream, _buf, size + remaining); } -bool MP3Stream::seek(const Timestamp &where) { - if (where == _length) { - _state = MP3_STATE_EOS; - return true; - } else if (where > _length) { - return false; - } - - const uint32 time = where.msecs(); - - mad_timer_t destination; - mad_timer_set(&destination, time / 1000, time % 1000, 1000); - - if (_state != MP3_STATE_READY || mad_timer_compare(destination, _curTime) < 0) - initStream(); - - while (mad_timer_compare(destination, _curTime) > 0 && _state != MP3_STATE_EOS) - readHeader(); - - decodeMP3Data(); - - return (_state != MP3_STATE_EOS); -} - -void MP3Stream::initStream() { +void BaseMP3Stream::initStream(Common::ReadStream &stream) { if (_state != MP3_STATE_INIT) deinitStream(); @@ -243,7 +231,6 @@ void MP3Stream::initStream() { mad_synth_init(&_synth); // Reset the stream data - _inStream->seek(0, SEEK_SET); _curTime = mad_timer_zero; _posInFrame = 0; @@ -251,16 +238,16 @@ void MP3Stream::initStream() { _state = MP3_STATE_READY; // Read the first few sample bytes - readMP3Data(); + readMP3Data(stream); } -void MP3Stream::readHeader() { +void BaseMP3Stream::readHeader(Common::ReadStream &stream) { if (_state != MP3_STATE_READY) return; // If necessary, load more data into the stream decoder if (_stream.error == MAD_ERROR_BUFLEN) - readMP3Data(); + readMP3Data(stream); while (_state != MP3_STATE_EOS) { _stream.error = MAD_ERROR_NONE; @@ -270,7 +257,7 @@ void MP3Stream::readHeader() { // be far too slow). Hence we perform this explicitly in a separate step. if (mad_header_decode(&_frame.header, &_stream) == -1) { if (_stream.error == MAD_ERROR_BUFLEN) { - readMP3Data(); // Read more data + readMP3Data(stream); // Read more data continue; } else if (MAD_RECOVERABLE(_stream.error)) { debug(6, "MP3Stream: Recoverable error in mad_header_decode (%s)", mad_stream_errorstr(&_stream)); @@ -290,7 +277,7 @@ void MP3Stream::readHeader() { _state = MP3_STATE_EOS; } -void MP3Stream::deinitStream() { +void BaseMP3Stream::deinitStream() { if (_state == MP3_STATE_INIT) return; @@ -302,7 +289,7 @@ void MP3Stream::deinitStream() { _state = MP3_STATE_EOS; } -static inline int scale_sample(mad_fixed_t sample) { +static inline int scaleSample(mad_fixed_t sample) { // round sample += (1L << (MAD_F_FRACBITS - 16)); @@ -316,28 +303,202 @@ static inline int scale_sample(mad_fixed_t sample) { return sample >> (MAD_F_FRACBITS + 1 - 16); } -int MP3Stream::readBuffer(int16 *buffer, const int numSamples) { +int BaseMP3Stream::fillBuffer(Common::ReadStream &stream, int16 *buffer, const int numSamples) { int samples = 0; // Keep going as long as we have input available while (samples < numSamples && _state != MP3_STATE_EOS) { const int len = MIN(numSamples, samples + (int)(_synth.pcm.length - _posInFrame) * MAD_NCHANNELS(&_frame.header)); while (samples < len) { - *buffer++ = (int16)scale_sample(_synth.pcm.samples[0][_posInFrame]); + *buffer++ = (int16)scaleSample(_synth.pcm.samples[0][_posInFrame]); samples++; if (MAD_NCHANNELS(&_frame.header) == 2) { - *buffer++ = (int16)scale_sample(_synth.pcm.samples[1][_posInFrame]); + *buffer++ = (int16)scaleSample(_synth.pcm.samples[1][_posInFrame]); samples++; } _posInFrame++; } if (_posInFrame >= _synth.pcm.length) { // We used up all PCM data in the current frame -- read & decode more - decodeMP3Data(); + decodeMP3Data(stream); + } + } + return samples; +} + +MP3Stream::MP3Stream(Common::SeekableReadStream *inStream, DisposeAfterUse::Flag dispose) : + BaseMP3Stream(), + _inStream(skipID3(inStream, dispose)), + _length(0, 1000) { + + // Initialize the stream with some data and set the channels and rate + // variables + decodeMP3Data(*_inStream); + _channels = MAD_NCHANNELS(&_frame.header); + _rate = _frame.header.samplerate; + + // Calculate the length of the stream + while (_state != MP3_STATE_EOS) + readHeader(*_inStream); + + // To rule out any invalid sample rate to be encountered here, say in case the + // MP3 stream is invalid, we just check the MAD error code here. + // We need to assure this, since else we might trigger an assertion in Timestamp + // (When getRate() returns 0 or a negative number to be precise). + // Note that we allow "MAD_ERROR_BUFLEN" as error code here, since according + // to mad.h it is also set on EOF. + if ((_stream.error == MAD_ERROR_NONE || _stream.error == MAD_ERROR_BUFLEN) && getRate() > 0) + _length = Timestamp(mad_timer_count(_curTime, MAD_UNITS_MILLISECONDS), getRate()); + + deinitStream(); + + // Reinit stream + _state = MP3_STATE_INIT; + _inStream->seek(0); + + // Decode the first chunk of data to set up the stream again. + decodeMP3Data(*_inStream); +} + +int MP3Stream::readBuffer(int16 *buffer, const int numSamples) { + return fillBuffer(*_inStream, buffer, numSamples); +} + +bool MP3Stream::seek(const Timestamp &where) { + if (where == _length) { + _state = MP3_STATE_EOS; + return true; + } else if (where > _length) { + return false; + } + + const uint32 time = where.msecs(); + + mad_timer_t destination; + mad_timer_set(&destination, time / 1000, time % 1000, 1000); + + if (_state != MP3_STATE_READY || mad_timer_compare(destination, _curTime) < 0) { + _inStream->seek(0); + initStream(*_inStream); + } + + while (mad_timer_compare(destination, _curTime) > 0 && _state != MP3_STATE_EOS) + readHeader(*_inStream); + + decodeMP3Data(*_inStream); + + return (_state != MP3_STATE_EOS); +} + +Common::SeekableReadStream *MP3Stream::skipID3(Common::SeekableReadStream *stream, DisposeAfterUse::Flag dispose) { + // Skip ID3 TAG if any + // ID3v1 (beginning with with 'TAG') is located at the end of files. So we can ignore those. + // ID3v2 can be located at the start of files and begins with a 10 bytes header, the first 3 bytes being 'ID3'. + // The tag size is coded on the last 4 bytes of the 10 bytes header as a 32 bit synchsafe integer. + // See http://id3.org/id3v2.4.0-structure for details. + char data[10]; + stream->read(data, sizeof(data)); + + uint32 offset = 0; + if (!stream->eos() && data[0] == 'I' && data[1] == 'D' && data[2] == '3') { + uint32 size = data[9] + 128 * (data[8] + 128 * (data[7] + 128 * data[6])); + // This size does not include an optional 10 bytes footer. Check if it is present. + if (data[5] & 0x10) + size += 10; + + // Add in the 10 bytes we read in + size += sizeof(data); + debug("Skipping ID3 TAG (%d bytes)", size); + offset = size; + } + + return new Common::SeekableSubReadStream(stream, offset, stream->size(), dispose); +} + +PacketizedMP3Stream::PacketizedMP3Stream(Common::SeekableReadStream &firstPacket) : + BaseMP3Stream(), + _finished(false) { + + // Load some data to get the channels/rate + _queue.push(&firstPacket); + decodeMP3Data(firstPacket); + _channels = MAD_NCHANNELS(&_frame.header); + _rate = _frame.header.samplerate; + + // Clear everything + deinitStream(); + _state = MP3_STATE_INIT; + _queue.clear(); +} + +PacketizedMP3Stream::PacketizedMP3Stream(uint channels, uint rate) : + BaseMP3Stream(), + _finished(false) { + _channels = channels; + _rate = rate; +} + +PacketizedMP3Stream::~PacketizedMP3Stream() { + while (!_queue.empty()) { + delete _queue.front(); + _queue.pop(); + } +} + +int PacketizedMP3Stream::readBuffer(int16 *buffer, const int numSamples) { + int samples = 0; + + while (samples < numSamples) { + Common::StackLock lock(_mutex); + + // Empty? Bail out for now + if (_queue.empty()) + return samples; + + Common::SeekableReadStream *packet = _queue.front(); + + if (_state == MP3_STATE_INIT) { + // Initialize everything + decodeMP3Data(*packet); + } else if (_state == MP3_STATE_EOS) { + // Reset the end-of-stream setting + _state = MP3_STATE_READY; + } + + samples += fillBuffer(*packet, buffer + samples, numSamples - samples); + + // If the stream is done, kill it + if (packet->pos() >= packet->size()) { + _queue.pop(); + delete packet; } } + return samples; } +bool PacketizedMP3Stream::endOfStream() const { + if (!endOfData()) + return false; + + // Lock the mutex + Common::StackLock lock(_mutex); + if (!_queue.empty()) + return false; + + return _finished; +} + +void PacketizedMP3Stream::queuePacket(Common::SeekableReadStream *packet) { + Common::StackLock lock(_mutex); + assert(!_finished); + _queue.push(packet); +} + +void PacketizedMP3Stream::finish() { + Common::StackLock lock(_mutex); + _finished = true; +} + #pragma mark - #pragma mark --- MP3 factory functions --- @@ -366,6 +527,15 @@ SeekableAudioStream *makeMP3Stream( } } +PacketizedAudioStream *makePacketizedMP3Stream(Common::SeekableReadStream &firstPacket) { + return new PacketizedMP3Stream(firstPacket); +} + +PacketizedAudioStream *makePacketizedMP3Stream(uint channels, uint rate) { + return new PacketizedMP3Stream(channels, rate); +} + + } // End of namespace Audio #endif // #ifdef USE_MAD diff --git a/audio/decoders/mp3.h b/audio/decoders/mp3.h index 609181bdba..709aad82b9 100644 --- a/audio/decoders/mp3.h +++ b/audio/decoders/mp3.h @@ -51,6 +51,7 @@ class SeekableReadStream; namespace Audio { +class PacketizedAudioStream; class SeekableAudioStream; /** @@ -65,6 +66,26 @@ SeekableAudioStream *makeMP3Stream( Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse); +/** + * Create a new PacketizedAudioStream from the first packet in the given + * stream. It does not own the packet and must be queued again later. + * + * @param firstPacket the SeekableReadStream from which to read the MP3 data + * @return a new PacketizedAudioStream + */ +PacketizedAudioStream *makePacketizedMP3Stream( + Common::SeekableReadStream &firstPacket); + +/** + * Create a new PacketizedAudioStream for a given number of channels + * and sample rate. + * + * @param firstPacket the SeekableReadStream from which to read the MP3 data + * @return a new PacketizedAudioStream + */ +PacketizedAudioStream *makePacketizedMP3Stream( + uint channels, uint rate); + } // End of namespace Audio #endif // #ifdef USE_MAD diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp index 331c850b1a..ff87e7a9f8 100644 --- a/audio/decoders/quicktime.cpp +++ b/audio/decoders/quicktime.cpp @@ -241,6 +241,15 @@ void QuickTimeAudioDecoder::QuickTimeAudioTrack::queueAudio(const Timestamp &len // If we have any samples that we need to skip (ie. we seeked into // the middle of a chunk), skip them here. if (_skipSamples != Timestamp()) { + if (_skipSamples > chunkLength) { + // If the amount we need to skip is greater than the size + // of the chunk, just skip it altogether. + _curMediaPos = _curMediaPos + chunkLength; + _skipSamples = _skipSamples - chunkLength; + delete stream; + continue; + } + skipSamples(_skipSamples, stream); _curMediaPos = _curMediaPos + _skipSamples; chunkLength = chunkLength - _skipSamples; diff --git a/audio/decoders/raw.cpp b/audio/decoders/raw.cpp index 9a9f79092a..477a8e7967 100644 --- a/audio/decoders/raw.cpp +++ b/audio/decoders/raw.cpp @@ -221,4 +221,24 @@ SeekableAudioStream *makeRawStream(const byte *buffer, uint32 size, return makeRawStream(new Common::MemoryReadStream(buffer, size, disposeAfterUse), rate, flags, DisposeAfterUse::YES); } +class PacketizedRawStream : public StatelessPacketizedAudioStream { +public: + PacketizedRawStream(int rate, byte flags) : + StatelessPacketizedAudioStream(rate, ((flags & FLAG_STEREO) != 0) ? 2 : 1), _flags(flags) {} + +protected: + AudioStream *makeStream(Common::SeekableReadStream *data); + +private: + byte _flags; +}; + +AudioStream *PacketizedRawStream::makeStream(Common::SeekableReadStream *data) { + return makeRawStream(data, getRate(), _flags); +} + +PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags) { + return new PacketizedRawStream(rate, flags); +} + } // End of namespace Audio diff --git a/audio/decoders/raw.h b/audio/decoders/raw.h index 14e7bd45fd..7ccbcdaded 100644 --- a/audio/decoders/raw.h +++ b/audio/decoders/raw.h @@ -35,6 +35,7 @@ class SeekableReadStream; namespace Audio { +class PacketizedAudioStream; class SeekableAudioStream; /** @@ -89,6 +90,17 @@ SeekableAudioStream *makeRawStream(Common::SeekableReadStream *stream, int rate, byte flags, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES); +/** + * Creates a PacketizedAudioStream that will automatically queue + * packets as individual AudioStreams like returned by makeRawStream. + * + * @param rate Rate of the sound data. + * @param flags Audio flags combination. + * @see RawFlags + * @return The new PacketizedAudioStream. + */ +PacketizedAudioStream *makePacketizedRawStream(int rate, byte flags); + } // End of namespace Audio #endif diff --git a/audio/decoders/wave.h b/audio/decoders/wave.h index 1dcaefd845..6bc9f72101 100644 --- a/audio/decoders/wave.h +++ b/audio/decoders/wave.h @@ -23,15 +23,25 @@ /** * @file * Sound decoder used in engines: + * - access * - agos + * - cge + * - cge2 + * - fullpipe * - gob + * - hopkins * - mohawk + * - prince * - saga * - sci * - scumm + * - sherlock * - sword1 * - sword2 + * - tony * - tucker + * - wintermute + * - zvision */ #ifndef AUDIO_WAVE_H |