[Scummvm-git-logs] scummvm master -> 4ba87013a9d9d50a2866fd23aaf39e546bff9b7b

csnover csnover at users.noreply.github.com
Sun Aug 20 18:36:45 CEST 2017


This automated email contains information about 2 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .

Summary:
1d844978d6 COMMON: Implement SeekableReadStream interface for MemoryReadWriteStream
4ba87013a9 VIDEO: Support old-style stereo in VMDs


Commit: 1d844978d675e1c4d875cc86c9ac86ad0fef04cb
    https://github.com/scummvm/scummvm/commit/1d844978d675e1c4d875cc86c9ac86ad0fef04cb
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-08-20T11:36:14-05:00

Commit Message:
COMMON: Implement SeekableReadStream interface for MemoryReadWriteStream

This allows MemoryReadWriteStream to be passed successfully to
functions that use the SeekableReadStream type so that they can
call the `pos` method, like the DPCMStream class of the VMD
decoder.

Changed paths:
    common/memstream.h


diff --git a/common/memstream.h b/common/memstream.h
index 25fdde9..0338d35 100644
--- a/common/memstream.h
+++ b/common/memstream.h
@@ -212,13 +212,14 @@ public:
 /**
 * MemoryStream based on RingBuffer. Grows if has insufficient buffer size.
 */
-class MemoryReadWriteStream : public WriteStream {
+class MemoryReadWriteStream : public SeekableReadStream, public WriteStream {
 private:
 	uint32 _capacity;
 	uint32 _size;
 	byte *_data;
 	uint32 _writePos, _readPos, _pos, _length;
 	DisposeAfterUse::Flag _disposeMemory;
+	bool _eos;
 
 	void ensureCapacity(uint32 new_len) {
 		if (new_len <= _capacity)
@@ -246,7 +247,7 @@ private:
 		}
 	}
 public:
-	MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory) {}
+	MemoryReadWriteStream(DisposeAfterUse::Flag disposeMemory = DisposeAfterUse::NO) : _capacity(0), _size(0), _data(0), _writePos(0), _readPos(0), _pos(0), _length(0), _disposeMemory(disposeMemory), _eos(false) {}
 
 	~MemoryReadWriteStream() {
 		if (_disposeMemory)
@@ -271,8 +272,10 @@ public:
 	}
 
 	virtual uint32 read(void *dataPtr, uint32 dataSize) {
-		uint32 length = _length;
-		if (length < dataSize) dataSize = length;
+		if (_length < dataSize) {
+			dataSize = _length;
+			_eos = true;
+		}
 		if (dataSize == 0 || _capacity == 0) return 0;
 		if (_readPos + dataSize < _capacity) {
 			memcpy(dataPtr, _data + _readPos, dataSize);
@@ -287,7 +290,10 @@ public:
 	}
 
 	int32 pos() const { return _pos - _length; } //'read' position in the stream
-	uint32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+	int32 size() const { return _size; } //that's also 'write' position in the stream, as it's append-only
+	bool seek(int32, int) { return false; }
+	bool eos() const { return _eos; }
+	void clearErr() { _eos = false; }
 
 	byte *getData() { return _data; }
 };


Commit: 4ba87013a9d9d50a2866fd23aaf39e546bff9b7b
    https://github.com/scummvm/scummvm/commit/4ba87013a9d9d50a2866fd23aaf39e546bff9b7b
Author: Colin Snover (github.com at zetafleet.com)
Date: 2017-08-20T11:36:26-05:00

Commit Message:
VIDEO: Support old-style stereo in VMDs

This format is used by the stereo audio VMDs in Lighthouse.

Changed paths:
    video/coktel_decoder.cpp
    video/coktel_decoder.h


diff --git a/video/coktel_decoder.cpp b/video/coktel_decoder.cpp
index 245319a..981dd82 100644
--- a/video/coktel_decoder.cpp
+++ b/video/coktel_decoder.cpp
@@ -1543,6 +1543,93 @@ Graphics::PixelFormat IMDDecoder::getPixelFormat() const {
 	return Graphics::PixelFormat::createFormatCLUT8();
 }
 
