[Scummvm-cvs-logs] scummvm master -> 4dec07bf2df4a15ef353ec95b72e76c3432751fb

m-kiewitz m_kiewitz at users.sourceforge.net
Thu Jun 4 15:53:58 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:
4dec07bf2d SHERLOCK: improve 3DO movie player, add SDX2 codec


Commit: 4dec07bf2df4a15ef353ec95b72e76c3432751fb
    https://github.com/scummvm/scummvm/commit/4dec07bf2df4a15ef353ec95b72e76c3432751fb
Author: Martin Kiewitz (m_kiewitz at users.sourceforge.net)
Date: 2015-06-04T15:53:54+02:00

Commit Message:
SHERLOCK: improve 3DO movie player, add SDX2 codec

- queue up to 0.5 seconds of audio to avoid buffer underruns
- support for SDX2 codec
- put both audio codecs into audio/decoders/3do.cpp
- made movie player capable of playing EA logo movie

Changed paths:
  A audio/decoders/3do.cpp
  A audio/decoders/3do.h
    audio/module.mk
    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
new file mode 100644
index 0000000..ab98aa2
--- /dev/null
+++ b/audio/decoders/3do.cpp
@@ -0,0 +1,275 @@
+/* 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"
+
+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
+};
+
+static int16 audio_3DO_ADP4_stepSizeIndex[] = {
+	-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 
+};
+
+int16 audio_3DO_ADP4_DecodeSample(uint8 dataNibble, int16 &decoderLastSample, int16 &decoderStepIndex) {
+	int16 currentStep = audio_3DO_ADP4_stepSizeTable[decoderStepIndex];
+	int32 decodedSample = decoderLastSample;
+	int16 delta = currentStep >> 3;
+
+	if (dataNibble & 1)
+		delta += currentStep >> 2;
+
+	if (dataNibble & 2)
+		delta += currentStep >> 1;
+
+	if (dataNibble & 4)
+		delta += currentStep;
+
+	if (dataNibble & 8) {
+		decodedSample -= delta;
+	} else {
+		decodedSample += delta;
+	}
+
+	decoderLastSample = CLIP<int32>(decodedSample, -32768, 32767);
+
+	decoderStepIndex += audio_3DO_ADP4_stepSizeIndex[dataNibble & 0x07];
+	decoderStepIndex = CLIP<int16>(decoderStepIndex, 0, AUDIO_3DO_ADP4_STEPSIZETABLE_MAX);
+
+   return decoderLastSample;
+}
+
+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;
+
+	audio_3DO_ADP4_PersistentSpace decoderData;
+
+	assert(compressedSize <= stream->size());
+
+	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 (persistentSpace) {
+		memcpy(&decoderData, persistentSpace, sizeof(decoderData));
+	} else {
+		memset(&decoderData, 0, sizeof(decoderData));
+	}
+
+	assert(compressedSize < 0x40000000); // safety check
+
+	decompressedSize = compressedSize * 4; // 4 bits == 1 16-bit sample
+	byte *decompressedData = (byte *)malloc(decompressedSize);
+	assert(decompressedData);
+
+	if (!(audioFlags & Audio::FLAG_STEREO)) {
+		// Mono
+		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
+			compressedByte = stream->readByte();
+
+			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;
+		}
+	}
+
+	if (disposeAfterUse == DisposeAfterUse::YES)
+		delete stream;
+
+	if (persistentSpace) {
+		memcpy(persistentSpace, &decoderData, sizeof(decoderData));
+	}
+
+	// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
+	return makeRawStream(decompressedData, decompressedSize, sampleRate, audioFlags);
+}
+
+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
+};
+
+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;
+
+	int8  compressedByte = 0;
+	uint8 squareTableOffset = 0;
+	int16 decodedSample = 0;
+
+	audio_3DO_SDX2_PersistentSpace decoderData;
+
+	assert(compressedSize <= stream->size());
+
+	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 (persistentSpace) {
+		memcpy(&decoderData, persistentSpace, sizeof(decoderData));
+	} else {
+		memset(&decoderData, 0, sizeof(decoderData));
+	}
+
+	assert(compressedSize < 0x40000000); // safety check
+
+	decompressedSize = compressedSize * 2; // 1 byte == 1 16-bit sample
+	byte *decompressedData = (byte *)malloc(decompressedSize);
+	assert(decompressedData);
+
+	if (!(audioFlags & Audio::FLAG_STEREO)) {
+		// Mono
+		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
+			compressedByte = stream->readSByte();
+			squareTableOffset = compressedByte + 128;
+
+			if (!(compressedByte & 1))
+				decoderData.lastSample1 = 0;
+
+			decodedSample = decoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
+			decoderData.lastSample1 = decodedSample;
+
+			WRITE_BE_UINT16(decompressedData + decompressedPos, decodedSample);
+			decompressedPos += 2;
+		}
+
+	} else {
+		// Stereo
+		for (streamPos = 0; streamPos < compressedSize; streamPos++) {
+			compressedByte = stream->readSByte();
+			squareTableOffset = compressedByte + 128;
+
+			if (!(streamPos & 1)) {
+				// First channel
+				if (!(compressedByte & 1))
+					decoderData.lastSample1 = 0;
+
+				decodedSample = decoderData.lastSample1 + audio_3DO_SDX2_SquareTable[squareTableOffset];
+				decoderData.lastSample1 = decodedSample;
+			} else {
+				// Second channel
+				if (!(compressedByte & 1))
+					decoderData.lastSample2 = 0;
+
+				decodedSample = decoderData.lastSample2 + audio_3DO_SDX2_SquareTable[squareTableOffset];
+				decoderData.lastSample2 = decodedSample;
+			}
+
+			WRITE_BE_UINT16(decompressedData + decompressedPos, decodedSample);
+			decompressedPos += 2;
+		}
+	}
+
+	if (disposeAfterUse == DisposeAfterUse::YES)
+		delete stream;
+
+	if (persistentSpace) {
+		memcpy(persistentSpace, &decoderData, sizeof(decoderData));
+	}
+
+	// Since we allocated our own buffer for the data, we must specify DisposeAfterUse::YES.
+	return makeRawStream(decompressedData, decompressedSize, sampleRate, audioFlags);
+}
+
+} // End of namespace Audio
diff --git a/audio/decoders/3do.h b/audio/decoders/3do.h
new file mode 100644
index 0000000..4d8412a
--- /dev/null
+++ b/audio/decoders/3do.h
@@ -0,0 +1,96 @@
+/* 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"
+
+namespace Common {
+class SeekableReadStream;
+}
+
+namespace Audio {
+
+class SeekableAudioStream;
+
+struct audio_3DO_ADP4_PersistentSpace {
+	int16 lastSample;
+	int16 stepIndex;
+};
+
+struct audio_3DO_SDX2_PersistentSpace {
+	int16 lastSample1;
+	int16 lastSample2;
+};
+
+
+/**
+ * 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
+ * @sampleRate				sample rate
+ * @audioFlags				flags, that specify the type of output
+ * @param					disposeAfterUse	whether to delete the stream after use
+ * @return					a new SeekableAudioStream, or NULL, if an error occurred
+ */
+SeekableAudioStream *make3DO_ADP4Stream(
+	Common::SeekableReadStream *stream,
+	uint32 size,
+	uint16 sampleRate,
+	byte audioFlags,
+	DisposeAfterUse::Flag disposeAfterUse,
+	audio_3DO_ADP4_PersistentSpace *persistentSpace = 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
+ * @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
+ * @return					a new SeekableAudioStream, or NULL, if an error occurred
+ */
+SeekableAudioStream *make3DO_SDX2Stream(
+	Common::SeekableReadStream *stream,
+	uint32 size,
+	uint16 sampleRate,
+	byte audioFlags,
+	DisposeAfterUse::Flag disposeAfterUse,
+	audio_3DO_SDX2_PersistentSpace *persistentSpace = NULL
+);
+
+} // End of namespace Audio
+
+#endif
diff --git a/audio/module.mk b/audio/module.mk
index 4e1c031..bdb71ab 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -14,6 +14,7 @@ MODULE_OBJS := \
 	musicplugin.o \
 	null.o \
 	timestamp.o \
