aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--audio/decoders/adpcm.cpp71
-rw-r--r--audio/decoders/adpcm.h1
-rw-r--r--audio/decoders/adpcm_intern.h30
-rw-r--r--engines/lastexpress/data/snd.cpp37
4 files changed, 82 insertions, 57 deletions
diff --git a/audio/decoders/adpcm.cpp b/audio/decoders/adpcm.cpp
index ac18b244c5..3b53ca2fd1 100644
--- a/audio/decoders/adpcm.cpp
+++ b/audio/decoders/adpcm.cpp
@@ -201,60 +201,49 @@ int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
return samples[0] + samples[1];
}
+
#pragma mark -
-int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
- int samples = 0;
- byte data;
+int MSIma_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
+ // Need to write at least one sample per channel
+ assert((numSamples % _channels) == 0);
- assert(numSamples % 2 == 0);
+ int samples = 0;
while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
if (_blockPos[0] == _blockAlign) {
- // read block header
- _status.ima_ch[0].last = _stream->readSint16LE();
- _status.ima_ch[0].stepIndex = _stream->readSint16LE();
- _blockPos[0] = 4;
- }
+ for (int i = 0; i < _channels; i++) {
+ // read block header
+ _status.ima_ch[i].last = _stream->readSint16LE();
+ _status.ima_ch[i].stepIndex = _stream->readSint16LE();
+ }
- for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
- data = _stream->readByte();
- _blockPos[0]++;
- buffer[samples] = decodeIMA(_invertSamples ? (data >> 4) & 0x0f : data & 0x0f);
- buffer[samples + 1] = decodeIMA(_invertSamples ? data & 0x0f : (data >> 4) & 0x0f);
+ _blockPos[0] = _channels * 4;
}
- }
- return samples;
-}
+ // Decode a set of samples
+ for (int i = 0; i < _channels; i++) {
+ // The stream encodes four bytes per channel at a time
+ for (int j = 0; j < 4; j++) {
+ byte data = _stream->readByte();
+ _blockPos[0]++;
+ _buffer[i][j * 2] = decodeIMA(data & 0x0f, i);
+ _buffer[i][j * 2 + 1] = decodeIMA((data >> 4) & 0x0f, i);
+ _samplesLeft[i] += 2;
+ }
+ }
-// Microsoft as usual tries to implement it differently. This method
-// is used for stereo data.
-int MSIma_ADPCMStream::readBufferMSIMA2(int16 *buffer, const int numSamples) {
- int samples;
- uint32 data;
- int nibble;
- byte k;
-
- // TODO: Currently this implementation only supports
- // reading a multiple of 16 samples at once. We might
- // consider changing that so it could read an arbitrary
- // sample pair count.
- assert(numSamples % 16 == 0);
-
- for (samples = 0; samples < numSamples && !_stream->eos() && _stream->pos() < _endpos;) {
- for (int channel = 0; channel < 2; channel++) {
- data = _stream->readUint32LE();
-
- for (nibble = 0; nibble < 8; nibble++) {
- k = ((data & 0xf0000000) >> 28);
- buffer[samples + channel + nibble * 2] = decodeIMA(k);
- data <<= 4;
+ while (samples < numSamples && _samplesLeft[0] != 0) {
+ for (int i = 0; i < _channels; i++) {
+ buffer[samples] = _buffer[i][8 - _samplesLeft[i]];
+ _samplesLeft[i]--;
}
+
+ samples += _channels;
}
- samples += 16;
}
+
return samples;
}
@@ -449,8 +438,6 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo
return new Oki_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMMSIma:
return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
- case kADPCMMSImaLastExpress:
- return new MSIma_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign, true);
case kADPCMMS:
return new MS_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMDVI:
diff --git a/audio/decoders/adpcm.h b/audio/decoders/adpcm.h
index 7202d6bc9c..f6b424c463 100644
--- a/audio/decoders/adpcm.h
+++ b/audio/decoders/adpcm.h
@@ -56,7 +56,6 @@ class RewindableAudioStream;
enum typesADPCM {
kADPCMOki, // Dialogic/Oki ADPCM (aka VOX)
kADPCMMSIma, // Microsoft IMA ADPCM
- kADPCMMSImaLastExpress, // Microsoft IMA ADPCM (with inverted samples)
kADPCMMS, // Microsoft ADPCM
kADPCMDVI, // Intel DVI IMA ADPCM
kADPCMApple, // Apple QuickTime IMA ADPCM
diff --git a/audio/decoders/adpcm_intern.h b/audio/decoders/adpcm_intern.h
index ea20b40338..dd8c14be79 100644
--- a/audio/decoders/adpcm_intern.h
+++ b/audio/decoders/adpcm_intern.h
@@ -148,24 +148,30 @@ public:
class MSIma_ADPCMStream : public Ima_ADPCMStream {
public:
- MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign, bool invertSamples = false)
- : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign), _invertSamples(invertSamples) {
+ MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
+ : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
+
if (blockAlign == 0)
- error("ADPCMStream(): blockAlign isn't specified for MS IMA ADPCM");
- }
+ error("MSIma_ADPCMStream(): blockAlign isn't specified");
- virtual int readBuffer(int16 *buffer, const int numSamples) {
- if (_channels == 1)
- return readBufferMSIMA1(buffer, numSamples);
- else
- return readBufferMSIMA2(buffer, numSamples);
+ if (blockAlign % (_channels * 4))
+ error("MSIma_ADPCMStream(): invalid blockAlign");
+
+ _samplesLeft[0] = 0;
+ _samplesLeft[1] = 0;
}
- int readBufferMSIMA1(int16 *buffer, const int numSamples);
- int readBufferMSIMA2(int16 *buffer, const int numSamples);
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+
+ void reset() {
+ Ima_ADPCMStream::reset();
+ _samplesLeft[0] = 0;
+ _samplesLeft[1] = 0;
+ }
private:
- bool _invertSamples; // Some implementations invert the way samples are decoded
+ int16 _buffer[2][8];
+ int _samplesLeft[2];
};
class MS_ADPCMStream : public ADPCMStream {
diff --git a/engines/lastexpress/data/snd.cpp b/engines/lastexpress/data/snd.cpp
index bd2320726a..6389489abb 100644
--- a/engines/lastexpress/data/snd.cpp
+++ b/engines/lastexpress/data/snd.cpp
@@ -30,12 +30,45 @@
#include "lastexpress/debug.h"
-#include "audio/decoders/adpcm.h"
+#include "audio/decoders/adpcm_intern.h"
#include "audio/audiostream.h"
#include "common/memstream.h"
namespace LastExpress {
+// Last Express ADPCM is similar to MS IMA mono, but inverts its nibbles
+// and does not have the 4 byte per channel requirement
+
+class LastExpress_ADPCMStream : public Audio::Ima_ADPCMStream {
+public:
+ LastExpress_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, uint32 blockSize) :
+ Audio::Ima_ADPCMStream(stream, disposeAfterUse, size, 44100, 1, blockSize) {}
+
+ int readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+
+ assert(numSamples % 2 == 0);
+
+ while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+ if (_blockPos[0] == _blockAlign) {
+ // read block header
+ _status.ima_ch[0].last = _stream->readSint16LE();
+ _status.ima_ch[0].stepIndex = _stream->readSint16LE();
+ _blockPos[0] = 4;
+ }
+
+ for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
+ byte data = _stream->readByte();
+ _blockPos[0]++;
+ buffer[samples] = decodeIMA((data >> 4) & 0x0f);
+ buffer[samples + 1] = decodeIMA(data & 0x0f);
+ }
+ }
+
+ return samples;
+ }
+};
+
//////////////////////////////////////////////////////////////////////////
// Sound
//////////////////////////////////////////////////////////////////////////
@@ -60,7 +93,7 @@ void SimpleSound::loadHeader(Common::SeekableReadStream *in) {
}
Audio::AudioStream *SimpleSound::makeDecoder(Common::SeekableReadStream *in, uint32 size) const {
- return Audio::makeADPCMStream(in, DisposeAfterUse::YES, size, Audio::kADPCMMSImaLastExpress, 44100, 1, _blockSize);
+ return new LastExpress_ADPCMStream(in, DisposeAfterUse::YES, size, _blockSize);
}
void SimpleSound::play(Audio::AudioStream *as) {