aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Kiewitz2015-06-06 22:50:36 +0200
committerMartin Kiewitz2015-06-06 22:50:36 +0200
commit97813f89ecd2a06c74f776708a3d1852c96811a7 (patch)
tree8a9d4bc96dcad8b2cc87bb116199cbdd1465f337
parent2ac05321aab74a4fced53f402f1b0cd16a33cc23 (diff)
downloadscummvm-rg350-97813f89ecd2a06c74f776708a3d1852c96811a7.tar.gz
scummvm-rg350-97813f89ecd2a06c74f776708a3d1852c96811a7.tar.bz2
scummvm-rg350-97813f89ecd2a06c74f776708a3d1852c96811a7.zip
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
-rw-r--r--audio/decoders/3do.cpp420
-rw-r--r--audio/decoders/3do.h96
-rw-r--r--audio/decoders/aiff.cpp21
-rw-r--r--audio/decoders/aiff.h5
-rw-r--r--engines/sherlock/debugger.cpp43
-rw-r--r--engines/sherlock/debugger.h5
-rw-r--r--engines/sherlock/scalpel/3do/movie_decoder.cpp36
-rw-r--r--engines/sherlock/scalpel/3do/movie_decoder.h2
8 files changed, 418 insertions, 210 deletions
diff --git a/audio/decoders/3do.cpp b/audio/decoders/3do.cpp
index ab98aa2ba2..32e48005be 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 4d8412a4cb..7524358543 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 1fc811973f..3a5849f6fd 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 5f21fc9bdf..14bfd05ad9 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 aaf24646e4..a627d7c494 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 39866d0cd3..9b12668679 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 bd3d483e2f..9280bbab13 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 60e79fa238..52d30cd9e2 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;