diff options
| author | vanfanel | 2015-11-11 17:56:12 +0100 | 
|---|---|---|
| committer | vanfanel | 2015-11-11 17:56:12 +0100 | 
| commit | 99739a13fe844c807d3cdd87e67e207e888fd48a (patch) | |
| tree | 6afbf4763326277efbf528f0bb9e587bf7a01788 /audio/decoders | |
| parent | 37e157a11c3fc731dfdcf6ec6b6a5a448550219b (diff) | |
| parent | 7e44493fe8877a3c6a65f83b9ed84a5f59169005 (diff) | |
| download | scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.gz scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.tar.bz2 scummvm-rg350-99739a13fe844c807d3cdd87e67e207e888fd48a.zip  | |
Merge branch 'master' into dispmanx
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  | 
