From 9d8874c7072709c3739efdad317454cdccf82683 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 24 Apr 2011 23:14:14 -0400 Subject: AUDIO: Cleanup MS IMA handling - Split The Last Express' ADPCM to the engine. Using the MS IMA routine was really a hack. - Fixed stereo MS IMA ADPCM, the old routine was completely wrong. --- audio/decoders/adpcm.cpp | 71 ++++++++++++++++------------------------ audio/decoders/adpcm.h | 1 - audio/decoders/adpcm_intern.h | 30 ++++++++++------- engines/lastexpress/data/snd.cpp | 37 +++++++++++++++++++-- 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) { -- cgit v1.2.3