[Scummvm-cvs-logs] scummvm master -> 97813f89ecd2a06c74f776708a3d1852c96811a7

m-kiewitz m_kiewitz at users.sourceforge.net
Sat Jun 6 22:51:21 CEST 2015


This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
97813f89ec SHERLOCK: rework 3DO audio, add AIFC file support


Commit: 97813f89ecd2a06c74f776708a3d1852c96811a7
    https://github.com/scummvm/scummvm/commit/97813f89ecd2a06c74f776708a3d1852c96811a7
Author: Martin Kiewitz (m_kiewitz at users.sourceforge.net)
Date: 2015-06-06T22:50:36+02:00

Commit Message:
SHERLOCK: rework 3DO audio, add AIFC file support

- rework 3DO audio decoders to decode into buffer only
- 3DO audio decoders also use streams without separate size arg now
- add support for ADP4 + SDX2 inside AIFC files
- add debug command "3do_playaudio" to play AIFC files
- remove audio flags and replace with stereo bool

Changed paths:
    audio/decoders/3do.cpp
    audio/decoders/3do.h
    audio/decoders/aiff.cpp
    audio/decoders/aiff.h
    engines/sherlock/debugger.cpp
    engines/sherlock/debugger.h
    engines/sherlock/scalpel/3do/movie_decoder.cpp
    engines/sherlock/scalpel/3do/movie_decoder.h



diff --git a/audio/decoders/3do.cpp b/audio/decoders/3do.cpp
index ab98aa2..32e4800 100644
--- a/audio/decoders/3do.cpp
+++ b/audio/decoders/3do.cpp
@@ -26,250 +26,318 @@
 
 #include "audio/decoders/3do.h"
 #include "audio/decoders/raw.h"
