From 876e38ca92cf51dc60bf2240b2c4a09d61ee9b77 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Thu, 11 Nov 2010 17:04:07 +0000 Subject: SOUND: Add DK3 IMA ADPCM decoder for duck videos svn-id: r54204 --- sound/decoders/adpcm.cpp | 92 ++++++++++++++++++++++++++++++++++++++++++++++++ sound/decoders/adpcm.h | 3 +- 2 files changed, 94 insertions(+), 1 deletion(-) diff --git a/sound/decoders/adpcm.cpp b/sound/decoders/adpcm.cpp index 8a27658e4b..4b5e6a5b84 100644 --- a/sound/decoders/adpcm.cpp +++ b/sound/decoders/adpcm.cpp @@ -689,6 +689,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 - @@ -750,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; diff --git a/sound/decoders/adpcm.h b/sound/decoders/adpcm.h index edcdc01ce9..d69387f1eb 100644 --- a/sound/decoders/adpcm.h +++ b/sound/decoders/adpcm.h @@ -58,7 +58,8 @@ enum typesADPCM { kADPCMTinsel6, // 6-bit ADPCM used by the Tinsel engine kADPCMTinsel8, // 8-bit ADPCM used by the Tinsel engine kADPCMIma, // Standard IMA ADPCM - kADPCMApple // Apple QuickTime IMA ADPCM + kADPCMApple, // Apple QuickTime IMA ADPCM + kADPCMDK3 // Duck DK3 IMA ADPCM }; /** -- cgit v1.2.3