+class DPCMStream : public Audio::AudioStream {
+public:
+	DPCMStream(Common::SeekableReadStream *stream, int rate, int channels, bool oldStereo) {
+		_stream = stream;
+		_rate = rate;
+		_channels = channels;
+		_oldStereo = oldStereo;
+		if (oldStereo) {
+			_buffer[0] = _buffer[1] = 0;
+		}
+	}
+
+	~DPCMStream() {
+		delete _stream;
+	}
+
+	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(); }
+
+private:
+	Common::SeekableReadStream *_stream;
+	int _channels;
+	int _rate;
+	int _buffer[2];
+	bool _oldStereo;
+};
+
+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 (!_oldStereo && _stream->pos() == 0) {
+		for (int i = 0; i < _channels; i++)
+			*buffer++ = _buffer[i] = _stream->readSint16LE();
+
+		samples += _channels;
+	}
+
+	while (!endOfData() && samples < numSamples) {
+		if (_channels == 2 && _stream->size() == 1) {
+			warning("Buffer underrun in DPCMStream");
+			break;
+		}
+
+		for (int i = 0; i < _channels; i++) {
+			byte data = _stream->readByte();
+
+			if (data & 0x80)
+				_buffer[i] -= tableDPCM[data & 0x7f];
+			else
+				_buffer[i] += tableDPCM[data];
+
+			// Emulating x86 16-bit signed register overflow
+			if (_buffer[i] > 32767) {
+				_buffer[i] -= 65536;
+			} else if (_buffer[i] < -32768) {
+				_buffer[i] += 65536;
+			}
+
+			*buffer++ = _buffer[i];
+		}
+
+		samples += _channels;
+	}
+
+	return samples;
+}
 
 VMDDecoder::File::File() {
 	offset   = 0;
@@ -1581,7 +1668,7 @@ VMDDecoder::VMDDecoder(Audio::Mixer *mixer, Audio::Mixer::SoundType soundType) :
 	_soundLastFilledFrame(0), _audioFormat(kAudioFormat8bitRaw),
 	_hasVideo(false), _videoCodec(0), _blitMode(0), _bytesPerPixel(0),
 	_firstFramePos(0), _videoBufferSize(0), _externalCodec(false), _codec(0),
-	_subtitle(-1), _isPaletted(true), _autoStartSound(true) {
+	_subtitle(-1), _isPaletted(true), _autoStartSound(true), _oldStereoBuffer(nullptr) {
 
 	_videoBuffer   [0] = 0;
 	_videoBuffer   [1] = 0;
@@ -1625,7 +1712,7 @@ bool VMDDecoder::seek(int32 frame, int whence, bool restart) {
 		delete _audioStream;
 
 		_soundStage  = kSoundLoaded;
-		_audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0);
+		createAudioStream();
 	}
 
 	_subtitle = -1;
@@ -1915,13 +2002,19 @@ bool VMDDecoder::assessAudioProperties() {
 
 		}
 	} else {
+		if (_soundStereo == 2) {
+			supportedFormat = false;
+		}
+
 		_soundBytesPerSample = 1;
-		_audioFormat         = kAudioFormat8bitRaw;
 		_soundHeaderSize     = 0;
 		_soundDataSize       = _soundSliceSize;
 
-		if (_soundStereo > 0)
-			supportedFormat = false;
+		if (_soundStereo == 1) {
+			_audioFormat = kAudioFormat16bitDPCM;
+		} else {
+			_audioFormat = kAudioFormat8bitRaw;
+		}
 	}
 
 	if (!supportedFormat) {
@@ -1930,14 +2023,12 @@ bool VMDDecoder::assessAudioProperties() {
 		return false;
 	}
 
-	_frameRate = Common::Rational(_soundFreq, _soundSliceSize);
+	_frameRate = Common::Rational(_soundFreq, _soundSliceSize / (_soundStereo == 1 ? 2 : 1));
 
 	_hasSound     = true;
 	_soundEnabled = true;
 	_soundStage   = kSoundLoaded;
-
-	_audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0);
-
+	createAudioStream();
 	return true;
 }
 
