[Scummvm-git-logs] scummvm master -> b4c52cac223d5e9749b4b8e18573414f80ea11b9

AndywinXp noreply at scummvm.org
Mon Jan 2 16:50:04 UTC 2023


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

Summary:
b4c52cac22 SCUMM: DIMUSE: Implement adaptive buffer underrun correction


Commit: b4c52cac223d5e9749b4b8e18573414f80ea11b9
    https://github.com/scummvm/scummvm/commit/b4c52cac223d5e9749b4b8e18573414f80ea11b9
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-01-02T17:50:00+01:00

Commit Message:
SCUMM: DIMUSE: Implement adaptive buffer underrun correction

Thanks to lephilousophe for the help on this one :-)

Changed paths:
    engines/scumm/imuse_digi/dimuse_engine.cpp
    engines/scumm/imuse_digi/dimuse_engine.h
    engines/scumm/imuse_digi/dimuse_tracks.cpp


diff --git a/engines/scumm/imuse_digi/dimuse_engine.cpp b/engines/scumm/imuse_digi/dimuse_engine.cpp
index 10c70f16e5d..2e92b40ebe4 100644
--- a/engines/scumm/imuse_digi/dimuse_engine.cpp
+++ b/engines/scumm/imuse_digi/dimuse_engine.cpp
@@ -78,6 +78,8 @@ IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer, Common::M
 
 	_radioChatterSFX = false;
 	_isEngineDisabled = false;
+	_checkForUnderrun = false;
+	_underrunCooldown = 0;
 
 	_audioNames = nullptr;
 	_numAudioNames = 0;
@@ -115,14 +117,20 @@ IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, Audio::Mixer *mixer, Common::M
 			_maxQueuedStreams++;
 		}
 
-		// The lower optimal bound is always 5, except if we're operating in low latency mode
-		_maxQueuedStreams = MAX(_mixer->getOutputBufSize() <= 1024 ? 4 : 5, _maxQueuedStreams);
+		// The lower optimal bound is always 4, except if we're operating in low latency mode
+		_maxQueuedStreams = MAX(_mixer->getOutputBufSize() <= 1024 ? 3 : 4, _maxQueuedStreams);
 	} else {
 		debug(5, "IMuseDigital::IMuseDigital(): WARNING: output audio buffer size not specified for this platform, defaulting _maxQueuedStreams to 4");
 		_maxQueuedStreams = 4;
 	}
 
+	// This value has been calculated in a way that is a good compromise
+	// between the absence of underruns and the lowest latency achievable.
+	// Of course this is never perfect, given the vast number of platforms
+	// we support, so the value might eventually be corrected by our custom
+	// adaptive buffer underrun correction routine.
 	_nominalBufferCount = _maxQueuedStreams;
+	_underrunCooldown = _maxQueuedStreams;
 
 	_vm->getTimerManager()->installTimerProc(timer_handler, 1000000 / _callbackFps, this, "IMuseDigital");
 }
@@ -152,8 +160,8 @@ IMuseDigital::~IMuseDigital() {
 }
 
 int IMuseDigital::roundRobinSetBufferCount() {
-	int minStreams = MAX<int>(_nominalBufferCount - 3, 0);
-	int maxStreams = _nominalBufferCount + 3;
+	int minStreams = MAX<int>(_nominalBufferCount - 5, 1);
+	int maxStreams = _nominalBufferCount + 5;
 	_maxQueuedStreams++;
 
 	if (_maxQueuedStreams > maxStreams) {
@@ -163,6 +171,11 @@ int IMuseDigital::roundRobinSetBufferCount() {
 	return _maxQueuedStreams;
 }
 
+void IMuseDigital::adaptBufferCount() {
+	_maxQueuedStreams++;
+	_nominalBufferCount = _maxQueuedStreams;
+}
+
 void IMuseDigital::stopSound(int sound) {
 	diMUSEStopSound(sound);
 }
diff --git a/engines/scumm/imuse_digi/dimuse_engine.h b/engines/scumm/imuse_digi/dimuse_engine.h
index 903f4b272b7..85dd2424f75 100644
--- a/engines/scumm/imuse_digi/dimuse_engine.h
+++ b/engines/scumm/imuse_digi/dimuse_engine.h
@@ -84,6 +84,8 @@ private:
 
 	bool _isEarlyDiMUSE;
 	bool _isEngineDisabled;
+	bool _checkForUnderrun;
+	int _underrunCooldown;
 
 	// These three are manipulated in the waveOut functions
 	uint8 *_outputAudioBuffer;
@@ -405,6 +407,7 @@ public:
 	int clampTuning(int value, int minValue, int maxValue);
 	int checkHookId(int &trackHookId, int sampleHookId);
 	int roundRobinSetBufferCount();
+	void adaptBufferCount();
 
 	// CMDs
 	int cmdsHandleCmd(int cmd, uint8 *ptr = nullptr,
diff --git a/engines/scumm/imuse_digi/dimuse_tracks.cpp b/engines/scumm/imuse_digi/dimuse_tracks.cpp
index 36c49a95b11..2b180cf455e 100644
--- a/engines/scumm/imuse_digi/dimuse_tracks.cpp
+++ b/engines/scumm/imuse_digi/dimuse_tracks.cpp
@@ -150,6 +150,23 @@ void IMuseDigital::tracksCallback() {
 		_tracksPauseTimer = 3;
 	}
 
+	// This piece of code is responsible for adaptive buffer overrun correction:
+	// it checks whether a buffer underrun has occurred within our output stream
+	// and then it increments the buffer count.
+	//
+	// This is not part of the original implementation, but it's used to yield
+	// smooth audio hopefully on every device.
+	if (_internalMixer->_stream->endOfData() && _checkForUnderrun) {
+		debug(5, "IMuseDigital::tracksCallback(): WARNING: audio buffer underrun, adapting the buffer queue count...");
+
+		adaptBufferCount();
+
+		// Allow the routine to cooldown: i.e. wait until the engine manages to
+		// refill the stream with the most recent maximum number of queueable buffers.
+		_underrunCooldown = _maxQueuedStreams;
+		_checkForUnderrun = false;
+	}
+
 	// If we leave the number of queued streams unbounded, we fill the queue with streams faster than
 	// we can play them: this leads to a very noticeable audio latency and desync with the graphics.
 	if ((int)_internalMixer->_stream->numQueuedStreams() < _maxQueuedStreams) {
@@ -159,6 +176,15 @@ void IMuseDigital::tracksCallback() {
 		waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
 
 		if (_outputFeedSize != 0) {
+			// Let's see if we should check for buffer underruns...
+			if (!_checkForUnderrun) {
+				if (_underrunCooldown == 0) {
+					_checkForUnderrun = true;
+				} else {
+					_underrunCooldown--;
+				}
+			}
+
 			_internalMixer->clearMixerBuffer();
 			if (_isEarlyDiMUSE && _splayer && _splayer->isAudioCallbackEnabled()) {
 				_splayer->processDispatches(_outputFeedSize);




More information about the Scummvm-git-logs mailing list