+	decoders/3do.o \
 	decoders/aac.o \
 	decoders/adpcm.o \
 	decoders/aiff.o \
diff --git a/engines/sherlock/scalpel/3do/movie_decoder.cpp b/engines/sherlock/scalpel/3do/movie_decoder.cpp
index 857e010..a50362c 100644
--- a/engines/sherlock/scalpel/3do/movie_decoder.cpp
+++ b/engines/sherlock/scalpel/3do/movie_decoder.cpp
@@ -26,6 +26,7 @@
 
 #include "audio/audiostream.h"
 #include "audio/decoders/raw.h"
+#include "audio/decoders/3do.h"
 
 #include "sherlock/scalpel/3do/movie_decoder.h"
 #include "image/codecs/cinepak.h"
@@ -44,6 +45,8 @@ namespace Sherlock {
 
 Scalpel3DOMovieDecoder::Scalpel3DOMovieDecoder()
 	: _stream(0), _videoTrack(0), _audioTrack(0) {
+	_streamVideoOffset = 0;
+	_streamAudioOffset = 0;
 }
 
 Scalpel3DOMovieDecoder::~Scalpel3DOMovieDecoder() {
@@ -64,18 +67,11 @@ bool Scalpel3DOMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
 	close();
 
 	_stream = stream;
-
-	// CTRL Header
-	if (_stream->readUint32BE() != MKTAG('C', 'T', 'R', 'L')) {
-		close();
-		return false;
-	}
-
-	uint32 ctrlSize = _stream->readUint32BE() - 8;
-	_stream->skip(ctrlSize);
+	_streamVideoOffset = 0;
+	_streamAudioOffset = 0;
 
 	// Look for packets that we care about
-	static const int maxPacketCheckCount = 10;
+	static const int maxPacketCheckCount = 20;
 	for (int i = 0; i < maxPacketCheckCount; i++) {
 		uint32 tag = _stream->readUint32BE();
 		uint32 chunkSize = _stream->readUint32BE() - 8;
@@ -116,8 +112,9 @@ bool Scalpel3DOMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
 				break;
 
 			default:
-				error("Sherlock 3DO movie: Unknown subtype inside FILM packet");
-				break;
+				warning("Sherlock 3DO movie: Unknown subtype inside FILM packet");
+				close();
+				return false;
 			}
 			break;
 		}
@@ -158,8 +155,9 @@ bool Scalpel3DOMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
 				// Audio data
 				break;
 			default:
-				error("Sherlock 3DO movie: Unknown subtype inside FILM packet");
-				break;
+				warning("Sherlock 3DO movie: Unknown subtype inside FILM packet");
+				close();
+				return false;
 			}
 			break;
 		}
