diff options
author | Matthew Hoops | 2011-04-25 12:45:33 -0400 |
---|---|---|
committer | Sven Hesse | 2011-11-27 15:52:05 +0100 |
commit | 6bbff583146843baade8f6268a2e1ea92be416ba (patch) | |
tree | e80281c7d7a99a9a2c523e0b6684d17089797280 /video | |
parent | 5518721a17e751a2ac6deefe56ef0ce446f184ab (diff) | |
download | scummvm-rg350-6bbff583146843baade8f6268a2e1ea92be416ba.tar.gz scummvm-rg350-6bbff583146843baade8f6268a2e1ea92be416ba.tar.bz2 scummvm-rg350-6bbff583146843baade8f6268a2e1ea92be416ba.zip |
VIDEO: Rewrite VMD audio streaming
Audio is now decoded in AudioStream classes instead of being decoded ahead of time and then queued.
Diffstat (limited to 'video')
-rw-r--r-- | video/coktel_decoder.cpp | 287 | ||||
-rw-r--r-- | video/coktel_decoder.h | 16 |
2 files changed, 114 insertions, 189 deletions
diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp index 8ea2d08acb..ae0c35cd76 100644 --- a/video/coktel_decoder.cpp +++ b/video/coktel_decoder.cpp @@ -1505,23 +1505,6 @@ VMDDecoder::Frame::~Frame() { delete[] parts; } - -const uint16 VMDDecoder::_tableDPCM[128] = { - 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, - 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, - 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, - 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, - 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, - 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, - 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, - 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, - 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, - 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, - 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, - 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, - 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 -}; - VMDDecoder::VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) : CoktelDecoder(mixer, soundType), _stream(0), _version(0), _flags(0), _frameInfoOffset(0), _partsPerFrame(0), _frames(0), _soundFlags(0), _soundFreq(0), _soundSliceSize(0), _soundSlicesCount(0), @@ -2402,33 +2385,36 @@ void VMDDecoder::blit24(const Graphics::Surface &srcSurf, Common::Rect &rect) { } void VMDDecoder::emptySoundSlice(uint32 size) { - byte *sound = soundEmpty(size); + byte *soundBuf = (byte *)malloc(size); - if (sound) { + if (soundBuf) { uint32 flags = 0; + memset(soundBuf, 0, size); flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; - _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); + _audioStream->queueBuffer(soundBuf, size, DisposeAfterUse::YES, flags); } } void VMDDecoder::filledSoundSlice(uint32 size) { - byte *sound = 0; + if (!_audioStream) { + _stream->skip(size); + return; + } + + Common::SeekableReadStream *data = _stream->readStream(size); + Audio::AudioStream *sliceStream = 0; + if (_audioFormat == kAudioFormat8bitRaw) - sound = sound8bitRaw(size); + sliceStream = create8bitRaw(data); else if (_audioFormat == kAudioFormat16bitDPCM) - sound = sound16bitDPCM(size); + sliceStream = create16bitDPCM(data); else if (_audioFormat == kAudioFormat16bitADPCM) - sound = sound16bitADPCM(size); + sliceStream = create16bitADPCM(data); - if (sound) { - uint32 flags = 0; - flags |= (_soundBytesPerSample == 2) ? Audio::FLAG_16BITS : 0; - flags |= (_soundStereo > 0) ? Audio::FLAG_STEREO : 0; - - _audioStream->queueBuffer(sound, size, DisposeAfterUse::YES, flags); - } + if (sliceStream) + _audioStream->queueAudioStream(sliceStream); } void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) { @@ -2475,173 +2461,120 @@ uint8 VMDDecoder::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) { return n; } -byte *VMDDecoder::soundEmpty(uint32 &size) { - if (!_audioStream) - return 0; +Audio::AudioStream *VMDDecoder::create8bitRaw(Common::SeekableReadStream *stream) { + int flags = Audio::FLAG_UNSIGNED; - byte *soundBuf = (byte *)malloc(size); - memset(soundBuf, 0, size); + if (_soundStereo != 0) + flags |= Audio::FLAG_STEREO; - return soundBuf; + return Audio::makeRawStream(stream, _soundFreq, flags, DisposeAfterUse::YES); } -byte *VMDDecoder::sound8bitRaw(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; +class DPCMStream : public Audio::AudioStream { +public: + DPCMStream(Common::SeekableReadStream *stream, int rate, int channels) { + _stream = stream; + _rate = rate; + _channels = channels; } - byte *soundBuf = (byte *)malloc(size); - _stream->read(soundBuf, size); - unsignedToSigned(soundBuf, size); - - return soundBuf; -} - -byte *VMDDecoder::sound16bitDPCM(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; + ~DPCMStream() { + delete _stream; } - int32 init[2]; + int readBuffer(int16 *buffer, const int numSamples); + bool isStereo() const { return _channels == 2; } + int getRate() const { return _rate; } + bool endOfData() const { return _stream->pos() >= _stream->size() || _stream->eos() || _stream->err(); } - init[0] = _stream->readSint16LE(); - size -= 2; - - if (_soundStereo > 0) { - init[1] = _stream->readSint16LE(); - size -= 2; - } - - byte *data = new byte[size]; - byte *sound = 0; - - if (_stream->read(data, size) == size) - sound = deDPCM(data, size, init); - - delete[] data; +private: + Common::SeekableReadStream *_stream; + int _channels; + int _rate; + int _buffer[2]; +}; - return sound; -} +int DPCMStream::readBuffer(int16 *buffer, const int numSamples) { + static const uint16 tableDPCM[128] = { + 0x0000, 0x0008, 0x0010, 0x0020, 0x0030, 0x0040, 0x0050, 0x0060, 0x0070, 0x0080, + 0x0090, 0x00A0, 0x00B0, 0x00C0, 0x00D0, 0x00E0, 0x00F0, 0x0100, 0x0110, 0x0120, + 0x0130, 0x0140, 0x0150, 0x0160, 0x0170, 0x0180, 0x0190, 0x01A0, 0x01B0, 0x01C0, + 0x01D0, 0x01E0, 0x01F0, 0x0200, 0x0208, 0x0210, 0x0218, 0x0220, 0x0228, 0x0230, + 0x0238, 0x0240, 0x0248, 0x0250, 0x0258, 0x0260, 0x0268, 0x0270, 0x0278, 0x0280, + 0x0288, 0x0290, 0x0298, 0x02A0, 0x02A8, 0x02B0, 0x02B8, 0x02C0, 0x02C8, 0x02D0, + 0x02D8, 0x02E0, 0x02E8, 0x02F0, 0x02F8, 0x0300, 0x0308, 0x0310, 0x0318, 0x0320, + 0x0328, 0x0330, 0x0338, 0x0340, 0x0348, 0x0350, 0x0358, 0x0360, 0x0368, 0x0370, + 0x0378, 0x0380, 0x0388, 0x0390, 0x0398, 0x03A0, 0x03A8, 0x03B0, 0x03B8, 0x03C0, + 0x03C8, 0x03D0, 0x03D8, 0x03E0, 0x03E8, 0x03F0, 0x03F8, 0x0400, 0x0440, 0x0480, + 0x04C0, 0x0500, 0x0540, 0x0580, 0x05C0, 0x0600, 0x0640, 0x0680, 0x06C0, 0x0700, + 0x0740, 0x0780, 0x07C0, 0x0800, 0x0900, 0x0A00, 0x0B00, 0x0C00, 0x0D00, 0x0E00, + 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 + }; + + assert((numSamples % _channels) == 0); + + int samples = 0; + + // Our starting position + if (_stream->pos() == 0) { + for (int i = 0; i < _channels; i++) + *buffer++ = _buffer[i] = _stream->readSint16LE(); + + samples += _channels; + } + + while (!endOfData() && samples < numSamples) { + for (int i = 0; i < _channels; i++) { + byte data = _stream->readByte(); + + if (data & 0x80) + _buffer[i] -= tableDPCM[data & 0x7f]; + else + _buffer[i] += tableDPCM[data]; + + *buffer++ = _buffer[i] = CLIP<int32>(_buffer[i], -32768, 32767); + } -byte *VMDDecoder::sound16bitADPCM(uint32 &size) { - if (!_audioStream) { - _stream->skip(size); - return 0; + samples += _channels; } - int32 init = _stream->readSint16LE(); - size -= 2; - - int32 index = _stream->readByte(); - size--; - - byte *data = new byte[size]; - byte *sound = 0; - - if (_stream->read(data, size) == size) - sound = deADPCM(data, size, init, index); - - delete[] data; - - return sound; + return samples; } -byte *VMDDecoder::deDPCM(const byte *data, uint32 &size, int32 init[2]) { - if (!data || (size == 0)) - return 0; - - int channels = (_soundStereo > 0) ? 2 : 1; - - uint32 inSize = size; - uint32 outSize = size + channels; - - int16 *out = (int16 *)malloc(outSize * 2); - byte *sound = (byte *)out; - - if (!out) - return 0; - - int channel = 0; - - for (int i = 0; i < channels; i++) { - *out++ = TO_BE_16(init[channel]); +Audio::AudioStream *VMDDecoder::create16bitDPCM(Common::SeekableReadStream *stream) { + return new DPCMStream(stream, _soundFreq, (_soundStereo == 0) ? 1 : 2); +} - channel = (channel + 1) % channels; +class VMD_ADPCMStream : public Audio::DVI_ADPCMStream { +public: + VMD_ADPCMStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse, + int rate, int channels) : Audio::DVI_ADPCMStream(stream, disposeAfterUse, stream->size(), rate, channels, 0) { + // FIXME: Using the same predictor/index for two channels probably won't work + // properly However, we have no samples of this, so an assert is here for now. + // Also, since the DPCM stereo has a second predictor, I'm lead to believe + // all VMD with ADPCM are mono unless they changed the code in a later + // revision. + assert(channels == 1); + _startPredictorValue = stream->readSint16LE(); + _startIndexValue = stream->readByte(); + _startpos = 3; + reset(); } - while (inSize-- > 0) { - if (*data & 0x80) - init[channel] -= _tableDPCM[*data++ & 0x7F]; - else - init[channel] += _tableDPCM[*data++]; - - init[channel] = CLIP<int32>(init[channel], -32768, 32767); - *out++ = TO_BE_16(init[channel]); - - channel = (channel + 1) % channels; +protected: + virtual void reset() { + Audio::DVI_ADPCMStream::reset(); + _status.ima_ch[0].last = _startPredictorValue; + _status.ima_ch[0].stepIndex = _startIndexValue; } - size = outSize * 2; - return sound; -} - -// Yet another IMA ADPCM variant -byte *VMDDecoder::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { - if (!data || (size == 0)) - return 0; - - uint32 outSize = size * 2; - - int16 *out = (int16 *)malloc(outSize * 2); - byte *sound = (byte *) out; - - index = CLIP<int32>(index, 0, 88); - - int32 predictor = Audio::Ima_ADPCMStream::_imaTable[index]; - - uint32 dataByte = 0; - bool newByte = true; - - size *= 2; - while (size -- > 0) { - byte code = 0; - - if (newByte) { - dataByte = *data++; - code = (dataByte >> 4) & 0xF; - } else - code = dataByte & 0xF; - - newByte = !newByte; - - index += Audio::ADPCMStream::_stepAdjustTable[code]; - index = CLIP<int32>(index, 0, 88); - - int32 value = predictor / 8; - - if (code & 4) - value += predictor; - if (code & 2) - value += predictor / 2; - if (code & 1) - value += predictor / 4; - - if (code & 8) - init -= value; - else - init += value; - - init = CLIP<int32>(init, -32768, 32767); - - predictor = Audio::Ima_ADPCMStream::_imaTable[index]; - - *out++ = TO_BE_16(init); - } +private: + int32 _startPredictorValue; + int32 _startIndexValue; +}; - size = outSize * 2; - return sound; +Audio::AudioStream *VMDDecoder::create16bitADPCM(Common::SeekableReadStream *stream) { + return new VMD_ADPCMStream(stream, DisposeAfterUse::YES, _soundFreq, (_soundStereo == 0) ? 1 : 2); } Graphics::PixelFormat VMDDecoder::getPixelFormat() const { diff --git a/video/coktel_decoder.h b/video/coktel_decoder.h index 8ad1456037..b99a44f332 100644 --- a/video/coktel_decoder.h +++ b/video/coktel_decoder.h @@ -437,9 +437,6 @@ private: ~Frame(); }; - // Tables for the audio decompressors - static const uint16 _tableDPCM[128]; - Common::SeekableReadStream *_stream; byte _version; @@ -508,15 +505,10 @@ private: uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max); - // Generating sound slices - byte *soundEmpty (uint32 &size); - byte *sound8bitRaw (uint32 &size); - byte *sound16bitDPCM (uint32 &size); - byte *sound16bitADPCM(uint32 &size); - - // Sound decompression - byte *deDPCM (const byte *data, uint32 &size, int32 init[2]); - byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 index); + // Generating audio streams + Audio::AudioStream *create8bitRaw (Common::SeekableReadStream *stream); + Audio::AudioStream *create16bitDPCM (Common::SeekableReadStream *stream); + Audio::AudioStream *create16bitADPCM(Common::SeekableReadStream *stream); bool getPartCoords(int16 frame, PartType type, int16 &x, int16 &y, int16 &width, int16 &height); }; |