+#include "audio/decoders/adpcm_intern.h"
 
 namespace Audio {
 
-#define AUDIO_3DO_ADP4_STEPSIZETABLE_MAX 88
-
-static int16 audio_3DO_ADP4_stepSizeTable[AUDIO_3DO_ADP4_STEPSIZETABLE_MAX + 1] = {
-        7,     8,     9,    10,    11,    12,    13,    14,    16,    17,
-	   19,    21,    23,    25,    28,    31,    34,    37,    41,    45,
-	   50,    55,    60,    66,    73,    80,    88,    97,   107,   118,
-	  130,   143,   157,   173,   190,   209,   230,   253,   279,   307,
-	  337,   371,   408,   449,   494,   544,   598,   658,   724,   796,
-	  876,   963,  1060,  1166,  1282,  1411,  1552,  1707,  1878,  2066,
-	 2272,  2499,  2749,  3024,  3327,  3660,  4026,  4428,  4871,  5358,
-	 5894,  6484,  7132,  7845,  8630,  9493, 10442, 11487, 12635, 13899,
-    15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, 32767
-};
+// 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;
 
-static int16 audio_3DO_ADP4_stepSizeIndex[] = {
-	-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 
+	reset();
 };
 
-int16 audio_3DO_ADP4_DecodeSample(uint8 dataNibble, int16 &decoderLastSample, int16 &decoderStepIndex) {
-	int16 currentStep = audio_3DO_ADP4_stepSizeTable[decoderStepIndex];
-	int32 decodedSample = decoderLastSample;
+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 (dataNibble & 1)
+	if (compressedNibble & 1)
 		delta += currentStep >> 2;
 
-	if (dataNibble & 2)
+	if (compressedNibble & 2)
 		delta += currentStep >> 1;
 
-	if (dataNibble & 4)
+	if (compressedNibble & 4)
 		delta += currentStep;
 
-	if (dataNibble & 8) {
+	if (compressedNibble & 8) {
 		decodedSample -= delta;
 	} else {
 		decodedSample += delta;
 	}
 
-	decoderLastSample = CLIP<int32>(decodedSample, -32768, 32767);
+	_curDecoderData.lastSample = CLIP<int32>(decodedSample, -32768, 32767);
 
-	decoderStepIndex += audio_3DO_ADP4_stepSizeIndex[dataNibble & 0x07];
-	decoderStepIndex = CLIP<int16>(decoderStepIndex, 0, AUDIO_3DO_ADP4_STEPSIZETABLE_MAX);
+	_curDecoderData.stepIndex += audio_3DO_ADP4_stepSizeIndex[compressedNibble & 0x07];
+	_curDecoderData.stepIndex = CLIP<int16>(_curDecoderData.stepIndex, 0, ARRAYSIZE(audio_3DO_ADP4_stepSizeTable) - 1);
 
-   return decoderLastSample;
+   return _curDecoderData.lastSample;
 }
 
-SeekableAudioStream *make3DO_ADP4Stream(Common::SeekableReadStream *stream,	uint32 size, uint16 sampleRate, byte audioFlags, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_ADP4_PersistentSpace *persistentSpace) {
-	int32 streamPos = 0;
-	int32 compressedSize = size;
-	int32 decompressedSize = 0;
-	int32 decompressedPos = 0;
-	byte  compressedByte = 0;
+// 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;
 
-	audio_3DO_ADP4_PersistentSpace decoderData;
+	int8  compressedByte = 0;
 
-	assert(compressedSize <= stream->size());
+	if (endOfData())
+		return 0; // no more bytes left
 
-	if (audioFlags & Audio::FLAG_UNSIGNED) {
-		// Unsigned data is not allowed
-		warning("make3DO_ADP4Stream(): sample data result is expected to be signed");
-		return 0;
-	}
-	if (!(audioFlags & Audio::FLAG_16BITS)) {
-		// 8-bit sample data is not allowed
-		warning("make3DO_ADP4Stream(): sample data result is expected to be 16-bit");
-		return 0;
-	}
-	if (audioFlags & Audio::FLAG_LITTLE_ENDIAN) {
-		// LE sample data is not allowed
-		warning("make3DO_ADP4Stream(): sample data result is expected to be Big Endian");
-		return 0;
-	}
-	if (audioFlags & Audio::FLAG_STEREO) {
-		warning("make3DO_ADP4Stream(): stereo currently not supported");
-		return 0;
+	if (_callerDecoderData) {
+		// copy caller decoder data over
+		memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
+		if (_initialRead) {
+			_initialRead = false;
+			memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
+		}
 	}
 
-	if (persistentSpace) {
-		memcpy(&decoderData, persistentSpace, sizeof(decoderData));
-	} else {
-		memset(&decoderData, 0, sizeof(decoderData));
-	}
+	requestedBytesLeft = numSamples >> 1; // 1 byte for 2 16-bit sample
+	if (requestedBytesLeft > _streamBytesLeft)
+		requestedBytesLeft = _streamBytesLeft; // not enough bytes left
 
-	assert(compressedSize < 0x40000000); // safety check
+	// in case caller requests an uneven amount of samples, we will return an even amount
 
-	decompressedSize = compressedSize * 4; // 4 bits == 1 16-bit sample
-	byte *decompressedData = (byte *)malloc(decompressedSize);
-	assert(decompressedData);
+	// 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;
+		}
 
-	if (!(audioFlags & Audio::FLAG_STEREO)) {
-		// Mono
-		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
-			compressedByte = stream->readByte();
+		requestedBytesLeft -= byteCacheSize;
+		_streamBytesLeft   -= byteCacheSize;
+
+		// Fill our byte cache
+		_stream->read(byteCache, byteCacheSize);
+
+		byteCachePtr = byteCache;
 
-			WRITE_BE_UINT16(decompressedData + decompressedPos, audio_3DO_ADP4_DecodeSample(compressedByte >> 4, decoderData.lastSample, decoderData.stepIndex));
-			decompressedPos += 2;
-			WRITE_BE_UINT16(decompressedData + decompressedPos, audio_3DO_ADP4_DecodeSample(compressedByte & 0x0F, decoderData.lastSample, decoderData.stepIndex));
-			decompressedPos += 2;
+		// Mono
+		while (byteCacheSize) {
+			compressedByte = *byteCachePtr++;
+			byteCacheSize--;
+
+			buffer[decodedSamplesCount] = decodeSample(compressedByte >> 4);
+			decodedSamplesCount++;
+			buffer[decodedSamplesCount] = decodeSample(compressedByte & 0x0f);
+			decodedSamplesCount++;
 		}
 	}
 
-	if (disposeAfterUse == DisposeAfterUse::YES)
-		delete stream;
-
-	if (persistentSpace) {
-		memcpy(persistentSpace, &decoderData, sizeof(decoderData));
+	if (_callerDecoderData) {
+		// copy caller decoder data back
+		memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
 	}
 
-	// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
-	return makeRawStream(decompressedData, decompressedSize, sampleRate, audioFlags);
+	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
+	-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
 };
 
-SeekableAudioStream *make3DO_SDX2Stream(Common::SeekableReadStream *stream,	uint32 size, uint16 sampleRate, byte audioFlags, DisposeAfterUse::Flag disposeAfterUse, audio_3DO_SDX2_PersistentSpace *persistentSpace) {
-	int32 streamPos = 0;
-	int32 compressedSize = size;
-	int32 decompressedSize = 0;
-	int32 decompressedPos = 0;
+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;
 
-	audio_3DO_SDX2_PersistentSpace decoderData;
-
-	assert(compressedSize <= stream->size());
+	if (endOfData())
+		return 0; // no more bytes left
 
-	if (audioFlags & Audio::FLAG_UNSIGNED) {
-		// Unsigned data is not allowed
-		warning("make3DO_SDX2Stream(): sample data result is expected to be signed");
-		return 0;
-	}
-	if (!(audioFlags & Audio::FLAG_16BITS)) {
-		// 8-bit sample data is not allowed
-		warning("make3DO_SDX2Stream(): sample data result is expected to be 16-bit");
-		return 0;
-	}
-	if (audioFlags & Audio::FLAG_LITTLE_ENDIAN) {
-		// LE sample data is not allowed
-		warning("make3DO_SDX2Stream(): sample data result is expected to be Big Endian");
-		return 0;
-	}
-	if (audioFlags & Audio::FLAG_STEREO) {
-		if (compressedSize & 1) {
-			warning("make3DO_SDX2Stream(): stereo data is uneven size");
-			return 0;
-		}
+	if (_stereo) {
+		// We expect numSamples to be even in case of Stereo audio
+		assert((numSamples & 1) == 0);
 	}
 
-	if (persistentSpace) {
-		memcpy(&decoderData, persistentSpace, sizeof(decoderData));
-	} else {
-		memset(&decoderData, 0, sizeof(decoderData));
+	if (_callerDecoderData) {
+		// copy caller decoder data over
+		memcpy(&_curDecoderData, _callerDecoderData, sizeof(_curDecoderData));
+		if (_initialRead) {
+			_initialRead = false;
+			memcpy(&_initialDecoderData, &_curDecoderData, sizeof(_initialDecoderData));
+		}
 	}
 
-	assert(compressedSize < 0x40000000); // safety check
+	requestedBytesLeft = numSamples;
+	if (requestedBytesLeft > _streamBytesLeft)
+		requestedBytesLeft = _streamBytesLeft; // not enough bytes left
 
-	decompressedSize = compressedSize * 2; // 1 byte == 1 16-bit sample
-	byte *decompressedData = (byte *)malloc(decompressedSize);
-	assert(decompressedData);
+	// 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;
+		}
 
-	if (!(audioFlags & Audio::FLAG_STEREO)) {
-		// Mono
-		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
-			compressedByte = stream->readSByte();
-			squareTableOffset = compressedByte + 128;
+		requestedBytesLeft -= byteCacheSize;
+		_streamBytesLeft   -= byteCacheSize;
 
-			if (!(compressedByte & 1))
-				decoderData.lastSample1 = 0;
+		// Fill our byte cache
+		_stream->read(byteCache, byteCacheSize);
 
-			decodedSample = decoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
-			decoderData.lastSample1 = decodedSample;
+		byteCachePtr = byteCache;
 
-			WRITE_BE_UINT16(decompressedData + decompressedPos, decodedSample);
-			decompressedPos += 2;
-		}
-
-	} else {
-		// Stereo
-		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
-			compressedByte = stream->readSByte();
-			squareTableOffset = compressedByte + 128;
+		if (!_stereo) {
+			// Mono
+			while (byteCacheSize) {
+				compressedByte = *byteCachePtr++;
+				byteCacheSize--;
+				squareTableOffset = compressedByte + 128;
 
-			if (!(streamPos & 1)) {
-				// First channel
 				if (!(compressedByte & 1))
-					decoderData.lastSample1 = 0;
+					_curDecoderData.lastSample1 = 0;
 
-				decodedSample = decoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
-				decoderData.lastSample1 = decodedSample;
-			} else {
-				// Second channel
-				if (!(compressedByte & 1))
-					decoderData.lastSample2 = 0;
+				decodedSample = _curDecoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
+				_curDecoderData.lastSample1 = decodedSample;
 
-				decodedSample = decoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
-				decoderData.lastSample2 = 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++;
 			}
-
-			WRITE_BE_UINT16(decompressedData + decompressedPos, decodedSample);
-			decompressedPos += 2;
 		}
 	}
 
-	if (disposeAfterUse == DisposeAfterUse::YES)
-		delete stream;
+	if (_callerDecoderData) {
+		// copy caller decoder data back
+		memcpy(_callerDecoderData, &_curDecoderData, sizeof(_curDecoderData));
+	}
 
-	if (persistentSpace) {
-		memcpy(persistentSpace, &decoderData, sizeof(decoderData));
+	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;
 	}
 
-	// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
-	return makeRawStream(decompressedData, decompressedSize, sampleRate, audioFlags);
+	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
index 4d8412a..7524358 100644
--- a/audio/decoders/3do.h
+++ b/audio/decoders/3do.h
@@ -31,6 +31,10 @@
 
 #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;
@@ -40,6 +44,10 @@ 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;
@@ -50,25 +58,78 @@ struct audio_3DO_SDX2_PersistentSpace {
 	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 ADP4 data
- * @size					how many bytes to read from stream
+ * @param stream			the SeekableReadStream from which to read the 3DO SDX2 data
  * @sampleRate				sample rate
- * @audioFlags				flags, that specify the type of output
- * @param					disposeAfterUse	whether to delete the stream after use
+ * @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
  */
-SeekableAudioStream *make3DO_ADP4Stream(
+RewindableAudioStream *make3DO_ADP4AudioStream(
 	Common::SeekableReadStream *stream,
-	uint32 size,
 	uint16 sampleRate,
-	byte audioFlags,
-	DisposeAfterUse::Flag disposeAfterUse,
-	audio_3DO_ADP4_PersistentSpace *persistentSpace = NULL
+	bool stereo,
+	uint32 *audioLengthMSecsPtr = NULL, // returns the audio length in milliseconds
+	DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES,
+	audio_3DO_ADP4_PersistentSpace *persistentSpacePtr = NULL
 );
 
 /**
@@ -76,19 +137,20 @@ SeekableAudioStream *make3DO_ADP4Stream(
  * from that data.
  *
  * @param stream			the SeekableReadStream from which to read the 3DO SDX2 data
- * @size					how many bytes to read from stream
  * @sampleRate				sample rate
- * @audioFlags				flags, that specify the type of output
- * @param					disposeAfterUse	whether to delete the stream after use
+ * @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
  */
-SeekableAudioStream *make3DO_SDX2Stream(
+RewindableAudioStream *make3DO_SDX2AudioStream(
 	Common::SeekableReadStream *stream,
-	uint32 size,
 	uint16 sampleRate,
-	byte audioFlags,
-	DisposeAfterUse::Flag disposeAfterUse,
-	audio_3DO_SDX2_PersistentSpace *persistentSpace = NULL
+	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
diff --git a/audio/decoders/aiff.cpp b/audio/decoders/aiff.cpp
index 1fc8119..3a5849f 100644
--- a/audio/decoders/aiff.cpp
+++ b/audio/decoders/aiff.cpp
@@ -35,6 +35,7 @@
 
 #include "audio/decoders/aiff.h"
 #include "audio/decoders/raw.h"
+#include "audio/decoders/3do.h"
 
 namespace Audio {
 
@@ -70,7 +71,13 @@ static const uint32 kVersionAIFC = MKTAG('A', 'I', 'F', 'C');
 // Codecs
 static const uint32 kCodecPCM = MKTAG('N', 'O', 'N', 'E'); // very original
 
-SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,	DisposeAfterUse::Flag disposeAfterUse) {
+// temporary Wrapper
+// TODO: adjust all calling code to use makeAIFFAudioStream() and dynamic_cast
+SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+	return dynamic_cast<Audio::SeekableAudioStream *>(makeAIFFAudioStream(stream, disposeAfterUse));
+}
+
+AudioStream *makeAIFFAudioStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
 	if (stream->readUint32BE() != MKTAG('F', 'O', 'R', 'M')) {
 		warning("makeAIFFStream: No 'FORM' header");
 
@@ -185,6 +192,8 @@ SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,	DisposeA
 		return 0;
 	}
 
+	bool stereo = channels > 1 ? true : false;
+
 	switch (codec) {
 	case kCodecPCM:
 	case MKTAG('t', 'w', 'o', 's'):
@@ -193,7 +202,7 @@ SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,	DisposeA
 		byte rawFlags = 0;
 		if (bitsPerSample == 16)
 			rawFlags |= Audio::FLAG_16BITS;
-		if (channels == 2)
+		if (stereo)
 			rawFlags |= Audio::FLAG_STEREO;
 		if (codec == MKTAG('s', 'o', 'w', 't'))
 			rawFlags |= Audio::FLAG_LITTLE_ENDIAN;
@@ -211,10 +220,16 @@ SeekableAudioStream *makeAIFFStream(Common::SeekableReadStream *stream,	DisposeA
 		// (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, stereo);
+	case MKTAG('S', 'D', 'X', '2'):
+		// SDX2 on 3DO
+		return make3DO_SDX2AudioStream(dataStream, rate, stereo);
 	default:
 		warning("Unhandled AIFF-C compression tag '%s'", tag2str(codec));
 	}
-	
+
 	delete dataStream;
 	return 0;
 }
diff --git a/audio/decoders/aiff.h b/audio/decoders/aiff.h
index 5f21fc9..14bfd05 100644
--- a/audio/decoders/aiff.h
+++ b/audio/decoders/aiff.h
@@ -42,6 +42,7 @@ class SeekableReadStream;
 
 namespace Audio {
 
+class AudioStream;
 class SeekableAudioStream;
 
 /**
@@ -56,6 +57,10 @@ SeekableAudioStream *makeAIFFStream(
 	Common::SeekableReadStream *stream,
 	DisposeAfterUse::Flag disposeAfterUse);
 
+AudioStream *makeAIFFAudioStream(
+	Common::SeekableReadStream *stream,
+	DisposeAfterUse::Flag disposeAfterUse);
+
 } // End of namespace Audio
 
 #endif
diff --git a/engines/sherlock/debugger.cpp b/engines/sherlock/debugger.cpp
index aaf2464..a627d7c 100644
--- a/engines/sherlock/debugger.cpp
+++ b/engines/sherlock/debugger.cpp
@@ -26,12 +26,18 @@
 
 #include "sherlock/scalpel/3do/movie_decoder.h"
 
+#include "audio/mixer.h"
+#include "audio/decoders/aiff.h"
+#include "audio/decoders/wave.h"
+#include "audio/decoders/3do.h"
+
 namespace Sherlock {
 
 Debugger::Debugger(SherlockEngine *vm) : GUI::Debugger(), _vm(vm) {
 	registerCmd("continue",	     WRAP_METHOD(Debugger, cmdExit));
 	registerCmd("scene",         WRAP_METHOD(Debugger, cmdScene));
 	registerCmd("3do_playmovie", WRAP_METHOD(Debugger, cmd3DO_PlayMovie));
+	registerCmd("3do_playaudio", WRAP_METHOD(Debugger, cmd3DO_PlayAudio));
 	registerCmd("song",          WRAP_METHOD(Debugger, cmdSong));
 }
 
@@ -84,6 +90,43 @@ bool Debugger::cmd3DO_PlayMovie(int argc, const char **argv) {
 	return cmdExit(0, 0);
 }
 
+bool Debugger::cmd3DO_PlayAudio(int argc, const char **argv) {
+	if (argc != 2) {
+		debugPrintf("Format: 3do_playaudio <3do-audio-file>\n");
+		return true;
+	}
+
+	Common::File *file = new Common::File();
+	if (!file->open(argv[1])) {
+		debugPrintf("can not open specified audio file\n");
+		return true;
+	}
+
+	Audio::AudioStream *testStream;
+	Audio::SoundHandle testHandle;
+
+	// Try to load the given file as AIFF/AIFC
+	testStream = Audio::makeAIFFAudioStream(file, DisposeAfterUse::YES);
+
+	if (testStream) {
+		g_system->getMixer()->playStream(Audio::Mixer::kPlainSoundType, &testHandle, testStream);
+		_vm->_events->clearEvents();
+
+		while ((!_vm->shouldQuit()) && g_system->getMixer()->isSoundHandleActive(testHandle)) {
+			_vm->_events->pollEvents();
+			g_system->delayMillis(10);
+			if (_vm->_events->kbHit()) {
+				break;
+			}
+		}
+
+		debugPrintf("playing completed\n");
+		g_system->getMixer()->stopHandle(testHandle);
+	}
+
+	return true;
+}
+
 bool Debugger::cmdSong(int argc, const char **argv) {
 	if (argc != 2) {
 		debugPrintf("Format: song <room>\n");
diff --git a/engines/sherlock/debugger.h b/engines/sherlock/debugger.h
index 39866d0..9b12668 100644
--- a/engines/sherlock/debugger.h
+++ b/engines/sherlock/debugger.h
@@ -56,6 +56,11 @@ private:
 	bool cmd3DO_PlayMovie(int argc, const char **argv);
 
 	/**
+	 * Plays a 3DO audio
+	 */
+	bool cmd3DO_PlayAudio(int argc, const char **argv);
+
+	/**
 	 * Plays a song
 	 */
 	bool cmdSong(int argc, const char **argv);
diff --git a/engines/sherlock/scalpel/3do/movie_decoder.cpp b/engines/sherlock/scalpel/3do/movie_decoder.cpp
index bd3d483..9280bba 100644
--- a/engines/sherlock/scalpel/3do/movie_decoder.cpp
+++ b/engines/sherlock/scalpel/3do/movie_decoder.cpp
@@ -409,40 +409,50 @@ Scalpel3DOMovieDecoder::StreamAudioTrack::StreamAudioTrack(uint32 codecTag, uint
 
 	_codecTag    = codecTag;
 	_sampleRate  = sampleRate;
-	_audioFlags  = Audio::FLAG_16BITS;
-	if (channels > 1)
-		_audioFlags |= Audio::FLAG_STEREO;
-
-	if (_audioFlags & Audio::FLAG_STEREO) {
-		_audioStream = Audio::makeQueuingAudioStream(sampleRate, true);
-	} else {
-		_audioStream = Audio::makeQueuingAudioStream(sampleRate, false);
+	switch (channels) {
+	case 1:
+		_stereo = false;
+		break;
+	case 2:
+		_stereo = true;
+		break;
+	default:
+		error("Unsupported Sherlock 3DO movie audio channels %d", channels);
 	}
 
-	// reset audio decoder persistant spaces
+	_audioStream = Audio::makeQueuingAudioStream(sampleRate, _stereo);
+
+	// reset audio decoder persistent spaces
 	memset(&_ADP4_PersistentSpace, 0, sizeof(_ADP4_PersistentSpace));
 	memset(&_SDX2_PersistentSpace, 0, sizeof(_SDX2_PersistentSpace));
 }
 
 Scalpel3DOMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
 	delete _audioStream;
+//	free(_ADP4_PersistentSpace);
+//	free(_SDX2_PersistentSpace);
 }
 
 void Scalpel3DOMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, uint32 size) {
-	Audio::SeekableAudioStream *audioStream = 0;
+	Common::SeekableReadStream *compressedAudioStream = 0;
+	Audio::RewindableAudioStream *audioStream = 0;
+	uint32 audioLengthMSecs = 0;
+
+	// Read the specified chunk into memory
+	compressedAudioStream = stream->readStream(size);
 
 	switch(_codecTag) {
 	case MKTAG('A','D','P','4'):
-		audioStream = Audio::make3DO_ADP4Stream(stream, size, _sampleRate, _audioFlags, DisposeAfterUse::NO, &_ADP4_PersistentSpace);
+		audioStream = Audio::make3DO_ADP4AudioStream(compressedAudioStream, _sampleRate, _stereo, &audioLengthMSecs, DisposeAfterUse::YES, &_ADP4_PersistentSpace);
 		break;
 	case MKTAG('S','D','X','2'):
-		audioStream = Audio::make3DO_SDX2Stream(stream, size, _sampleRate, _audioFlags, DisposeAfterUse::NO, &_SDX2_PersistentSpace);
+		audioStream = Audio::make3DO_SDX2AudioStream(compressedAudioStream, _sampleRate, _stereo, &audioLengthMSecs, DisposeAfterUse::YES, &_SDX2_PersistentSpace);
 		break;
 	default:
 		break;
 	}
 	if (audioStream) {
-		_totalAudioQueued += audioStream->getLength().msecs();
+		_totalAudioQueued += audioLengthMSecs;
 		_audioStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
 	}
 }
diff --git a/engines/sherlock/scalpel/3do/movie_decoder.h b/engines/sherlock/scalpel/3do/movie_decoder.h
index 60e79fa..52d30cd 100644
--- a/engines/sherlock/scalpel/3do/movie_decoder.h
+++ b/engines/sherlock/scalpel/3do/movie_decoder.h
@@ -107,7 +107,7 @@ private:
 
 		uint32 _codecTag;
 		uint16 _sampleRate;
-		byte   _audioFlags;
+		bool   _stereo;
 
 		Audio::audio_3DO_ADP4_PersistentSpace _ADP4_PersistentSpace;
 		Audio::audio_3DO_SDX2_PersistentSpace _SDX2_PersistentSpace;






More information about the Scummvm-git-logs mailing list