aboutsummaryrefslogtreecommitdiff
path: root/audio/decoders
diff options
context:
space:
mode:
Diffstat (limited to 'audio/decoders')
-rw-r--r--audio/decoders/3do.cpp343
-rw-r--r--audio/decoders/3do.h158
-rw-r--r--audio/decoders/adpcm.cpp30
-rw-r--r--audio/decoders/adpcm.h21
-rw-r--r--audio/decoders/aiff.cpp212
-rw-r--r--audio/decoders/aiff.h16
-rw-r--r--audio/decoders/codec.h7
-rw-r--r--audio/decoders/mp3.cpp340
-rw-r--r--audio/decoders/mp3.h21
-rw-r--r--audio/decoders/quicktime.cpp9
-rw-r--r--audio/decoders/raw.cpp20
-rw-r--r--audio/decoders/raw.h12
-rw-r--r--audio/decoders/wave.h10
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