@@ -169,6 +167,10 @@ bool Scalpel3DOMovieDecoder::loadStream(Common::SeekableReadStream *stream) {
 			// Ignore but also accept CTRL + FILL packets
 			break;
 
+		case MKTAG('S','H','D','R'):
+			// Happens for EA logo, seems to be garbage data right at the start of the file
+			break;
+
 		default:
 			warning("Unknown header inside Sherlock 3DO movie");
 			close();
@@ -201,26 +203,58 @@ void Scalpel3DOMovieDecoder::close() {
 	_videoTrack = 0;
 }
 
+// We try to at least decode 1 frame
+// and also try to get at least 0.5 seconds of audio queued up
 void Scalpel3DOMovieDecoder::readNextPacket() {
-	uint32 videoSubType = 0;
+	uint32 currentMovieTime = getTime();
+	uint32 wantedAudioQueued  = currentMovieTime + 500; // always try to be 0.500 seconds in front of movie time
+
+	int32 chunkOffset     = 0;
+	int32 dataStartOffset = 0;
+	int32 nextChunkOffset = 0;
+	uint32 chunkTag       = 0;
+	uint32 chunkSize      = 0;
+
+	uint32 videoSubType   = 0;
 	uint32 videoTimeStamp = 0;
 	uint32 videoFrameSize = 0;
-	uint32 audioSubType = 0;
-	uint32 audioSampleBytes = 0;
-	bool gotAudio = false;
-	bool gotVideo = false;
+	uint32 audioSubType   = 0;
+	uint32 audioBytes     = 0;
+	bool videoGotFrame = false;
+	bool videoDone     = false;
+	bool audioDone     = false;
+
+	// Seek to smallest stream offset
+	if (_streamVideoOffset <= _streamAudioOffset) {
+		_stream->seek(_streamVideoOffset);
+	} else {
+		_stream->seek(_streamAudioOffset);
+	}
 
-	while (!endOfVideoTracks()) {
-		uint32 tag = _stream->readUint32BE();
-		uint32 chunkSize = _stream->readUint32BE() - 8;
-		uint32 dataStartOffset = _stream->pos();
+	if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
+		// already got enough audio queued up
+		audioDone = true;
+	}
+
+	while (1) {
+		chunkOffset = _stream->pos();
+		assert(chunkOffset >= 0);
+
+		// Read chunk header
+		chunkTag = _stream->readUint32BE();
+		chunkSize = _stream->readUint32BE() - 8;
+
+		// Calculate offsets
+		dataStartOffset = _stream->pos();
+		assert(dataStartOffset >= 0);
+		nextChunkOffset = dataStartOffset + chunkSize;
 
 		//warning("offset %lx - tag %lx", dataStartOffset, tag);
 
 		if (_stream->eos())
 			break;
 
-		switch (tag) {
+		switch (chunkTag) {
 		case MKTAG('F','I','L','M'):
 			videoTimeStamp = _stream->readUint32BE();
 			_stream->skip(4); // Unknown
@@ -232,13 +266,34 @@ void Scalpel3DOMovieDecoder::readNextPacket() {
 				break;
 
 			case MKTAG('F', 'R', 'M', 'E'):
-				// Have a frame!
-
-				// If we previously found one, this is just to get the time offset of the next one
-				/* uint32 frmeSize = */ _stream->readUint32BE();
-				videoFrameSize = _stream->readUint32BE();
-				_videoTrack->decodeFrame(_stream->readStream(videoFrameSize), videoTimeStamp);
-				gotVideo = true;
+				// Found frame data
+				if (_streamVideoOffset <= chunkOffset) {
+					if (!videoDone) {
+						if (!videoGotFrame) {
+							// If we previously found one, this is just to get the time offset of the next one
+							_stream->readUint32BE();
+							videoFrameSize = _stream->readUint32BE();
+							_videoTrack->decodeFrame(_stream->readStream(videoFrameSize), videoTimeStamp);
+
+							_streamVideoOffset = nextChunkOffset;
+							videoGotFrame = true;
+
+						} else {
+							// Already decoded a frame, so seek back to current chunk and exit
+
+							// Calculate next frame time
+							// 3DO clock time for movies runs at 240Hh, that's why timestamps are based on 240.
+							uint32 currentFrameStartTime = _videoTrack->getNextFrameStartTime();
+							uint32 nextFrameStartTime = videoTimeStamp * 1000 / 240;
+							assert(currentFrameStartTime <= nextFrameStartTime);
+							_videoTrack->setNextFrameStartTime(nextFrameStartTime);
+
+							// next time we want to start at the current chunk
+							_streamVideoOffset = chunkOffset;
+							videoDone = true;
+						}
+					}
+				}
 				break;
 
 			default:
@@ -258,9 +313,18 @@ void Scalpel3DOMovieDecoder::readNextPacket() {
 
 			case MKTAG('S', 'S', 'M', 'P'):
 				// Got audio chunk
-				audioSampleBytes = _stream->readUint32BE();
-				_audioTrack->queueAudio(_stream, audioSampleBytes);
-				gotAudio = true;
+				if (_streamAudioOffset <= chunkOffset) {
+					if (!audioDone) {
+						audioBytes = _stream->readUint32BE();
+						_audioTrack->queueAudio(_stream, audioBytes);
+
+						_streamAudioOffset = nextChunkOffset;
+						if (wantedAudioQueued <= _audioTrack->getTotalAudioQueued()) {
+							// Got enough audio
+							audioDone = true;
+						}
+					}
+				}
 				break;
 
 			default:
@@ -274,16 +338,21 @@ void Scalpel3DOMovieDecoder::readNextPacket() {
 			// Ignore but also accept CTRL + FILL packets
 			break;
 
+		case MKTAG('S','H','D','R'):
+			// Happens for EA logo, seems to be garbage data right at the start of the file
+			break;
+
 		default:
 			error("Unknown header inside Sherlock 3DO movie");
 		}
 
 		// Always seek to end of chunk
 		// Sometimes not all of the chunk is filled with audio
-		_stream->seek(dataStartOffset + chunkSize);
+		_stream->seek(nextChunkOffset);
 
-		if (gotVideo)
+		if ((videoDone) && (audioDone)) {
 			return;
+		}
 	}
 }
 
@@ -314,101 +383,61 @@ Graphics::PixelFormat Scalpel3DOMovieDecoder::StreamVideoTrack::getPixelFormat()
 }
 
 void Scalpel3DOMovieDecoder::StreamVideoTrack::decodeFrame(Common::SeekableReadStream *stream, uint32 videoTimeStamp) {
-	uint32 currentFrameStartTime = 0;
-
 	_surface = _codec->decodeFrame(*stream);
 	_curFrame++;
-
-	// Calculate next frame time
-	currentFrameStartTime = videoTimeStamp * 1000 / 240;
-	assert(currentFrameStartTime >= _nextFrameStartTime);
-	_nextFrameStartTime = currentFrameStartTime;
 }
 
-#define STREAMAUDIO_STEPSIZETABLE_MAX 88
-
-static int16 streamAudio_stepSizeTable[STREAMAUDIO_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
-};
-
-static int16 streamAudio_stepSizeIndex[] = {
-	-1, -1, -1, -1, 2, 4, 6, 8, -1, -1, -1, -1, 2, 4, 6, 8 
-};
-
 Scalpel3DOMovieDecoder::StreamAudioTrack::StreamAudioTrack(uint32 codecTag, uint32 sampleRate, uint32 channels) {
-	if (channels != 1) {
-		error("Sherlock 3DO stream audio is not mono");
-	}
-	if (codecTag != MKTAG('A','D','P','4')) {
+	switch (codecTag) {
+	case MKTAG('A','D','P','4'):
+	case MKTAG('S','D','X','2'):
+		// ADP4 + SDX2 are both allowed
+		break;
+
+	default:
 		error("Sherlock 3DO stream audio is not using codec ADP4");
 	}
-	_audioStream = Audio::makeQueuingAudioStream(sampleRate, false);
 
-	// reset ADPCM decoding
-	_lastSample = 0;
-	_stepIndex = 0;
-}
+	_totalAudioQueued = 0; // currently 0 milliseconds queued
 
-Scalpel3DOMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
-	delete _audioStream;
-}
-
-int16 Scalpel3DOMovieDecoder::StreamAudioTrack::decodeSample(uint8 dataNibble) {
-	int16 currentStep = streamAudio_stepSizeTable[_stepIndex];
-	int32 decodedSample = _lastSample;
-	int16 delta = currentStep >> 3;
-
-	if (dataNibble & 1)
-		delta += currentStep >> 2;
-
-	if (dataNibble & 2)
-		delta += currentStep >> 1;
-
-	if (dataNibble & 4)
-		delta += currentStep;
+	_codecTag    = codecTag;
+	_sampleRate  = sampleRate;
+	_audioFlags  = Audio::FLAG_16BITS;
+	if (channels > 1)
+		_audioFlags |= Audio::FLAG_STEREO;
 
-	if (dataNibble & 8) {
-		decodedSample -= delta;
+	if (_audioFlags & Audio::FLAG_STEREO) {
+		_audioStream = Audio::makeQueuingAudioStream(sampleRate, true);
 	} else {
-		decodedSample += delta;
+		_audioStream = Audio::makeQueuingAudioStream(sampleRate, false);
 	}
 
-	_lastSample = CLIP<int32>(decodedSample, -32768, 32767);
-
-	_stepIndex += streamAudio_stepSizeIndex[dataNibble & 0x07];
-	_stepIndex = CLIP<int16>(_stepIndex, 0, STREAMAUDIO_STEPSIZETABLE_MAX);
+	// reset audio decoder persistant spaces
+	memset(&_ADP4_PersistentSpace, 0, sizeof(_ADP4_PersistentSpace));
+	memset(&_SDX2_PersistentSpace, 0, sizeof(_SDX2_PersistentSpace));
+}
 
-   return _lastSample;
+Scalpel3DOMovieDecoder::StreamAudioTrack::~StreamAudioTrack() {
+	delete _audioStream;
 }
 
-void Scalpel3DOMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, uint32 length) {
-	uint32 decodedAudioSize = length * 4;
-	byte  *decodedAudioBuffer = NULL;
-	uint8  streamByte = 0;
-	uint32 streamPos = 0;
-	uint32 audioPos = 0;
-
-	decodedAudioBuffer = (byte *)malloc(decodedAudioSize);
-	assert(decodedAudioBuffer);
-
-	for (streamPos = 0; streamPos < length; streamPos++) {
-		streamByte = stream->readByte();
-		WRITE_BE_UINT16(decodedAudioBuffer + audioPos, decodeSample(streamByte >> 4));
-		audioPos += 2;
-		WRITE_BE_UINT16(decodedAudioBuffer + audioPos, decodeSample(streamByte & 0x0F));
-		audioPos += 2;
+void Scalpel3DOMovieDecoder::StreamAudioTrack::queueAudio(Common::SeekableReadStream *stream, uint32 size) {
+	Audio::SeekableAudioStream *audioStream = 0;
+
+	switch(_codecTag) {
+	case MKTAG('A','D','P','4'):
+		audioStream = Audio::make3DO_ADP4Stream(stream, size, _sampleRate, _audioFlags, DisposeAfterUse::NO, &_ADP4_PersistentSpace);
+		break;
+	case MKTAG('S','D','X','2'):
+		audioStream = Audio::make3DO_SDX2Stream(stream, size, _sampleRate, _audioFlags, DisposeAfterUse::NO, &_SDX2_PersistentSpace);
+		break;
+	default:
+		break;
+	}
+	if (audioStream) {
+		_totalAudioQueued += audioStream->getLength().msecs();
+		_audioStream->queueAudioStream(audioStream, DisposeAfterUse::YES);
 	}
-
-	// Now the audio is loaded, so let's queue it
-	_audioStream->queueBuffer(decodedAudioBuffer, decodedAudioSize, DisposeAfterUse::YES, Audio::FLAG_16BITS);
 }
 
 Audio::AudioStream *Scalpel3DOMovieDecoder::StreamAudioTrack::getAudioStream() const {
diff --git a/engines/sherlock/scalpel/3do/movie_decoder.h b/engines/sherlock/scalpel/3do/movie_decoder.h
index be5a834..60e79fa 100644
--- a/engines/sherlock/scalpel/3do/movie_decoder.h
+++ b/engines/sherlock/scalpel/3do/movie_decoder.h
@@ -24,6 +24,7 @@
 #define SHERLOCK_SCALPEL_3DO_MOVIE_DECODER_H
 
 #include "video/video_decoder.h"
+#include "audio/decoders/3do.h"
 
 namespace Audio {
 class QueuingAudioStream;
@@ -51,6 +52,10 @@ protected:
 	void readNextPacket();
 
 private:
+	int32 _streamVideoOffset; /* current stream offset for video decoding */
+	int32 _streamAudioOffset; /* current stream offset for audio decoding */
+
+private:
 	class StreamVideoTrack : public VideoTrack  {
 	public:
 		StreamVideoTrack(uint32 width, uint32 height, uint32 codecTag, uint32 frameCount);
@@ -63,6 +68,7 @@ private:
 		Graphics::PixelFormat getPixelFormat() const;
 		int getCurFrame() const { return _curFrame; }
 		int getFrameCount() const { return _frameCount; }
+		void setNextFrameStartTime(uint32 nextFrameStartTime) { _nextFrameStartTime = nextFrameStartTime; }
 		uint32 getNextFrameStartTime() const { return _nextFrameStartTime; }
 		const Graphics::Surface *decodeNextFrame() { return _surface; }
 
@@ -84,19 +90,27 @@ private:
 		StreamAudioTrack(uint32 codecTag, uint32 sampleRate, uint32 channels);
 		~StreamAudioTrack();
 
-		void queueAudio(Common::SeekableReadStream *stream, uint32 length);
+		void queueAudio(Common::SeekableReadStream *stream, uint32 size);
 
 	protected:
 		Audio::AudioStream *getAudioStream() const;
 
 	private:
 		Audio::QueuingAudioStream *_audioStream;
+		uint32 _totalAudioQueued; /* total amount of milliseconds of audio, that we queued up already */
+
+	public:
+		uint32 getTotalAudioQueued() const { return _totalAudioQueued; }
 
 	private:
 		int16 decodeSample(uint8 dataNibble);
 
-		int16 _lastSample;
-		int16 _stepIndex;
+		uint32 _codecTag;
+		uint16 _sampleRate;
+		byte   _audioFlags;
+
+		Audio::audio_3DO_ADP4_PersistentSpace _ADP4_PersistentSpace;
+		Audio::audio_3DO_SDX2_PersistentSpace _SDX2_PersistentSpace;
 	};
 
 	Common::SeekableReadStream *_stream;






More information about the Scummvm-git-logs mailing list