aboutsummaryrefslogtreecommitdiff
path: root/sound/decoders/adpcm.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'sound/decoders/adpcm.cpp')
-rw-r--r--sound/decoders/adpcm.cpp105
1 files changed, 101 insertions, 4 deletions
diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp
index c8a907d13e..4b5e6a5b84 100644
--- a/sound/decoders/adpcm.cpp
+++ b/sound/decoders/adpcm.cpp
@@ -290,8 +290,8 @@ int Apple_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
class MSIma_ADPCMStream : public Ima_ADPCMStream {
public:
- MSIma_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
- : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
+ 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) {
if (blockAlign == 0)
error("ADPCMStream(): blockAlign isn't specified for MS IMA ADPCM");
}
@@ -305,6 +305,9 @@ public:
int readBufferMSIMA1(int16 *buffer, const int numSamples);
int readBufferMSIMA2(int16 *buffer, const int numSamples);
+
+private:
+ bool _invertSamples; // Some implementations invert the way samples are decoded
};
int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
@@ -324,8 +327,8 @@ int MSIma_ADPCMStream::readBufferMSIMA1(int16 *buffer, const int numSamples) {
for (; samples < numSamples && _blockPos[0] < _blockAlign && !_stream->eos() && _stream->pos() < _endpos; samples += 2) {
data = _stream->readByte();
_blockPos[0]++;
- buffer[samples] = decodeIMA(data & 0x0f);
- buffer[samples + 1] = decodeIMA((data >> 4) & 0x0f);
+ buffer[samples] = decodeIMA(_invertSamples ? (data >> 4) & 0x0f : data & 0x0f);
+ buffer[samples + 1] = decodeIMA(_invertSamples ? data & 0x0f : (data >> 4) & 0x0f);
}
}
return samples;
@@ -688,6 +691,96 @@ int Tinsel8_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
#pragma mark -
+// Duck DK3 IMA ADPCM Decoder
+// Based on FFmpeg's decoder and http://wiki.multimedia.cx/index.php?title=Duck_DK3_IMA_ADPCM
+
+class DK3_ADPCMStream : public Ima_ADPCMStream {
+protected:
+
+ void reset() {
+ Ima_ADPCMStream::reset();
+ _topNibble = false;
+ }
+
+public:
+ DK3_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, uint32 size, int rate, int channels, uint32 blockAlign)
+ : Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign) {
+
+ // DK3 only works as a stereo stream
+ assert(channels == 2);
+ _topNibble = false;
+ }
+
+ virtual int readBuffer(int16 *buffer, const int numSamples);
+
+private:
+ byte _nibble, _lastByte;
+ bool _topNibble;
+};
+
+#define DK3_READ_NIBBLE() \
+do { \
+ if (_topNibble) { \
+ _nibble = _lastByte >> 4; \
+ _topNibble = false; \
+ } else { \
+ if (_stream->pos() >= _endpos) \
+ break; \
+ if ((_stream->pos() % _blockAlign) == 0) \
+ continue; \
+ _lastByte = _stream->readByte(); \
+ _nibble = _lastByte & 0xf; \
+ _topNibble = true; \
+ } \
+} while (0)
+
+
+int DK3_ADPCMStream::readBuffer(int16 *buffer, const int numSamples) {
+ int samples = 0;
+
+ assert((numSamples % 4) == 0);
+
+ while (samples < numSamples && !_stream->eos() && _stream->pos() < _endpos) {
+ if ((_stream->pos() % _blockAlign) == 0) {
+ _stream->readUint16LE(); // Unknown
+ uint16 rate = _stream->readUint16LE(); // Copy of rate
+ _stream->skip(6); // Unknown
+ // Get predictor for both sum/diff channels
+ _status.ima_ch[0].last = _stream->readSint16LE();
+ _status.ima_ch[1].last = _stream->readSint16LE();
+ // Get index for both sum/diff channels
+ _status.ima_ch[0].stepIndex = _stream->readByte();
+ _status.ima_ch[1].stepIndex = _stream->readByte();
+
+ if (_stream->eos())
+ break;
+
+ // Sanity check
+ assert(rate == getRate());
+ }
+
+ DK3_READ_NIBBLE();
+ decodeIMA(_nibble, 0);
+
+ DK3_READ_NIBBLE();
+ decodeIMA(_nibble, 1);
+
+ buffer[samples++] = _status.ima_ch[0].last + _status.ima_ch[1].last;
+ buffer[samples++] = _status.ima_ch[0].last - _status.ima_ch[1].last;
+
+ DK3_READ_NIBBLE();
+ decodeIMA(_nibble, 0);
+
+ buffer[samples++] = _status.ima_ch[0].last + _status.ima_ch[1].last;
+ buffer[samples++] = _status.ima_ch[0].last - _status.ima_ch[1].last;
+ }
+
+ return samples;
+}
+
+
+#pragma mark -
+
// adjust the step for use on the next sample.
int16 ADPCMStream::stepAdjust(byte code) {
@@ -733,6 +826,8 @@ 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 kADPCMTinsel4:
@@ -745,6 +840,8 @@ RewindableAudioStream *makeADPCMStream(Common::SeekableReadStream *stream, Dispo
return new Ima_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
case kADPCMApple:
return new Apple_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
+ case kADPCMDK3:
+ return new DK3_ADPCMStream(stream, disposeAfterUse, size, rate, channels, blockAlign);
default:
error("Unsupported ADPCM encoding");
break;