@@ -2074,6 +2165,7 @@ void VMDDecoder::close() {
 	_soundDataSize        = 0;
 	_soundLastFilledFrame = 0;
 	_audioFormat          = kAudioFormat8bitRaw;
+	_oldStereoBuffer      = nullptr;
 
 	_hasVideo      = false;
 	_videoCodec    = 0;
@@ -2123,9 +2215,9 @@ void VMDDecoder::processFrame() {
 
 	bool startSound = false;
 
-	for (uint16 i = 0; i < _partsPerFrame; i++) {
-		uint32 pos = _stream->pos();
+	_stream->seek(_frames[_curFrame].offset, SEEK_SET);
 
+	for (uint16 i = 0; i < _partsPerFrame; i++) {
 		Part &part = _frames[_curFrame].parts[i];
 
 		if (part.type == kPartTypeAudio) {
@@ -2147,7 +2239,7 @@ void VMDDecoder::processFrame() {
 
 				if (_soundEnabled) {
 					uint32 mask = _stream->readUint32LE();
-					filledSoundSlices(part.size - 4, mask);
+					filledSoundSlices(part.size - /* mask size */ 4, mask);
 
 					if (_soundStage == kSoundLoaded)
 						startSound = true;
@@ -2176,8 +2268,6 @@ void VMDDecoder::processFrame() {
 				_stream->skip(part.size);
 			}
 
-			_stream->seek(pos + part.size);
-
 		} else if ((part.type == kPartTypeVideo) && !_hasVideo) {
 
 			warning("VMDDecoder::processFrame(): Header claims there's no video, but video found (%d)", part.size);
@@ -2470,6 +2560,14 @@ void VMDDecoder::blit24(const Graphics::Surface &srcSurf, Common::Rect &rect) {
 }
 
 void VMDDecoder::emptySoundSlice(uint32 size) {
+	if (_soundStereo == 1) {
+		// Technically an empty slice could be used at the very beginning of the
+		// stream, but anywhere else it would need to dynamically calculate the
+		// delta between the current sample and zero sample level and the steps
+		// to get a zero level
+		error("Old-style stereo cannot be filled with an empty slice");
+	}
+
 	byte *soundBuf = (byte *)malloc(size);
 
 	if (soundBuf) {
@@ -2488,6 +2586,17 @@ void VMDDecoder::filledSoundSlice(uint32 size) {
 		return;
 	}
 
+	if (_soundStereo == 1) {
+		void *buf = malloc(size);
+		assert(buf);
+		const uint32 numBytesRead = _stream->read(buf, size);
+		assert(numBytesRead == size);
+		const uint32 numBytesWritten = _oldStereoBuffer->write(buf, size);
+		assert(numBytesWritten == size);
+		free(buf);
+		return;
+	}
+
 	Common::SeekableReadStream *data = _stream->readStream(size);
 	Audio::AudioStream *sliceStream = 0;
 
@@ -2508,9 +2617,9 @@ void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) {
 	uint8 max;
 	uint8 n = evaluateMask(mask, fillInfo, max);
 
-	int32 extraSize;
-
-	extraSize = size - n * _soundDataSize;
+	// extraSize is needed by videos in some games (GK2) or audio data will be
+	// incomplete
+	int32 extraSize = size - n * _soundDataSize;
 
 	if (_soundSlicesCount > 32)
 		extraSize -= (_soundSlicesCount - 32) * _soundDataSize;
@@ -2518,6 +2627,11 @@ void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) {
 	if (n > 0)
 		extraSize /= n;
 
+	// extraSize cannot be negative or audio data will be incomplete in some
+	// games (old-style stereo videos in Lighthouse)
+	if (extraSize < 0)
+		extraSize = 0;
+
 	for (uint8 i = 0; i < max; i++)
 		if (fillInfo[i])
 			filledSoundSlice(_soundDataSize + extraSize);
@@ -2528,6 +2642,14 @@ void VMDDecoder::filledSoundSlices(uint32 size, uint32 mask) {
 		filledSoundSlice((_soundSlicesCount - 32) * _soundDataSize + _soundHeaderSize);
 }
 
+void VMDDecoder::createAudioStream() {
+	_audioStream = Audio::makeQueuingAudioStream(_soundFreq, _soundStereo != 0);
+	if (_soundStereo == 1) {
+		_oldStereoBuffer = new Common::MemoryReadWriteStream(DisposeAfterUse::YES);
+		_audioStream->queueAudioStream(new DPCMStream(_oldStereoBuffer, _soundFreq, 2, true));
+	}
+}
+
 uint8 VMDDecoder::evaluateMask(uint32 mask, bool *fillInfo, uint8 &max) {
 	max = MIN<int>(_soundSlicesCount - 1, 31);
 
@@ -2555,86 +2677,12 @@ Audio::AudioStream *VMDDecoder::create8bitRaw(Common::SeekableReadStream *stream
 	return Audio::makeRawStream(stream, _soundFreq, flags, DisposeAfterUse::YES);
 }
 
-class DPCMStream : public Audio::AudioStream {
-public:
-	DPCMStream(Common::SeekableReadStream *stream, int rate, int channels) {
-		_stream = stream;
-		_rate = rate;
-		_channels = channels;
-	}
-
-	~DPCMStream() {
-		delete _stream;
-	}
-
-	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(); }
-
-private:
-	Common::SeekableReadStream *_stream;
-	int _channels;
-	int _rate;
-	int _buffer[2];
-};
-
-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];
-
-			// Emulating x86 16-bit signed register overflow
-			if (_buffer[i] > 32767) {
-				_buffer[i] -= 65536;
-			} else if (_buffer[i] < -32768) {
-				_buffer[i] += 65536;
-			}
-
-			*buffer++ = _buffer[i];
-		}
-
-		samples += _channels;
-	}
-
-	return samples;
-}
-
 Audio::AudioStream *VMDDecoder::create16bitDPCM(Common::SeekableReadStream *stream) {
-	return new DPCMStream(stream, _soundFreq, (_soundStereo == 0) ? 1 : 2);
+	// Old-style stereo audio blocks are not self-contained so cannot be played
+	// using this mechanism
+	assert(_soundStereo != 1);
+
+	return new DPCMStream(stream, _soundFreq, (_soundStereo == 0) ? 1 : 2, false);
 }
 
 class VMD_ADPCMStream : public Audio::DVI_ADPCMStream {
diff --git a/video/coktel_decoder.h b/video/coktel_decoder.h
index b8faa99..d1189c9 100644
--- a/video/coktel_decoder.h
+++ b/video/coktel_decoder.h
@@ -43,6 +43,7 @@
 
 namespace Common {
 struct Rect;
+class MemoryReadWriteStream;
 class SeekableReadStream;
 }
 namespace Audio {
@@ -503,6 +504,17 @@ private:
 	AudioFormat _audioFormat;
 	bool   _autoStartSound;
 
+	/**
+	 * Old stereo format packs a DPCM stream into audio packets without ensuring
+	 * that each packet contains an even amount of samples. In order for the
+	 * stream to play back correctly, all audio data needs to be pushed into a
+	 * single data buffer and read from there.
+	 *
+	 * This buffer is owned by _audioStream and will be disposed when
+	 * _audioStream is disposed.
+	 */
+	Common::MemoryReadWriteStream *_oldStereoBuffer;
+
 	// Video properties
 	bool   _hasVideo;
 	uint32 _videoCodec;
@@ -545,6 +557,7 @@ private:
 	void emptySoundSlice  (uint32 size);
 	void filledSoundSlice (uint32 size);
 	void filledSoundSlices(uint32 size, uint32 mask);
+	void createAudioStream();
 
 	uint8 evaluateMask(uint32 mask, bool *fillInfo, uint8 &max);
 





More information about the Scummvm-git-logs mailing list