diff options
Diffstat (limited to 'sound/decoders/adpcm.cpp')
-rw-r--r-- | sound/decoders/adpcm.cpp | 105 |
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; |