From 44fb798a3af69ec7e983b60011d8153b804168ba Mon Sep 17 00:00:00 2001 From: Sven Hesse Date: Fri, 24 Jul 2009 21:29:41 +0000 Subject: Adding support for ADPCM sound data (yet another IMA ADPCM variant). What we've called ADPCM before is more like DPCM svn-id: r42704 --- graphics/video/coktelvideo/coktelvideo.cpp | 585 ++++++++++++++++++----------- graphics/video/coktelvideo/coktelvideo.h | 39 +- 2 files changed, 399 insertions(+), 225 deletions(-) (limited to 'graphics/video/coktelvideo') diff --git a/graphics/video/coktelvideo/coktelvideo.cpp b/graphics/video/coktelvideo/coktelvideo.cpp index 0681e8261e..6309bc5ae2 100644 --- a/graphics/video/coktelvideo/coktelvideo.cpp +++ b/graphics/video/coktelvideo/coktelvideo.cpp @@ -45,25 +45,26 @@ bool Imd::load(Common::SeekableReadStream &stream) { _stream = &stream; - // Version - uint16 handle = _stream->readUint16LE(); + uint16 handle; + + handle = _stream->readUint16LE(); _version = _stream->readByte(); // Version checking if ((handle != 0) || (_version < 2)) { - warning("IMD Version incorrect (%d,%X)", handle, _version); + warning("Imd::load(): Version incorrect (%d,%X)", handle, _version); unload(); return false; } // Rest header - _features = _stream->readByte(); - _framesCount = _stream->readUint16LE(); - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); - _height = _stream->readSint16LE(); - _flags = _stream->readUint16LE(); + _features = _stream->readByte(); + _framesCount = _stream->readUint16LE(); + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); + _height = _stream->readSint16LE(); + _flags = _stream->readUint16LE(); _firstFramePos = _stream->readUint16LE(); // IMDs always have video @@ -83,9 +84,9 @@ bool Imd::load(Common::SeekableReadStream &stream) { return false; } if (_stdX != 0) { - _stdX = _stream->readSint16LE(); - _stdY = _stream->readSint16LE(); - _stdWidth = _stream->readSint16LE(); + _stdX = _stream->readSint16LE(); + _stdY = _stream->readSint16LE(); + _stdWidth = _stream->readSint16LE(); _stdHeight = _stream->readSint16LE(); _features |= kFeaturesStdCoords; } else @@ -111,8 +112,8 @@ bool Imd::load(Common::SeekableReadStream &stream) { // Sound if (_features & kFeaturesSound) { - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); _soundSlicesCount = _stream->readSint16LE(); if (_soundFreq < 0) @@ -122,7 +123,7 @@ bool Imd::load(Common::SeekableReadStream &stream) { _soundSlicesCount = -_soundSlicesCount - 1; if (_soundSlicesCount > 40) { - warning("IMD: More than 40 sound slices found (%d)", _soundSlicesCount); + warning("Imd::load(): More than 40 sound slices found (%d)", _soundSlicesCount); unload(); return false; } @@ -132,7 +133,7 @@ bool Imd::load(Common::SeekableReadStream &stream) { _frameLength = _soundSliceLength >> 16; _soundStage = 1; - _hasSound = true; + _hasSound = true; _audioStream = Audio::makeAppendableAudioStream(_soundFreq, 0); } else @@ -165,9 +166,9 @@ bool Imd::load(Common::SeekableReadStream &stream) { _frameCoords = new Coord[_framesCount]; assert(_frameCoords); for (int i = 0; i < _framesCount; i++) { - _frameCoords[i].left = _stream->readSint16LE(); - _frameCoords[i].top = _stream->readSint16LE(); - _frameCoords[i].right = _stream->readSint16LE(); + _frameCoords[i].left = _stream->readSint16LE(); + _frameCoords[i].top = _stream->readSint16LE(); + _frameCoords[i].right = _stream->readSint16LE(); _frameCoords[i].bottom = _stream->readSint16LE(); } } @@ -194,7 +195,7 @@ void Imd::setFrameRate(int16 frameRate) { if (frameRate == 0) frameRate = 1; - _frameRate = frameRate; + _frameRate = frameRate; _frameLength = 1000 / _frameRate; } @@ -212,11 +213,11 @@ void Imd::setXY(int16 x, int16 y) { for (int i = 0; i < _framesCount; i++) { if (_frameCoords[i].left != -1) { if (x >= 0) { - _frameCoords[i].left = _frameCoords[i].left - _x + x; + _frameCoords[i].left = _frameCoords[i].left - _x + x; _frameCoords[i].right = _frameCoords[i].right - _x + x; } if (y >= 0) { - _frameCoords[i].top = _frameCoords[i].top - _y + y; + _frameCoords[i].top = _frameCoords[i].top - _y + y; _frameCoords[i].bottom = _frameCoords[i].bottom - _y + y; } } @@ -233,8 +234,8 @@ void Imd::setVideoMemory(byte *vidMem, uint16 width, uint16 height) { deleteVidMem(); _hasOwnVidMem = false; - _vidMem = vidMem; - _vidMemWidth = width; + _vidMem = vidMem; + _vidMemWidth = width; _vidMemHeight = height; } @@ -244,8 +245,8 @@ void Imd::setVideoMemory() { if ((_width > 0) && (_height > 0)) { setXY(0, 0); _hasOwnVidMem = true; - _vidMem = new byte[_width * _height]; - _vidMemWidth = _width; + _vidMem = new byte[_width * _height]; + _vidMemWidth = _width; _vidMemHeight = _height; } } @@ -269,7 +270,7 @@ void Imd::disableSound() { delete _audioStream; _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } _soundEnabled = false; _mixer = 0; @@ -313,7 +314,7 @@ void Imd::seekFrame(int32 frame, int16 whence, bool restart) { for (int i = ((frame > _curFrame) ? _curFrame : 0); i <= frame; i++) processFrame(i); } else - error("Frame %d is not directly accessible", frame); + error("Imd::seekFrame(): Frame %d is not directly accessible", frame); // Seek _stream->seek(framePos); @@ -403,8 +404,9 @@ void Imd::deleteVidMem(bool del) { } _hasOwnVidMem = false; - _vidMem = 0; - _vidMemWidth = _vidMemHeight = 0; + _vidMem = 0; + _vidMemWidth = 0; + _vidMemHeight = 0; } void Imd::clear(bool del) { @@ -419,39 +421,41 @@ void Imd::clear(bool del) { _stream = 0; - _version = 0; + _version = 0; _features = 0; - _flags = 0; - _x = _y = _width = _height = 0; + _flags = 0; + + _x = _y = _width = _height = 0; _stdX = _stdY = _stdWidth = _stdHeight = 0; + _framesCount = _curFrame = 0; - _framesPos = 0; + + _framesPos = 0; _firstFramePos = 0; - _frameCoords = 0; + _frameCoords = 0; _frameDataSize = _vidBufferSize = 0; - _frameData = _vidBuffer = 0; - _frameDataLen = 0; + _frameData = _vidBuffer = 0; + _frameDataLen = 0; memset(_palette, 0, 768); deleteVidMem(del); - _hasSound = false; + _hasSound = false; _soundEnabled = false; - _soundStage = 0; - _skipFrames = 0; + _soundStage = 0; + _skipFrames = 0; - _soundFlags = 0; - _soundFreq = 0; - _soundSliceSize = 0; + _soundFlags = 0; + _soundFreq = 0; + _soundSliceSize = 0; _soundSlicesCount = 0; _soundSliceLength = 0; + _audioStream = 0; - _audioStream = 0; - - _frameRate = 12; - _frameLength = 0; + _frameRate = 12; + _frameLength = 0; _lastFrameTime = 0; } @@ -474,25 +478,25 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { if (!_vidMem) setVideoMemory(); - state.left = _x; - state.top = _y; - state.right = _width + state.left - 1; - state.bottom = _height + state.top - 1; + state.left = _x; + state.top = _y; + state.right = _width + state.left - 1; + state.bottom = _height + state.top - 1; do { if (frame != 0) { if (_stdX != -1) { - state.left = _stdX; - state.top = _stdY; - state.right = _stdWidth + state.left - 1; - state.bottom = _stdHeight + state.top - 1; + state.left = _stdX; + state.top = _stdY; + state.right = _stdWidth + state.left - 1; + state.bottom = _stdHeight + state.top - 1; state.flags |= kStateStdCoords; } if (_frameCoords && (_frameCoords[frame].left != -1)) { - state.left = _frameCoords[frame].left; - state.top = _frameCoords[frame].top; - state.right = _frameCoords[frame].right; + state.left = _frameCoords[frame].left; + state.top = _frameCoords[frame].top; + state.right = _frameCoords[frame].right; state.bottom = _frameCoords[frame].bottom; state.flags |= kStateFrameCoords; } @@ -525,8 +529,8 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { if (_soundStage != 0) { byte *soundBuf; - // Next sound slice data if (cmd == 0xFF00) { + // Next sound slice data if (!hasNextCmd && _soundEnabled) { soundBuf = new byte[_soundSliceSize]; @@ -540,8 +544,9 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { cmd = _stream->readUint16LE(); - // Initial sound data (all slices) } else if (cmd == 0xFF01) { + // Initial sound data (all slices) + int dataLength = _soundSliceSize * _soundSlicesCount; if (!hasNextCmd && _soundEnabled) { @@ -560,8 +565,9 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { cmd = _stream->readUint16LE(); - // Empty sound slice } else if (!hasNextCmd && (_soundEnabled)) { + // Empty sound slice + soundBuf = new byte[_soundSliceSize]; assert(soundBuf); @@ -602,13 +608,13 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { _frameDataLen = cmd + 2; if (_vidMemWidth <= state.right) { - state.left = 0; + state.left = 0; state.right -= state.left; } if (_vidMemWidth <= state.right) state.right = _vidMemWidth - 1; if (_vidMemHeight <= state.bottom) { - state.top = 0; + state.top = 0; state.bottom -= state.top; } if (_vidMemHeight <= state.bottom) @@ -642,7 +648,7 @@ CoktelVideo::State Imd::processFrame(uint16 frame) { _audioStream->finish(); _mixer->stopHandle(_audioHandle); _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } _lastFrameTime = g_system->getMillis(); @@ -654,12 +660,15 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { return 0; uint32 retVal = 0; - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - byte *dataPtr = _frameData; + + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + + byte *dataPtr = _frameData; byte *imdVidMem = _vidMem + sW * top + left; byte *srcPtr; + uint8 type = *dataPtr++; if (type & 0x10) { // Palette data @@ -691,7 +700,7 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { if (type == 2) { // Whole block for (int i = 0; i < height; i++) { memcpy(imdVidMem, srcPtr, width); - srcPtr += width; + srcPtr += width; imdVidMem += sW; } } else if (type == 1) { // Sparse block @@ -705,16 +714,16 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { memcpy(imdVidMem, srcPtr, pixCount); pixWritten += pixCount; - imdVidMem += pixCount; - srcPtr += pixCount; + imdVidMem += pixCount; + srcPtr += pixCount; } else { // "Hole" - pixCount = (pixCount + 1) % 256; + pixCount = (pixCount + 1) % 256; pixWritten += pixCount; - imdVidMem += pixCount; + imdVidMem += pixCount; } } imdVidMemBak += sW; - imdVidMem = imdVidMemBak; + imdVidMem = imdVidMemBak; } } else if (type == 0x42) { // Whole quarter-wide block for (int i = 0; i < height; i++) { @@ -724,7 +733,7 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { memset(imdVidMem, *srcPtr, 4); imdVidMemBak += sW; - imdVidMem = imdVidMemBak; + imdVidMem = imdVidMemBak; } } else if ((type & 0xF) == 2) { // Whole half-high block for (; height > 1; height -= 2, imdVidMem += sW + sW, srcPtr += width) { @@ -745,16 +754,16 @@ uint32 Imd::renderFrame(int16 left, int16 top, int16 right, int16 bottom) { memcpy(imdVidMem + sW, srcPtr, pixCount); pixWritten += pixCount; - imdVidMem += pixCount; - srcPtr += pixCount; + imdVidMem += pixCount; + srcPtr += pixCount; } else { // "Hole" - pixCount = (pixCount + 1) % 256; + pixCount = (pixCount + 1) % 256; pixWritten += pixCount; - imdVidMem += pixCount; + imdVidMem += pixCount; } } imdVidMemBak += sW + sW; - imdVidMem = imdVidMemBak; + imdVidMem = imdVidMemBak; } } @@ -848,7 +857,7 @@ void Imd::deLZ77(byte *dest, byte *src) { } } -const uint16 Vmd::_tableADPCM[128] = { +const uint16 Vmd::_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, @@ -864,6 +873,26 @@ const uint16 Vmd::_tableADPCM[128] = { 0x0F00, 0x1000, 0x1400, 0x1800, 0x1C00, 0x2000, 0x3000, 0x4000 }; +const int32 Vmd::_tableADPCM[] = { + 7, 8, 9, 10, 11, 12, 13, 14, + 16, 17, 19, 21, 23, 25, 28, 31, + 34, 37, 41, 45, 50, 55, 60, 66, + 73, 80, 88, 97, 107, 118, 130, 143, + 157, 173, 190, 209, 230, 253, 279, 307, + 337, 371, 408, 449, 494, 544, 598, 658, + 724, 796, 876, 963, 1060, 1166, 1282, 1411, + 1552, 1707, 1878, 2066, 2272, 2499, 2749, 3024, + 3327, 3660, 4026, 4428, 4871, 5358, 5894, 6484, + 7132, 7845, 8630, 9493, 10442, 11487, 12635, 13899, + 15289, 16818, 18500, 20350, 22385, 24623, 27086, 29794, + 32767, 0 +}; + +const int32 Vmd::_tableADPCMStep[] = { + -1, -1, -1, -1, 2, 4, 6, 8, + -1, -1, -1, -1, 2, 4, 6, 8 +}; + Vmd::Vmd(Graphics::PaletteLUT *palLUT) : _palLUT(palLUT) { clear(false); } @@ -877,40 +906,40 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _stream = &stream; - uint16 headerLength = _stream->readUint16LE(); - uint16 handle = _stream->readUint16LE(); - _version = _stream->readUint16LE(); + uint16 headerLength; + uint16 handle; + + headerLength = _stream->readUint16LE(); + handle = _stream->readUint16LE(); + _version = _stream->readUint16LE(); if (!(_version & 2)) _features |= kFeaturesPalette; else _features |= kFeaturesFullColor; - // 0x4 (4) - // Version checking if (headerLength != 814) { - warning("VMD Version incorrect (%d, %d, %d)", headerLength, handle, _version); + warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); unload(); return false; } _framesCount = _stream->readUint16LE(); - // 0x6 (6) - - _x = _stream->readSint16LE(); - _y = _stream->readSint16LE(); - _width = _stream->readSint16LE(); + _x = _stream->readSint16LE(); + _y = _stream->readSint16LE(); + _width = _stream->readSint16LE(); _height = _stream->readSint16LE(); - // 0xE (14) - if ((_width != 0) && (_height != 0)) { + _hasVideo = true; _features |= kFeaturesVideo; + if (_features & kFeaturesFullColor) _codecIndeo3 = new Indeo3(_width, _height, _palLUT); + } else _hasVideo = false; @@ -922,7 +951,7 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } if (handle > 2) { - warning("VMD Version incorrect (%d, %d, %d)", headerLength, handle, _version); + warning("Vmd::load(): Version incorrect (%d, %d, %d)", headerLength, handle, _version); unload(); return false; } @@ -930,7 +959,7 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _bytesPerPixel = handle + 1; if (_bytesPerPixel > 1) { - _features |= kFeaturesFullColor; + _features |= kFeaturesFullColor; _features &= ~kFeaturesPalette; } @@ -940,12 +969,8 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _firstFramePos = _stream->readUint32LE(); _stream->skip(4); // Unknown - // 0x1A (26) - _stream->read((byte *) _palette, 768); - // 0x31A (794) - _frameDataSize = _stream->readUint32LE(); _vidBufferSize = _stream->readUint32LE(); @@ -957,7 +982,7 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } else _externalCodec = false; - _preScaleX = 1; + _preScaleX = 1; _postScaleX = 1; if (_externalCodec) @@ -970,10 +995,10 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _blitMode = n - 1; if (_bytesPerPixel == 2) { - _preScaleX = n; + _preScaleX = n; _postScaleX = 1; } else if (_bytesPerPixel == 3) { - _preScaleX = 1; + _preScaleX = 1; _postScaleX = n; } @@ -984,8 +1009,6 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (!_externalCodec && !(_flags & 0x1000)) _scaleExternalX = _bytesPerPixel; - // 0x322 (802) - if (_hasVideo) { if ((_frameDataSize == 0) || (_frameDataSize > 1048576)) _frameDataSize = _width * _height + 1000; @@ -1009,27 +1032,41 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (_externalCodec && _codecIndeo3) _features |= kFeaturesSupportsDouble; - _soundFreq = _stream->readSint16LE(); - _soundSliceSize = _stream->readSint16LE(); + _soundFreq = _stream->readSint16LE(); + _soundSliceSize = _stream->readSint16LE(); _soundSlicesCount = _stream->readSint16LE(); - _soundFlags = _stream->readUint16LE(); - _hasSound = (_soundFreq != 0); + _soundFlags = _stream->readUint16LE(); - // 0x32A (810) + _hasSound = (_soundFreq != 0); if (_hasSound) { _features |= kFeaturesSound; _soundStereo = (_soundFlags & 0x8000) ? 1 : ((_soundFlags & 0x200) ? 2 : 0); if (_soundStereo > 0) { - warning("TODO: VMD stereo"); + warning("Vmd::load(): TODO: Stereo sound"); unload(); return false; } if (_soundSliceSize < 0) { _soundBytesPerSample = 2; - _soundSliceSize = -_soundSliceSize; + _soundSliceSize = -_soundSliceSize; + + if (_soundFlags & 0x10) { + _audioFormat = kAudioFormat16bitADPCM; + _soundHeaderSize = 3; + _soundDataSize = _soundSliceSize >> 1; + } else { + _audioFormat = kAudioFormat16bitDPCM; + _soundHeaderSize = 1; + _soundDataSize = _soundSliceSize; + } + } else { + _soundBytesPerSample = 1; + _audioFormat = kAudioFormat8bitDirect; + _soundHeaderSize = 0; + _soundDataSize = _soundSliceSize; } _soundSliceLength = (uint32) (((double) (1000 << 16)) / @@ -1053,14 +1090,15 @@ bool Vmd::load(Common::SeekableReadStream &stream) { _stream->skip(2); // Unknown _frames[i].offset = _stream->readUint32LE(); } + for (uint16 i = 0; i < _framesCount; i++) { bool separator = false; for (uint16 j = 0; j < _partsPerFrame; j++) { - _frames[i].parts[j].type = (PartType) _stream->readByte(); + _frames[i].parts[j].type = (PartType) _stream->readByte(); _frames[i].parts[j].field_1 = _stream->readByte(); - _frames[i].parts[j].size = _stream->readUint32LE(); + _frames[i].parts[j].size = _stream->readUint32LE(); if (_frames[i].parts[j].type == kPartTypeAudio) { @@ -1069,12 +1107,12 @@ bool Vmd::load(Common::SeekableReadStream &stream) { } else if (_frames[i].parts[j].type == kPartTypeVideo) { - _frames[i].parts[j].left = _stream->readUint16LE(); - _frames[i].parts[j].top = _stream->readUint16LE(); - _frames[i].parts[j].right = _stream->readUint16LE(); - _frames[i].parts[j].bottom = _stream->readUint16LE(); + _frames[i].parts[j].left = _stream->readUint16LE(); + _frames[i].parts[j].top = _stream->readUint16LE(); + _frames[i].parts[j].right = _stream->readUint16LE(); + _frames[i].parts[j].bottom = _stream->readUint16LE(); _frames[i].parts[j].field_E = _stream->readByte(); - _frames[i].parts[j].flags = _stream->readByte(); + _frames[i].parts[j].flags = _stream->readByte(); } else if (_frames[i].parts[j].type == kPartTypeExtraData) { if (!separator) @@ -1111,9 +1149,10 @@ bool Vmd::load(Common::SeekableReadStream &stream) { if (_frames[i].parts[j].type == kPartTypeExtraData) { ExtraData data; - data.offset = _stream->pos() + 20; - data.size = _frames[i].parts[j].size; + data.offset = _stream->pos() + 20; + data.size = _frames[i].parts[j].size; data.realSize = _stream->readUint32LE(); + _stream->read(data.name, 16); data.name[15] = '\0'; @@ -1150,11 +1189,11 @@ void Vmd::setXY(int16 x, int16 y) { if (_frames[i].parts[j].type == kPartTypeVideo) { if (x >= 0) { - _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; + _frames[i].parts[j].left = _frames[i].parts[j].left - _x + x; _frames[i].parts[j].right = _frames[i].parts[j].right - _x + x; } if (y >= 0) { - _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; + _frames[i].parts[j].top = _frames[i].parts[j].top - _y + y; _frames[i].parts[j].bottom = _frames[i].parts[j].bottom - _y + y; } } @@ -1252,16 +1291,19 @@ void Vmd::clear(bool del) { _extraData.clear(); _soundBytesPerSample = 1; - _soundStereo = 0; - - _externalCodec = false; - _doubleMode = false; - _blitMode = 0; - _bytesPerPixel = 1; - _preScaleX = 1; - _postScaleX = 1; + _soundStereo = 0; + _soundHeaderSize = 0; + _soundDataSize = 0; + _audioFormat = kAudioFormat8bitDirect; + + _externalCodec = false; + _doubleMode = false; + _blitMode = 0; + _bytesPerPixel = 1; + _preScaleX = 1; + _postScaleX = 1; _scaleExternalX = 1; - _vidMemBuffer = 0; + _vidMemBuffer = 0; } CoktelVideo::State Vmd::processFrame(uint16 frame) { @@ -1271,9 +1313,9 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { seekFrame(frame); state.flags |= kStateNoVideoData; - state.left = 0x7FFF; - state.top = 0x7FFF; - state.right = 0; + state.left = 0x7FFF; + state.top = 0x7FFF; + state.right = 0; state.bottom = 0; if (!_vidMem) @@ -1312,7 +1354,7 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { } else if (part.flags == 3) { if (_soundEnabled) { - emptySoundSlice(_soundSliceSize * _soundBytesPerSample); + emptySoundSlice(_soundDataSize * _soundBytesPerSample); if (_soundStage == 1) startSound = true; @@ -1320,12 +1362,12 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { _stream->skip(part.size); } else { - warning("Unknown sound part type %d", part.flags); + warning("Vmd::processFrame(): Unknown sound type %d", part.flags); _stream->skip(part.size); } } else if ((part.type == kPartTypeVideo) && !_hasVideo) { - warning("Header claims there's no video, but video frame part found"); + warning("Vmd::processFrame(): Header claims there's no video, but video found"); } else if ((part.type == kPartTypeVideo) && _hasVideo) { state.flags &= ~kStateNoVideoData; @@ -1360,12 +1402,17 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { state.bottom = MAX(state.bottom, b); } - } else if (part.type == 4) { + } else if (part.type == kPartTypeSeparator) { + } else if (part.type == kPartTypeExtraData) { + _stream->skip(part.size); + } else if (part.type == kPartType4) { // Unknown _stream->skip(part.size); + } else if (part.type == kPartTypeSpeech) { + // Always triggers when speech starts + _stream->skip(part.size); } else { - // Unknow type -// warning("Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); + warning("Vmd::processFrame(): Unknown frame part type %d, size %d (%d of %d)", part.type, part.size, i + 1, _partsPerFrame); } } @@ -1379,7 +1426,7 @@ CoktelVideo::State Vmd::processFrame(uint16 frame) { _audioStream->finish(); _mixer->stopHandle(_audioHandle); _audioStream = 0; - _soundStage = 0; + _soundStage = 0; } // If these are still 0x7FFF, no video data has been processed @@ -1421,19 +1468,20 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { if (!_frameData || !_vidMem || (_width <= 0) || (_height <= 0)) return 0; - int16 width = right - left + 1; - int16 height = bottom - top + 1; - int16 sW = _vidMemWidth; - int16 sH = _vidMemHeight; + int16 width = right - left + 1; + int16 height = bottom - top + 1; + int16 sW = _vidMemWidth; + int16 sH = _vidMemHeight; uint32 dataLen = _frameDataLen; - byte *dataPtr = _frameData; + + byte *dataPtr = _frameData; byte *imdVidMem = _vidMem + sW * top + left; byte *srcPtr; - uint8 type; if ((width < 0) || (height < 0)) return 1; + uint8 type; byte *dest = imdVidMem; if (Indeo3::isIndeo3(dataPtr, dataLen)) { @@ -1444,12 +1492,12 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { width * (_doubleMode ? 2 : 1), height * (_doubleMode ? 2 : 1))) return 0; - type = 2; + type = 2; srcPtr = _vidBuffer; - width = _width * (_doubleMode ? 2 : 1); + width = _width * (_doubleMode ? 2 : 1); height = _height * (_doubleMode ? 2 : 1); - right = left + width - 1; - bottom = top + height - 1; + right = left + width - 1; + bottom = top + height - 1; } else { @@ -1458,8 +1506,8 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { return 0; } - type = *dataPtr++; - srcPtr = dataPtr; + type = *dataPtr++; + srcPtr = dataPtr; if (_blitMode > 0) { dest = _vidMemBuffer + postScaleX(_width) * (top - _y) + postScaleX((left - _x)); @@ -1495,12 +1543,12 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { memcpy(dest, srcPtr, pixCount); pixWritten += pixCount; - dest += pixCount; - srcPtr += pixCount; + dest += pixCount; + srcPtr += pixCount; } else { // "Hole" - pixCount = (pixCount + 1) % 256; + pixCount = (pixCount + 1) % 256; pixWritten += pixCount; - dest += pixCount; + dest += pixCount; } } destBak += sW; @@ -1516,7 +1564,7 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { dest[j] = srcPtr[j]; srcPtr += postScaleX(width); - dest += sW; + dest += sW; } } else if (type == 3) { // RLE block @@ -1531,14 +1579,14 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { if (*srcPtr != 0xFF) { // Normal copy memcpy(dest, srcPtr, pixCount); - dest += pixCount; + dest += pixCount; srcPtr += pixCount; } else deRLE(srcPtr, dest, pixCount); pixWritten += pixCount; } else { // "Hole" - dest += pixCount + 1; + dest += pixCount + 1; pixWritten += pixCount + 1; } @@ -1554,7 +1602,7 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { memset(dest, *srcPtr, 4); destBak += sW; - dest = destBak; + dest = destBak; } } else if ((type & 0xF) == 2) { // Whole half-high block for (; height > 1; height -= 2, dest += sW + sW, srcPtr += width) { @@ -1575,16 +1623,16 @@ uint32 Vmd::renderFrame(int16 &left, int16 &top, int16 &right, int16 &bottom) { memcpy(dest + sW, srcPtr, pixCount); pixWritten += pixCount; - dest += pixCount; - srcPtr += pixCount; + dest += pixCount; + srcPtr += pixCount; } else { // "Hole" - pixCount = (pixCount + 1) % 256; + pixCount = (pixCount + 1) % 256; pixWritten += pixCount; - dest += pixCount; + dest += pixCount; } } destBak += sW + sW; - dest = destBak; + dest = destBak; } } @@ -1675,58 +1723,178 @@ void Vmd::blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 heigh dither->nextLine(); dest += _vidMemWidth; - src += 3 * srcPitch; + src += 3 * srcPitch; } delete dither; } -void Vmd::emptySoundSlice(uint32 size) { - if (!_audioStream) - return; +byte *Vmd::deDPCM(const byte *data, uint32 &size, int32 init) { + if (!data || (size == 0)) + return 0; - byte *soundBuf = new byte[size]; - assert(soundBuf); + uint32 inSize = size; + uint32 outSize = size * 2; - memset(soundBuf, 0, size); + byte *sound = new byte[outSize]; + + int16 *out = (int16 *) sound; - _audioStream->queueBuffer(soundBuf, size); + while (inSize-- > 0) { + if (*data & 0x80) + init -= _tableDPCM[*data++ & 0x7F]; + else + init += _tableDPCM[*data++]; + + init = CLIP(init, -32768, 32767); + *out++ = TO_BE_16(init); + } + + size = outSize; + return sound; +} + +// Yet another IMA ADPCM variant +byte *Vmd::deADPCM(const byte *data, uint32 &size, int32 init, int32 index) { + if (!data || (size == 0)) + return 0; + + uint32 outSize = size * 4; + + byte *sound = new byte[outSize]; + int16 *out = (int16 *) sound; + + index = CLIP(index, 0, 88); + + int32 predictor = _tableADPCM[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 += _tableADPCMStep[code]; + index = CLIP(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(init, -32768, 32767); + + predictor = _tableADPCM[index]; + + *out++ = TO_BE_16(init); + } + + size = outSize; + return sound; } -void Vmd::soundSlice8bit(uint32 size) { +byte *Vmd::soundEmpty(uint32 &size) { if (!_audioStream) - return; + return 0; byte *soundBuf = new byte[size]; - assert(soundBuf); + memset(soundBuf, 0, size); + return soundBuf; +} + +byte *Vmd::sound8bitDirect(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + byte *soundBuf = new byte[size]; _stream->read(soundBuf, size); unsignedToSigned(soundBuf, size); - _audioStream->queueBuffer(soundBuf, size); + return soundBuf; } -void Vmd::soundSlice16bit(uint32 size, int16 &init) { - if (!_audioStream) - return; +byte *Vmd::sound16bitDPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; + } + + int32 init = _stream->readSint16LE(); + size -= 2; + + byte *data = new byte[size]; + byte *sound = 0; - byte *dataBuf = new byte[size]; - byte *soundBuf = new byte[size * 2]; + if (_stream->read(data, size) == size) + sound = deDPCM(data, size, init); - _stream->read(dataBuf, size); - deADPCM(soundBuf, dataBuf, init, size); - _audioStream->queueBuffer(soundBuf, size * 2); + delete[] data; - delete[] dataBuf; + return sound; } -void Vmd::filledSoundSlice(uint32 size) { - if (_soundBytesPerSample == 1) { - soundSlice8bit(size); - } else if (_soundBytesPerSample == 2) { - int16 init = _stream->readSint16LE(); - soundSlice16bit(size - 2, init); +byte *Vmd::sound16bitADPCM(uint32 &size) { + if (!_audioStream) { + _stream->skip(size); + return 0; } + + int32 init = _stream->readSint16LE(); + size -= 2; + + int32 v28 = _stream->readByte(); + size--; + + byte *data = new byte[size]; + byte *sound = 0; + + if (_stream->read(data, size) == size) + sound = deADPCM(data, size, init, v28); + + delete[] data; + + return sound; +} + +void Vmd::emptySoundSlice(uint32 size) { + byte *sound = soundEmpty(size); + + if (sound) + _audioStream->queueBuffer(sound, size); +} + +void Vmd::filledSoundSlice(uint32 size) { + byte *sound = 0; + if (_audioFormat == kAudioFormat8bitDirect) + sound = sound8bitDirect(size); + else if (_audioFormat == kAudioFormat16bitDPCM) + sound = sound16bitDPCM(size); + else if (_audioFormat == kAudioFormat16bitADPCM) + sound = sound16bitADPCM(size); + + if (sound) + _audioStream->queueBuffer(sound, size); } void Vmd::filledSoundSlices(uint32 size, uint32 mask) { @@ -1734,29 +1902,14 @@ void Vmd::filledSoundSlices(uint32 size, uint32 mask) { for (int i = 0; i < n; i++) { if (mask & 1) - emptySoundSlice(_soundSliceSize * _soundBytesPerSample); + emptySoundSlice(_soundDataSize * _soundBytesPerSample); else - filledSoundSlice(_soundSliceSize + 1); + filledSoundSlice(_soundDataSize + _soundHeaderSize); mask >>= 1; } if (_soundSlicesCount > 32) - filledSoundSlice((_soundSlicesCount - 32) * _soundSliceSize); -} - -void Vmd::deADPCM(byte *soundBuf, byte *dataBuf, int16 &init, uint32 n) { - int16 *out = (int16 *) soundBuf; - - int32 s = init; - for (uint32 i = 0; i < n; i++) { - if (dataBuf[i] & 0x80) - s -= _tableADPCM[dataBuf[i] & 0x7F]; - else - s += _tableADPCM[dataBuf[i]]; - - s = CLIP(s, -32768, 32767); - *out++ = TO_BE_16(s); - } + filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize); } bool Vmd::getAnchor(int16 frame, uint16 partType, @@ -1789,9 +1942,9 @@ bool Vmd::getAnchor(int16 frame, uint16 partType, } _stream->skip(5); - x = _stream->readSint16LE(); - y = _stream->readSint16LE(); - width = _stream->readSint16LE() - x + 1; + x = _stream->readSint16LE(); + y = _stream->readSint16LE(); + width = _stream->readSint16LE() - x + 1; height = _stream->readSint16LE() - y + 1; _stream->seek(pos); diff --git a/graphics/video/coktelvideo/coktelvideo.h b/graphics/video/coktelvideo/coktelvideo.h index 58b56e18ec..50fec4c787 100644 --- a/graphics/video/coktelvideo/coktelvideo.h +++ b/graphics/video/coktelvideo/coktelvideo.h @@ -335,16 +335,26 @@ public: protected: enum PartType { kPartTypeSeparator = 0, - kPartTypeAudio = 1, - kPartTypeVideo = 2, - kPartTypeExtraData = 3 + kPartTypeAudio = 1, + kPartTypeVideo = 2, + kPartTypeExtraData = 3, + kPartType4 = 4, + kPartTypeSpeech = 5 }; + + enum AudioFormat { + kAudioFormat8bitDirect = 0, + kAudioFormat16bitDPCM = 1, + kAudioFormat16bitADPCM = 2 + }; + struct ExtraData { char name[16]; uint32 offset; uint32 size; uint32 realSize; } PACKED_STRUCT; + struct Part { PartType type; byte field_1; @@ -356,6 +366,7 @@ protected: int16 bottom; byte flags; } PACKED_STRUCT; + struct Frame { uint32 offset; Part *parts; @@ -364,7 +375,9 @@ protected: ~Frame() { delete[] parts; } } PACKED_STRUCT; - static const uint16 _tableADPCM[128]; + static const uint16 _tableDPCM[128]; + static const int32 _tableADPCM[]; + static const int32 _tableADPCMStep[]; bool _hasVideo; @@ -374,8 +387,11 @@ protected: Common::Array _extraData; - byte _soundBytesPerSample; - byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + byte _soundBytesPerSample; + byte _soundStereo; // (0: mono, 1: old-style stereo, 2: new-style stereo) + uint32 _soundHeaderSize; + uint32 _soundDataSize; + AudioFormat _audioFormat; bool _externalCodec; byte _blitMode; @@ -404,12 +420,17 @@ protected: void blit16(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); void blit24(byte *dest, byte *src, int16 srcPitch, int16 width, int16 height); + byte *deDPCM(const byte *data, uint32 &size, int32 init); + byte *deADPCM(const byte *data, uint32 &size, int32 init, int32 v28); + + byte *soundEmpty(uint32 &size); + byte *sound8bitDirect(uint32 &size); + byte *sound16bitDPCM(uint32 &size); + byte *sound16bitADPCM(uint32 &size); + void emptySoundSlice(uint32 size); - void soundSlice8bit(uint32 size); - void soundSlice16bit(uint32 size, int16 &init); void filledSoundSlice(uint32 size); void filledSoundSlices(uint32 size, uint32 mask); - void deADPCM(byte *soundBuf, byte *dataBuf, int16 &init, uint32 n); }; } // End of namespace Graphics -- cgit v1.2.3