[Scummvm-git-logs] scummvm master -> 0c178b00f43a09166eb96f02788edaf9fbb4363d

lephilousophe noreply at scummvm.org
Wed Jan 12 08:11:03 UTC 2022


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:
dec0427eda VIDEO: Fix HNM audio desynchronization
0c178b00f4 VIDEO: Make unsigned -1 looking better


Commit: dec0427eda173036278f0f1af01777bfb2496c28
    https://github.com/scummvm/scummvm/commit/dec0427eda173036278f0f1af01777bfb2496c28
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-12T09:10:40+01:00

Commit Message:
VIDEO: Fix HNM audio desynchronization

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


diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index cdb5ea18bec..186b37b516d 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -34,7 +34,7 @@
 namespace Video {
 
 // When no sound display a frame every 80ms
-HNMDecoder::HNMDecoder(bool loop, byte *initialPalette) : _regularFrameDelay(80),
+HNMDecoder::HNMDecoder(bool loop, byte *initialPalette) : _regularFrameDelayMs(80),
 	_videoTrack(nullptr), _audioTrack(nullptr), _stream(nullptr),
 	_loop(loop), _initialPalette(initialPalette), _dataBuffer(nullptr), _dataBufferAlloc(0) {
 }
@@ -82,30 +82,35 @@ bool HNMDecoder::loadStream(Common::SeekableReadStream *stream) {
 		frameCount = 0;
 	}
 
+	// When no audio use a factor of 1 for audio timestamp
+	uint32 audioSampleRate = 1;
+
 	_videoTrack = nullptr;
 	_audioTrack = nullptr;
 	if (tag == MKTAG('H', 'N', 'M', '4')) {
-		_videoTrack = new HNM4VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay,
-		                                 _initialPalette);
 		if (soundFormat == 2 && soundBits != 0) {
 			// HNM4 is Mono 22050Hz
 			_audioTrack = new DPCMAudioTrack(soundFormat, soundBits, 22050, false, getSoundType());
+			audioSampleRate = 22050;
 		}
-	} else if (tag == MKTAG('U', 'B', 'B', '2')) {
-		_videoTrack = new HNM5VideoTrack(width, height, frameSize, frameCount, _regularFrameDelay,
+		_videoTrack = new HNM4VideoTrack(width, height, frameSize, frameCount,
+		                                 _regularFrameDelayMs, audioSampleRate,
 		                                 _initialPalette);
+	} else if (tag == MKTAG('U', 'B', 'B', '2')) {
 		if (soundFormat == 2 && soundBits == 0) {
 			// UBB2 is Stereo 22050Hz
 			_audioTrack = new DPCMAudioTrack(soundFormat, 16, 22050, true, getSoundType());
+			audioSampleRate = 22050;
 		}
+		_videoTrack = new HNM5VideoTrack(width, height, frameSize, frameCount,
+		                                 _regularFrameDelayMs, audioSampleRate,
+		                                 _initialPalette);
 	} else {
 		// We should never be here
 		close();
 		return false;
 	}
-	if (_videoTrack) {
-		addTrack(_videoTrack);
-	}
+	addTrack(_videoTrack);
 	if (_audioTrack) {
 		addTrack(_audioTrack);
 	}
@@ -158,6 +163,9 @@ void HNMDecoder::readNextPacket() {
 		error("Not enough data in file");
 	}
 
+	// We use -1 here to discrimate a possibly empty sound frame
+	uint32 audioNumSamples = -1;
+
 	byte *data_p = _dataBuffer;
 	while (superchunkRemaining > 0) {
 		if (superchunkRemaining < 8) {
@@ -177,8 +185,7 @@ void HNMDecoder::readNextPacket() {
 
 		if (chunkType == MKTAG16('S', 'D')) {
 			if (_audioTrack) {
-				Audio::Timestamp duration = _audioTrack->decodeSound(data_p, chunkSize - 8);
-				_videoTrack->setFrameDelay(duration.msecs());
+				audioNumSamples = _audioTrack->decodeSound(data_p, chunkSize - 8);
 			} else {
 				warning("Got audio data without an audio track");
 			}
@@ -189,27 +196,40 @@ void HNMDecoder::readNextPacket() {
 		data_p += (chunkSize - 8);
 		superchunkRemaining -= chunkSize;
 	}
+	_videoTrack->newFrame(audioNumSamples);
 }
 
-HNMDecoder::HNMVideoTrack::HNMVideoTrack(uint32 frameCount, uint32 regularFrameDelay) :
-	_frameCount(frameCount), _curFrame(-1),
-	_regularFrameDelay(regularFrameDelay), _nextFrameStartTime(0) {
+HNMDecoder::HNMVideoTrack::HNMVideoTrack(uint32 frameCount,
+        uint32 regularFrameDelayMs, uint32 audioSampleRate) :
+	_frameCount(frameCount), _curFrame(-1), _regularFrameDelayMs(regularFrameDelayMs),
+	_nextFrameStartTime(0, audioSampleRate) {
 	restart();
 }
 
-void HNMDecoder::HNMVideoTrack::setFrameDelay(uint32 frameDelay) {
-	if (_nextFrameDelay == uint32(-1)) {
-		_nextFrameDelay = frameDelay;
-	} else if (_nextNextFrameDelay == uint32(-1)) {
-		_nextNextFrameDelay = frameDelay;
-	} else {
-		_nextNextFrameDelay += frameDelay;
+void HNMDecoder::HNMVideoTrack::newFrame(uint32 frameDelay) {
+	// Frame done
+	_curFrame++;
+
+	// Add frameDelay if we had no sound
+	// We can't rely on a detection in the header as some soundless HNM indicate they have
+	if (frameDelay == uint32(-1)) {
+		_nextFrameStartTime = _nextFrameStartTime.addMsecs(_regularFrameDelayMs);
 	}
+
+	// HNM decoders use sound double buffering to pace the frames
+	// First frame is loaded in first buffer, second frame in second buffer
+	// It's only for third frame that we wait for the first buffer to be free
+	// It's presentation time is then delayed by the number of sound samples in the first frame
+	if (_lastFrameDelaySamps) {
+		_nextFrameStartTime = _nextFrameStartTime.addFrames(_lastFrameDelaySamps);
+	}
+	_lastFrameDelaySamps = frameDelay;
 }
 
 HNMDecoder::HNM45VideoTrack::HNM45VideoTrack(uint32 width, uint32 height, uint32 frameSize,
-        uint32 frameCount, uint32 regularFrameDelay, const byte *initialPalette) :
-	HNMVideoTrack(frameCount, regularFrameDelay) {
+        uint32 frameCount, uint32 regularFrameDelayMs, uint32 audioSampleRate,
+        const byte *initialPalette) :
+	HNMVideoTrack(frameCount, regularFrameDelayMs, audioSampleRate) {
 
 	// Get the currently loaded palette for undefined colors
 	if (initialPalette) {
@@ -281,8 +301,10 @@ void HNMDecoder::HNM45VideoTrack::decodePalette(byte *data, uint32 size) {
 }
 
 HNMDecoder::HNM4VideoTrack::HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize,
-        uint32 frameCount, uint32 regularFrameDelay, const byte *initialPalette) :
-	HNM45VideoTrack(width, height, frameSize, frameCount, regularFrameDelay, initialPalette) {
+        uint32 frameCount, uint32 regularFrameDelayMs, uint32 audioSampleRate,
+        const byte *initialPalette) :
+	HNM45VideoTrack(width, height, frameSize, frameCount,
+	                regularFrameDelayMs, audioSampleRate, initialPalette) {
 
 	_frameBufferF = new byte[frameSize]();
 }
@@ -564,12 +586,6 @@ void HNMDecoder::HNM4VideoTrack::presentFrame(uint16 flags) {
 	} else {
 		error("HNMDecoder::HNM4VideoTrack::postprocess(%x): Unexpected width: %d", flags, width);
 	}
-
-	// Frame done
-	_curFrame++;
-	_nextFrameStartTime += _nextFrameDelay != uint32(-1) ? _nextFrameDelay : _regularFrameDelay;
-	_nextFrameDelay = _nextNextFrameDelay;
-	_nextNextFrameDelay = uint32(-1);
 }
 
 void HNMDecoder::HNM5VideoTrack::decodeChunk(byte *data, uint32 size,
@@ -953,13 +969,6 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 	}
 
 	_surface.setPixels(_frameBufferC);
-
-	// Frame done
-	_curFrame++;
-	_nextFrameStartTime += _nextFrameDelay != uint32(-1) ? _nextFrameDelay : _regularFrameDelay;
-	_nextFrameDelay = _nextNextFrameDelay;
-	_nextNextFrameDelay = uint32(-1);
-
 }
 
 HNMDecoder::DPCMAudioTrack::DPCMAudioTrack(uint16 format, uint16 bits, uint sampleRate, bool stereo,
@@ -979,8 +988,7 @@ HNMDecoder::DPCMAudioTrack::~DPCMAudioTrack() {
 	delete _audioStream;
 }
 
-Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
-    byte *data, uint32 size) {
+uint32 HNMDecoder::DPCMAudioTrack::decodeSound(byte *data, uint32 size) {
 	if (!_gotLUT) {
 		if (size < 256 * sizeof(*_lut)) {
 			error("Invalid first sound chunk");
@@ -997,21 +1005,25 @@ Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
 	}
 
 	if (size == 0) {
-		return Audio::Timestamp(0, 0, _sampleRate);
+		return 0;
 	}
 
 	uint16 *out = (uint16 *)malloc(size * sizeof(*out));
 	uint16 *p = out;
 
+	uint32 numSamples = size;
+
 	byte flags = Audio::FLAG_16BITS;
 #ifdef SCUMM_LITTLE_ENDIAN
 	flags |= Audio::FLAG_LITTLE_ENDIAN;
 #endif
 	if (_audioStream->isStereo()) {
+		numSamples /= 2;
+
 		uint16 sampleL = _lastSampleL;
 		uint16 sampleR = _lastSampleR;
 		byte deltaId;
-		for (uint32 i = 0; i < size / 2; i++, p += 2) {
+		for (uint32 i = 0; i < numSamples; i++, p += 2) {
 			deltaId = *(data++);
 			sampleL += _lut[deltaId];
 			deltaId = *(data++);
@@ -1026,7 +1038,7 @@ Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
 	} else {
 		uint16 sample = _lastSampleL;
 		byte deltaId;
-		for (uint32 i = 0; i < size; i++, p++) {
+		for (uint32 i = 0; i < numSamples; i++, p++) {
 			deltaId = *(data++);
 			sample += _lut[deltaId];
 			*p = sample;
@@ -1035,7 +1047,7 @@ Audio::Timestamp HNMDecoder::DPCMAudioTrack::decodeSound(
 	}
 
 	_audioStream->queueBuffer((byte *)out, size * sizeof(*out), DisposeAfterUse::YES, flags);
-	return Audio::Timestamp(0, _audioStream->isStereo() ? size / 2 : size, _sampleRate);
+	return numSamples;
 }
 
 } // End of namespace Video
diff --git a/video/hnm_decoder.h b/video/hnm_decoder.h
index 80218e4ed80..b69f83f28a0 100644
--- a/video/hnm_decoder.h
+++ b/video/hnm_decoder.h
@@ -49,30 +49,29 @@ public:
 	void readNextPacket() override;
 	void close() override;
 
-	void setRegularFrameDelay(uint32 regularFrameDelay) { _regularFrameDelay = regularFrameDelay; }
+	void setRegularFrameDelay(uint32 regularFrameDelay) { _regularFrameDelayMs = regularFrameDelay; }
 
 private:
 	class HNMVideoTrack : public VideoTrack {
 	public:
-		HNMVideoTrack(uint32 frameCount, uint32 regularFrameDelay);
+		HNMVideoTrack(uint32 frameCount, uint32 regularFrameDelayMs, uint32 audioSampleRate);
 
 		// When _frameCount is 0, it means we are looping
 		bool endOfTrack() const override { return (_frameCount == 0) ? false : VideoTrack::endOfTrack(); }
 		int getCurFrame() const override { return _curFrame; }
 		int getFrameCount() const override { return _frameCount; }
-		uint32 getNextFrameStartTime() const override { return _nextFrameStartTime; }
+		uint32 getNextFrameStartTime() const override { return _nextFrameStartTime.msecs(); }
 
-		void restart() { _nextFrameDelay = uint32(-1); _nextNextFrameDelay = uint32(-1); }
-		void setFrameDelay(uint32 frameDelay);
+		void restart() { _lastFrameDelaySamps = 0; }
+		void newFrame(uint32 frameDelay);
 
 		virtual void decodeChunk(byte *data, uint32 size,
 		                         uint16 chunkType, uint16 flags) = 0;
 
 	protected:
-		uint32 _regularFrameDelay;
-		uint32 _nextFrameDelay;
-		uint32 _nextNextFrameDelay;
-		uint32 _nextFrameStartTime;
+		uint32 _regularFrameDelayMs;
+		uint32 _lastFrameDelaySamps;
+		Audio::Timestamp _nextFrameStartTime;
 
 		uint32 _frameCount;
 		int _curFrame;
@@ -90,7 +89,8 @@ private:
 
 	protected:
 		HNM45VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
-		                uint32 regularFrameDelay, const byte *initialPalette = nullptr);
+		                uint32 regularFrameDelayMs, uint32 audioSampleRate,
+		                const byte *initialPalette = nullptr);
 		~HNM45VideoTrack() override;
 
 		/** Decode a video chunk. */
@@ -108,7 +108,8 @@ private:
 	class HNM4VideoTrack : public HNM45VideoTrack {
 	public:
 		HNM4VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
-		               uint32 regularFrameDelay, const byte *initialPalette = nullptr);
+		               uint32 regularFrameDelayMs, uint32 audioSampleRate,
+		               const byte *initialPalette = nullptr);
 		~HNM4VideoTrack() override;
 
 		/** Decode a video chunk. */
@@ -128,8 +129,10 @@ private:
 	class HNM5VideoTrack : public HNM45VideoTrack {
 	public:
 		HNM5VideoTrack(uint32 width, uint32 height, uint32 frameSize, uint32 frameCount,
-		               uint32 regularFrameDelay, const byte *initialPalette = nullptr) :
-			HNM45VideoTrack(width, height, frameSize, frameCount, regularFrameDelay, initialPalette) {}
+		               uint32 regularFrameDelayMs, uint32 audioSampleRate,
+		               const byte *initialPalette = nullptr) :
+			HNM45VideoTrack(width, height, frameSize, frameCount, regularFrameDelayMs, audioSampleRate,
+			                initialPalette) {}
 		/** Decode a video chunk. */
 		void decodeChunk(byte *data, uint32 size,
 		                 uint16 chunkType, uint16 flags) override;
@@ -145,7 +148,7 @@ private:
 		               Audio::Mixer::SoundType soundType);
 		~DPCMAudioTrack() override;
 
-		Audio::Timestamp decodeSound(byte *data, uint32 size);
+		uint32 decodeSound(byte *data, uint32 size);
 	protected:
 		Audio::AudioStream *getAudioStream() const override { return _audioStream; }
 	private:
@@ -161,7 +164,7 @@ private:
 	bool _loop;
 	byte *_initialPalette;
 
-	uint32 _regularFrameDelay;
+	uint32 _regularFrameDelayMs;
 	// These two pointer are owned by VideoDecoder
 	HNMVideoTrack *_videoTrack;
 	DPCMAudioTrack *_audioTrack;


Commit: 0c178b00f43a09166eb96f02788edaf9fbb4363d
    https://github.com/scummvm/scummvm/commit/0c178b00f43a09166eb96f02788edaf9fbb4363d
Author: Le Philousophe (lephilousophe at users.noreply.github.com)
Date: 2022-01-12T09:10:40+01:00

Commit Message:
VIDEO: Make unsigned -1 looking better

Changed paths:
    video/hnm_decoder.cpp


diff --git a/video/hnm_decoder.cpp b/video/hnm_decoder.cpp
index 186b37b516d..20620cba390 100644
--- a/video/hnm_decoder.cpp
+++ b/video/hnm_decoder.cpp
@@ -843,8 +843,8 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 	uint16 pitch = _surface.pitch;
 	bool eop = false;
 
-	byte height = (byte)-1;
-	byte currentMode = (byte)-1;
+	byte height = -1;
+	byte currentMode = -1;
 	uint32 currentPos = 0;
 
 	while (!eop) {
@@ -856,7 +856,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 		size -= 1;
 
 		if (opcode == 0x20) {
-			assert(height != (byte)-1);
+			assert(height != byte(-1));
 			if (size < 1) {
 				error("Not enough data for opcode 0x20");
 			}
@@ -869,7 +869,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 			currentPos += width;
 		} else if (opcode == 0x60) {
 			// Maximal pixels height is 4
-			assert(height != (byte)-1 && height <= 4);
+			assert(height != byte(-1) && height <= 4);
 			if (size < height) {
 				error("Not enough data for opcode 0x60");
 			}
@@ -879,7 +879,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 			size -= height;
 			currentPos += 1;
 		} else if (opcode == 0xA0) {
-			assert(height != (byte)-1);
+			assert(height != byte(-1));
 			if (size < 1) {
 				error("Not enough data for opcode 0x20");
 			}
@@ -905,7 +905,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 			size -= 1;
 
 			if (subop == 0x00) {
-				assert(height != (byte)-1);
+				assert(height != byte(-1));
 				if (size < 2) {
 					error("Not enough data for opcode 0xE0 0x00");
 				}
@@ -920,7 +920,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 				}
 				currentPos += width;
 			} else if (subop == 0x01) {
-				if (height != (byte)-1) {
+				if (height != byte(-1)) {
 					currentPos += (height - 1) * pitch;
 				}
 
@@ -930,7 +930,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 				assert((currentPos % pitch) == 0);
 				assert(subop < 48);
 
-				if (height != (byte)-1) {
+				if (height != byte(-1)) {
 					currentPos += (height - 1) * pitch;
 				}
 
@@ -951,7 +951,7 @@ void HNMDecoder::HNM5VideoTrack::decodeFrame(byte *data, uint32 size) {
 
 			}
 		} else {
-			assert(height != (byte)-1);
+			assert(height != byte(-1));
 			assert(2 <= height && height <= 4);
 			byte index = opcode & 0x1f;
 			byte copyMode = (opcode >> 5) & 0x7;




More information about the Scummvm-git-logs mailing list