[Scummvm-git-logs] scummvm master -> 8d6737e16fd1fbebdb40366eb87319c947fe90cf
AndywinXp
noreply at scummvm.org
Tue Jan 10 20:17:28 UTC 2023
This automated email contains information about 3 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
8d0e97af70 SCUMM: DIMUSE: Implement low latency mode
a41b1174ed SCUMM: v7-8: Add GUI option for toggling low latency audio mode
8d6737e16f SCUMM: DIMUSE: Increase buffer size for low latency mode
Commit: 8d0e97af70b816127f7ba56554bde8c2155227ee
https://github.com/scummvm/scummvm/commit/8d0e97af70b816127f7ba56554bde8c2155227ee
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-01-10T21:17:23+01:00
Commit Message:
SCUMM: DIMUSE: Implement low latency mode
This fixes bug #13462
Changed paths:
engines/scumm/imuse_digi/dimuse_defs.h
engines/scumm/imuse_digi/dimuse_engine.cpp
engines/scumm/imuse_digi/dimuse_engine.h
engines/scumm/imuse_digi/dimuse_internalmixer.cpp
engines/scumm/imuse_digi/dimuse_internalmixer.h
engines/scumm/imuse_digi/dimuse_tracks.cpp
engines/scumm/imuse_digi/dimuse_waveout.cpp
engines/scumm/scumm.cpp
diff --git a/engines/scumm/imuse_digi/dimuse_defs.h b/engines/scumm/imuse_digi/dimuse_defs.h
index 010a6b0308a..f9774897bea 100644
--- a/engines/scumm/imuse_digi/dimuse_defs.h
+++ b/engines/scumm/imuse_digi/dimuse_defs.h
@@ -181,6 +181,7 @@ typedef struct {
} IMuseDigiFade;
struct IMuseDigiTrack {
+ int index;
IMuseDigiTrack *prev;
IMuseDigiTrack *next;
IMuseDigiDispatch *dispatchPtr;
diff --git a/engines/scumm/imuse_digi/dimuse_engine.cpp b/engines/scumm/imuse_digi/dimuse_engine.cpp
index f05fad7b7d6..49ceb7c0b55 100644
--- a/engines/scumm/imuse_digi/dimuse_engine.cpp
+++ b/engines/scumm/imuse_digi/dimuse_engine.cpp
@@ -41,7 +41,7 @@ void IMuseDigital::timer_handler(void *refCon) {
diMUSE->callback();
}
-IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *mixer, Common::Mutex *mutex)
+IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *mixer, Common::Mutex *mutex, bool lowLatencyMode)
: _vm(scumm), _mixer(mixer), _mutex(mutex) {
assert(_vm);
assert(mixer);
@@ -83,12 +83,13 @@ IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *
_isEngineDisabled = false;
_checkForUnderrun = false;
_underrunCooldown = 0;
+ _lowLatencyMode = lowLatencyMode;
_audioNames = nullptr;
_numAudioNames = 0;
_emptyMarker[0] = '\0';
- _internalMixer = new IMuseDigiInternalMixer(mixer, _internalSampleRate, _isEarlyDiMUSE);
+ _internalMixer = new IMuseDigiInternalMixer(mixer, _internalSampleRate, _isEarlyDiMUSE, _lowLatencyMode);
_groupsHandler = new IMuseDigiGroupsHandler(this);
_fadesHandler = new IMuseDigiFadesHandler(this);
_triggersHandler = new IMuseDigiTriggersHandler(this);
@@ -159,6 +160,9 @@ IMuseDigital::~IMuseDigital() {
free(_waveOutOutputBuffer);
_waveOutOutputBuffer = nullptr;
+ free(_waveOutLowLatencyOutputBuffer);
+ _waveOutLowLatencyOutputBuffer = nullptr;
+
free(_audioNames);
}
diff --git a/engines/scumm/imuse_digi/dimuse_engine.h b/engines/scumm/imuse_digi/dimuse_engine.h
index 2daba5e2455..f563518bca3 100644
--- a/engines/scumm/imuse_digi/dimuse_engine.h
+++ b/engines/scumm/imuse_digi/dimuse_engine.h
@@ -86,6 +86,7 @@ private:
bool _isEngineDisabled;
bool _checkForUnderrun;
int _underrunCooldown;
+ bool _lowLatencyMode;
int _internalFeedSize;
int _internalSampleRate;
@@ -95,6 +96,9 @@ private:
int _outputFeedSize;
int _outputSampleRate;
+ // Used in low latency mode only
+ uint8 *_outputLowLatencyAudioBuffers[DIMUSE_MAX_TRACKS];
+
int _maxQueuedStreams; // maximum number of streams which can be queued before they are played
int _nominalBufferCount;
@@ -200,6 +204,7 @@ private:
void tracksSaveLoad(Common::Serializer &ser);
void tracksSetGroupVol();
void tracksCallback();
+ void tracksLowLatencyCallback();
int tracksStartSound(int soundId, int tryPriority, int group);
int tracksStopSound(int soundId);
int tracksStopAllSounds();
@@ -307,8 +312,14 @@ private:
void waveOutCallback();
byte waveOutGetStreamFlags();
+ // Low latency mode
+ void waveOutLowLatencyWrite(uint8 **audioBuffer, int &feedSize, int &sampleRate, int idx);
+ void waveOutEmptyBuffer(int idx);
+
+ uint8 *_waveOutLowLatencyOutputBuffer;
+
public:
- IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *mixer, Common::Mutex *mutex);
+ IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *mixer, Common::Mutex *mutex, bool lowLatencyMode = false);
~IMuseDigital() override;
// Wrapper functions used by the main engine
diff --git a/engines/scumm/imuse_digi/dimuse_internalmixer.cpp b/engines/scumm/imuse_digi/dimuse_internalmixer.cpp
index 74b2171798f..fccff781bac 100644
--- a/engines/scumm/imuse_digi/dimuse_internalmixer.cpp
+++ b/engines/scumm/imuse_digi/dimuse_internalmixer.cpp
@@ -30,10 +30,21 @@
namespace Scumm {
-IMuseDigiInternalMixer::IMuseDigiInternalMixer(Audio::Mixer *mixer, int sampleRate, bool isEarlyDiMUSE) {
+IMuseDigiInternalMixer::IMuseDigiInternalMixer(Audio::Mixer *mixer, int sampleRate, bool isEarlyDiMUSE, bool lowLatencyMode) {
_mixer = mixer;
- _stream = Audio::makeQueuingAudioStream(sampleRate, _mixer->getOutputStereo());
+ _sampleRate = sampleRate;
+ _lowLatencyMode = lowLatencyMode;
+
+ // Mark all separate streams for low latency mode as available...
+ for (int i = 0; i < ARRAYSIZE(_separateStreams); i++) {
+ _separateStreams[i] = nullptr;
+ }
+
+ if (!_lowLatencyMode || _isEarlyDiMUSE)
+ _stream = Audio::makeQueuingAudioStream(_sampleRate, _mixer->getOutputStereo());
+
_isEarlyDiMUSE = isEarlyDiMUSE;
+
_radioChatter = 0;
_amp8Table = nullptr;
}
@@ -148,10 +159,21 @@ int IMuseDigiInternalMixer::init(int bytesPerSample, int numChannels, uint8 *mix
((int16 *)_softLMID)[-i - 1] = -1 - (int16)softLcurValue;
}
}
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_channelHandle, _stream, -1, Audio::Mixer::kMaxChannelVolume, false);
+
+ // In low latency the following stream is still used
+ // for FTSMUSH cutscenes, so let's still play it.
+ if (!_lowLatencyMode || _isEarlyDiMUSE) {
+ _mixer->playStream(
+ Audio::Mixer::kPlainSoundType,
+ &_channelHandle,
+ _stream,
+ -1,
+ Audio::Mixer::kMaxChannelVolume);
+ }
+
return 0;
} else {
- debug(5, "DiMUSE_InternalMixer::init(): ERROR: couldn't allocate mixer tables");
+ debug(5, "IMuseDigiInternalMixer::init(): ERROR: couldn't allocate mixer tables");
return -1;
}
}
@@ -309,6 +331,44 @@ int IMuseDigiInternalMixer::loop(uint8 **destBuffer, int len) {
return 0;
}
+void IMuseDigiInternalMixer::setCurrentMixerBuffer(uint8 *newBuf) {
+ // Used to swap mixer buffers in low latency mode
+ _mixBuf = newBuf;
+}
+
+void IMuseDigiInternalMixer::endStream(int idx) {
+ // We mark the stream as finished and then remove
+ // the reference: in this way, the stream will have time
+ // to flush the remaining sound data and will be disposed
+ // by the mixer, while we freed up a slot for a new stream.
+ if (_lowLatencyMode && idx != -1) {
+ _separateStreams[idx]->finish();
+ _separateStreams[idx] = nullptr;
+ }
+}
+
+Audio::QueuingAudioStream *IMuseDigiInternalMixer::getStream(int idx) {
+ if (!_lowLatencyMode || idx == -1) {
+ // Stream for the original mode
+ return _stream;
+ } else if (!_separateStreams[idx]) {
+ // New stream for a new sound
+ _separateStreams[idx] = Audio::makeQueuingAudioStream(_sampleRate, _mixer->getOutputStereo());
+
+ _mixer->playStream(
+ Audio::Mixer::kPlainSoundType,
+ &_separateChannelHandles[idx],
+ _separateStreams[idx],
+ -1,
+ Audio::Mixer::kMaxChannelVolume);
+
+ return _separateStreams[idx];
+ } else {
+ // On-going stream
+ return _separateStreams[idx];
+ }
+}
+
void IMuseDigiInternalMixer::mixBits8Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable, bool ftIs11025Hz) {
uint16 *mixBufCurCell;
uint8 *srcBuf_ptr;
diff --git a/engines/scumm/imuse_digi/dimuse_internalmixer.h b/engines/scumm/imuse_digi/dimuse_internalmixer.h
index e9ecf18dc77..2edc7379bc9 100644
--- a/engines/scumm/imuse_digi/dimuse_internalmixer.h
+++ b/engines/scumm/imuse_digi/dimuse_internalmixer.h
@@ -58,8 +58,10 @@ private:
int _radioChatter;
int _outWordSize;
int _outChannelCount;
+ int _sampleRate;
int _stereoReverseFlag;
bool _isEarlyDiMUSE;
+ bool _lowLatencyMode;
void mixBits8Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable, bool ftIs11025Hz);
void mixBits12Mono(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
@@ -78,7 +80,7 @@ private:
void mixBits16Stereo(uint8 *srcBuf, int32 inFrameCount, int feedSize, int32 mixBufStartIndex, int32 *ampTable);
public:
- IMuseDigiInternalMixer(Audio::Mixer *mixer, int sampleRate, bool isEarlyDiMUSE);
+ IMuseDigiInternalMixer(Audio::Mixer *mixer, int sampleRate, bool isEarlyDiMUSE, bool lowLatencyMode = false);
~IMuseDigiInternalMixer();
int init(int bytesPerSample, int numChannels, uint8 *mixBuf, int mixBufSize, int sizeSampleKB, int mixChannelsNum);
void setRadioChatter();
@@ -88,6 +90,14 @@ public:
void mix(uint8 *srcBuf, int32 inFrameCount, int wordSize, int channelCount, int feedSize, int32 mixBufStartIndex, int volume, int pan, bool ftIs11025Hz);
int loop(uint8 **destBuffer, int len);
Audio::QueuingAudioStream *_stream;
+
+ // For low latency audio
+ void setCurrentMixerBuffer(uint8 *newBuf);
+ void endStream(int idx);
+ Audio::QueuingAudioStream *getStream(int idx);
+
+ Audio::QueuingAudioStream *_separateStreams[DIMUSE_MAX_TRACKS];
+ Audio::SoundHandle _separateChannelHandles[DIMUSE_MAX_TRACKS];
};
} // End of namespace Scumm
diff --git a/engines/scumm/imuse_digi/dimuse_tracks.cpp b/engines/scumm/imuse_digi/dimuse_tracks.cpp
index 86b86c031cb..5b9e7888391 100644
--- a/engines/scumm/imuse_digi/dimuse_tracks.cpp
+++ b/engines/scumm/imuse_digi/dimuse_tracks.cpp
@@ -24,7 +24,7 @@
namespace Scumm {
int IMuseDigital::tracksInit() {
- _trackCount = 6;
+ _trackCount = _lowLatencyMode ? DIMUSE_MAX_TRACKS : 6;
_tracksPauseTimer = 0;
_trackList = nullptr;
@@ -43,6 +43,7 @@ int IMuseDigital::tracksInit() {
}
for (int l = 0; l < _trackCount; l++) {
+ _tracks[l].index = l;
_tracks[l].prev = nullptr;
_tracks[l].next = nullptr;
_tracks[l].dispatchPtr = dispatchGetDispatchByTrackId(l);
@@ -213,6 +214,109 @@ void IMuseDigital::tracksCallback() {
}
}
+void IMuseDigital::tracksLowLatencyCallback() {
+ // Why do we need a low latency mode?
+ //
+ // For every audio callback, this engine works by collecting all the sound
+ // data for every track and by mixing it up in a single output stream.
+ // This is exactly how the original executables worked, so our implementation
+ // provides a very faithful recreation of that experience. And it comes with
+ // a compromise that e.g. The Dig and Full Throttle didn't have to front:
+ //
+ // in order to provide glitchless audio, an appropriate stream queue size
+ // has to be enforced: a longer queue yields a lower probability of audio glitches
+ // but a higher latency, and viceversa. In our case, this depends on the audio backend
+ // configuration. As such: some configurations might encounter audible latency (#13462).
+ //
+ // We solve this issue by offering this alternate low latency mode which, instead
+ // of keeping a single stream for everything, creates (and disposes) streams on the fly
+ // for every different sound. This means that whenever the new sound data is ready,
+ // a new stream is initialized and played immediately, without having to wait for all
+ // the other sounds to be processed and mixed in the same sample pool.
+
+ if (_tracksPauseTimer) {
+ if (++_tracksPauseTimer < 3)
+ return;
+ _tracksPauseTimer = 3;
+ }
+
+ // The callback path is heavily inspired from the original one (see tracksCallback()),
+ // but it handles each track separatedly, with the exception of SMUSH audio for Full
+ // Throttle: this is why we operate on two parallel paths...
+
+ if (!_isEarlyDiMUSE)
+ dispatchPredictFirstStream();
+
+ IMuseDigiTrack *track = _trackList;
+
+ // This flag ensures that, even when no track is available,
+ // FT SMUSH audio can still be played. At least, and AT MOST once :-)
+ bool runSMUSHAudio = _isEarlyDiMUSE;
+
+ while (track || runSMUSHAudio) {
+
+ IMuseDigiTrack *next = track ? track->next : nullptr;
+ int idx = track ? track->index : -1;
+
+ // We use a separate queue cardinality handling, since SMUSH audio and iMUSE audio can overlap...
+ bool canQueueBufs = (int)_internalMixer->getStream(idx)->numQueuedStreams() < _maxQueuedStreams;
+ bool canQueueFtSmush = (int)_internalMixer->getStream(-1)->numQueuedStreams() < _maxQueuedStreams;
+
+ if (canQueueBufs) {
+ if (track)
+ waveOutLowLatencyWrite(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize, _outputSampleRate, idx);
+
+ // Notice how SMUSH audio for Full Throttle uses the original single-stream mode:
+ // this is necessary both for code cleanliness and for correct audio sync.
+ if (runSMUSHAudio && canQueueFtSmush)
+ waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
+
+ if (_outputFeedSize != 0) {
+ // FT SMUSH dispatch processing...
+ if (runSMUSHAudio && canQueueFtSmush && _isEarlyDiMUSE && _splayer && _splayer->isAudioCallbackEnabled()) {
+ _internalMixer->setCurrentMixerBuffer(_outputAudioBuffer);
+ _internalMixer->clearMixerBuffer();
+
+ _splayer->processDispatches(_outputFeedSize);
+ _internalMixer->loop(&_outputAudioBuffer, _outputFeedSize);
+ }
+
+ // Ordinary audio tracks handling...
+ if (track) {
+ _internalMixer->setCurrentMixerBuffer(_outputLowLatencyAudioBuffers[idx]);
+ _internalMixer->clearMixerBuffer();
+
+ if (!_tracksPauseTimer) {
+ if (_isEarlyDiMUSE) {
+ dispatchProcessDispatches(track, _outputFeedSize);
+ } else {
+ dispatchProcessDispatches(track, _outputFeedSize, _outputSampleRate);
+ }
+ }
+
+ _internalMixer->loop(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize);
+
+ // The Dig tries to write a second time
+ if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
+ waveOutLowLatencyWrite(&_outputLowLatencyAudioBuffers[idx], _outputFeedSize, _outputSampleRate, idx);
+ }
+
+ // If, after processing the track dispatch, the sound is set to zero
+ // it means that it has reached the end: let's notify its stream...
+ if (track->soundId == 0) {
+ _internalMixer->endStream(idx);
+ }
+ }
+ }
+ }
+
+ if (track)
+ track = next;
+
+ runSMUSHAudio = false;
+ }
+}
+
int IMuseDigital::tracksStartSound(int soundId, int tryPriority, int group) {
int priority = clampNumber(tryPriority, 0, 127);
@@ -400,6 +504,9 @@ void IMuseDigital::tracksClear(IMuseDigiTrack *trackPtr) {
_vm->_res->unlock(rtSound, trackPtr->soundId);
}
+ if (_lowLatencyMode)
+ waveOutEmptyBuffer(trackPtr->index);
+
trackPtr->soundId = 0;
}
diff --git a/engines/scumm/imuse_digi/dimuse_waveout.cpp b/engines/scumm/imuse_digi/dimuse_waveout.cpp
index 56080d9b6e8..c9712a998d3 100644
--- a/engines/scumm/imuse_digi/dimuse_waveout.cpp
+++ b/engines/scumm/imuse_digi/dimuse_waveout.cpp
@@ -31,19 +31,36 @@ int IMuseDigital::waveOutInit(waveOutParamsStruct *waveOutSettingsStruct) {
_waveOutSampleRate = _internalSampleRate;
_waveOutPreferredFeedSize = _internalFeedSize;
- // Nine buffers (waveOutPreferredFeedSize * 4 bytes each), two will be used for the mixer
- _waveOutOutputBuffer = (uint8 *)malloc(_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
- _waveOutMixBuffer = _waveOutOutputBuffer + (_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 7); // 8-th buffer
+ _waveOutOutputBuffer = nullptr;
+ _waveOutMixBuffer = nullptr;
+ _waveOutLowLatencyOutputBuffer = nullptr;
+
+ if (!_lowLatencyMode || _isEarlyDiMUSE) {
+ // Nine buffers (waveOutPreferredFeedSize * 4 bytes each), two will be used for the mixer
+ _waveOutOutputBuffer = (uint8 *)malloc(_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
+ _waveOutMixBuffer = _waveOutOutputBuffer + (_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 7); // 8-th buffer
+ }
+
+ // Replicate another set of buffers for the low latency mode, we will use the previous ones for cutscenes if the mode is active
+ if (_lowLatencyMode) {
+ _waveOutLowLatencyOutputBuffer = (uint8 *)malloc(_waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
+ }
// This information will be fed to the internal mixer during its initialization
waveOutSettingsStruct->bytesPerSample = _waveOutBytesPerSample * 8;
waveOutSettingsStruct->numChannels = _waveOutNumChannels;
waveOutSettingsStruct->mixBufSize = (_waveOutBytesPerSample * _waveOutNumChannels) * _waveOutPreferredFeedSize;
waveOutSettingsStruct->sizeSampleKB = 0;
- waveOutSettingsStruct->mixBuf = _waveOutMixBuffer;
+ waveOutSettingsStruct->mixBuf = _waveOutMixBuffer; // Note: in low latency mode this initialization is a dummy
- // Init the buffer filling it with zero volume samples
- memset(_waveOutOutputBuffer, _waveOutZeroLevel, _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
+ // Init the buffers filling them with zero volume samples
+ if (!_lowLatencyMode || _isEarlyDiMUSE) {
+ memset(_waveOutOutputBuffer, _waveOutZeroLevel, _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
+ }
+
+ if (_lowLatencyMode) {
+ memset(_waveOutLowLatencyOutputBuffer, _waveOutZeroLevel, _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize * 9);
+ }
_waveOutDisableWrite = 0;
return 0;
@@ -73,7 +90,7 @@ void IMuseDigital::waveOutWrite(uint8 **audioData, int &feedSize, int &sampleRat
byte *ptr = (byte *)malloc(_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
memcpy(ptr, curBufferBlock, _outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
- _internalMixer->_stream->queueBuffer(ptr,
+ _internalMixer->getStream(-1)->queueBuffer(ptr,
_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels,
DisposeAfterUse::YES,
waveOutGetStreamFlags());
@@ -88,7 +105,11 @@ int IMuseDigital::waveOutDeinit() {
void IMuseDigital::waveOutCallback() {
Common::StackLock lock(*_mutex);
- tracksCallback();
+ if (_lowLatencyMode) {
+ tracksLowLatencyCallback();
+ } else {
+ tracksCallback();
+ }
}
byte IMuseDigital::waveOutGetStreamFlags() {
@@ -104,4 +125,40 @@ byte IMuseDigital::waveOutGetStreamFlags() {
return flags;
}
+void IMuseDigital::waveOutLowLatencyWrite(uint8 **audioData, int &feedSize, int &sampleRate, int idx) {
+ uint8 *curBufferBlock;
+ if (_waveOutDisableWrite)
+ return;
+
+ if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
+ _waveOutXorTrigger ^= 1;
+ if (!_waveOutXorTrigger)
+ return;
+ }
+
+ feedSize = 0;
+ if (_mixer->isReady()) {
+ curBufferBlock = &_waveOutLowLatencyOutputBuffer[_waveOutPreferredFeedSize * idx * _waveOutBytesPerSample * _waveOutNumChannels];
+
+ *audioData = curBufferBlock;
+
+ sampleRate = _waveOutSampleRate;
+ feedSize = _waveOutPreferredFeedSize;
+
+ byte *ptr = (byte *)malloc(_outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
+ memcpy(ptr, curBufferBlock, _outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels);
+
+ _internalMixer->getStream(idx)->queueBuffer(ptr,
+ _outputFeedSize * _waveOutBytesPerSample * _waveOutNumChannels,
+ DisposeAfterUse::YES,
+ waveOutGetStreamFlags());
+ }
+}
+
+void IMuseDigital::waveOutEmptyBuffer(int idx) {
+ // This is necessary in low latency mode to clean-up the buffers of stale/finished sounds
+ int bufferSize = _waveOutNumChannels * _waveOutBytesPerSample * _waveOutPreferredFeedSize;
+ memset(&_waveOutLowLatencyOutputBuffer[bufferSize * idx], _waveOutZeroLevel, bufferSize);
+}
+
} // End of namespace Scumm
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 0287e20a73d..583a35dc570 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1443,6 +1443,12 @@ void ScummEngine_v7::setupScumm(const Common::String &macResourceFile) {
}
}
+ bool lowLatencyMode = false;
+ ConfMan.registerDefault("dimuse_low_latency_mode", false);
+ if (ConfMan.hasKey("dimuse_low_latency_mode", _targetName)) {
+ lowLatencyMode = ConfMan.getBool("dimuse_low_latency_mode");
+ }
+
_musicEngine = _imuseDigital = new IMuseDigital(this, sampleRate, _mixer, &_resourceAccessMutex);
if (filesAreCompressed) {
Commit: a41b1174ed3e64853e9f9796430ef901fa3c08af
https://github.com/scummvm/scummvm/commit/a41b1174ed3e64853e9f9796430ef901fa3c08af
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-01-10T21:17:23+01:00
Commit Message:
SCUMM: v7-8: Add GUI option for toggling low latency audio mode
Changed paths:
engines/scumm/detection.h
engines/scumm/detection_tables.h
engines/scumm/metaengine.cpp
engines/scumm/scumm.cpp
diff --git a/engines/scumm/detection.h b/engines/scumm/detection.h
index 4eb36ca1408..51927c822ef 100644
--- a/engines/scumm/detection.h
+++ b/engines/scumm/detection.h
@@ -33,6 +33,7 @@ namespace Scumm {
#define GUIO_ENHANCEMENTS GUIO_GAMEOPTIONS2
#define GUIO_AUDIO_OVERRIDE GUIO_GAMEOPTIONS3
#define GUIO_ORIGINALGUI GUIO_GAMEOPTIONS4
+#define GUIO_LOWLATENCYAUDIO GUIO_GAMEOPTIONS5
/**
* Descriptor of a specific SCUMM game. Used internally to store
diff --git a/engines/scumm/detection_tables.h b/engines/scumm/detection_tables.h
index ab85848be6b..4092f8b5ef7 100644
--- a/engines/scumm/detection_tables.h
+++ b/engines/scumm/detection_tables.h
@@ -213,15 +213,15 @@ static const GameSettings gameVariantsTable[] = {
{"samnmax", "", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO3(GUIO_RENDEREGA, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI)},
{"samnmax", "Floppy", 0, GID_SAMNMAX, 6, 0, MDT_ADLIB | MDT_MIDI | MDT_PREFER_GM, GF_USE_KEY, UNK, GUIO4(GUIO_NOSPEECH, GUIO_RENDEREGA, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI)},
- {"ft", "", 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO3(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI)},
- {"ft", "Demo", 0, GID_FT, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO2(GUIO_NOMIDI, GUIO_ORIGINALGUI)},
+ {"ft", "", 0, GID_FT, 7, 0, MDT_NONE, 0, UNK, GUIO4(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
+ {"ft", "Demo", 0, GID_FT, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO3(GUIO_NOMIDI, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
- {"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO3(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI)},
- {"dig", "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO2(GUIO_NOMIDI, GUIO_ORIGINALGUI)},
- {"dig", "Steam", "steam", GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO3(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI)},
+ {"dig", "", 0, GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO4(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
+ {"dig", "Demo", 0, GID_DIG, 7, 0, MDT_NONE, GF_DEMO, UNK, GUIO3(GUIO_NOMIDI, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
+ {"dig", "Steam", "steam", GID_DIG, 7, 0, MDT_NONE, 0, UNK, GUIO4(GUIO_NOMIDI, GUIO_ENHANCEMENTS, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
- {"comi", "", 0, GID_CMI, 8, 0, MDT_NONE, 0, Common::kPlatformWindows, GUIO3(GUIO_NOMIDI, GUIO_NOASPECT, GUIO_ORIGINALGUI)},
- {"comi", "Demo", 0, GID_CMI, 8, 0, MDT_NONE, GF_DEMO, Common::kPlatformWindows, GUIO3(GUIO_NOMIDI, GUIO_NOASPECT, GUIO_ORIGINALGUI)},
+ {"comi", "", 0, GID_CMI, 8, 0, MDT_NONE, 0, Common::kPlatformWindows, GUIO4(GUIO_NOMIDI, GUIO_NOASPECT, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
+ {"comi", "Demo", 0, GID_CMI, 8, 0, MDT_NONE, GF_DEMO, Common::kPlatformWindows, GUIO4(GUIO_NOMIDI, GUIO_NOASPECT, GUIO_ORIGINALGUI, GUIO_LOWLATENCYAUDIO)},
// Humongous Entertainment Scumm Version 6
{"activity", "", 0, GID_HEGAME, 6, 62, MDT_ADLIB | MDT_MIDI, GF_USE_KEY, UNK, GUIO2(GUIO_NOLAUNCHLOAD, GUIO_AUDIO_OVERRIDE)},
diff --git a/engines/scumm/metaengine.cpp b/engines/scumm/metaengine.cpp
index 20b220a2a07..ec82248d205 100644
--- a/engines/scumm/metaengine.cpp
+++ b/engines/scumm/metaengine.cpp
@@ -670,6 +670,15 @@ static const ExtraGuiOption enableOriginalGUI = {
0
};
+static const ExtraGuiOption enableLowLatencyAudio = {
+ _s("Enable low latency audio mode"),
+ _s("Allows the game to use low latency audio, at the cost of sound accuracy."),
+ "dimuse_low_latency_mode",
+ false,
+ 0,
+ 0
+};
+
const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &target) const {
ExtraGuiOptions options;
// Query the GUI options
@@ -685,6 +694,9 @@ const ExtraGuiOptions ScummMetaEngine::getExtraGuiOptions(const Common::String &
if (target.empty() || guiOptions.contains(GUIO_ENHANCEMENTS)) {
options.push_back(enableEnhancements);
}
+ if (target.empty() || guiOptions.contains(GUIO_LOWLATENCYAUDIO)) {
+ options.push_back(enableLowLatencyAudio);
+ }
if (target.empty() || guiOptions.contains(GUIO_AUDIO_OVERRIDE)) {
options.push_back(audioOverride);
}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 583a35dc570..91b72ac5614 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1449,7 +1449,7 @@ void ScummEngine_v7::setupScumm(const Common::String &macResourceFile) {
lowLatencyMode = ConfMan.getBool("dimuse_low_latency_mode");
}
- _musicEngine = _imuseDigital = new IMuseDigital(this, sampleRate, _mixer, &_resourceAccessMutex);
+ _musicEngine = _imuseDigital = new IMuseDigital(this, sampleRate, _mixer, &_resourceAccessMutex, lowLatencyMode);
if (filesAreCompressed) {
GUI::MessageDialog dialog(_(
Commit: 8d6737e16fd1fbebdb40366eb87319c947fe90cf
https://github.com/scummvm/scummvm/commit/8d6737e16fd1fbebdb40366eb87319c947fe90cf
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-01-10T21:17:23+01:00
Commit Message:
SCUMM: DIMUSE: Increase buffer size for low latency mode
Changed paths:
engines/scumm/imuse_digi/dimuse_engine.cpp
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 49ceb7c0b55..2856115d62d 100644
--- a/engines/scumm/imuse_digi/dimuse_engine.cpp
+++ b/engines/scumm/imuse_digi/dimuse_engine.cpp
@@ -50,9 +50,14 @@ IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *
_callbackFps = DIMUSE_TIMER_BASE_RATE_HZ;
_usecPerInt = DIMUSE_TIMER_BASE_RATE_USEC;
+ _lowLatencyMode = lowLatencyMode;
_internalSampleRate = sampleRate;
_internalFeedSize = (int)(DIMUSE_BASE_FEEDSIZE * ((float)_internalSampleRate / DIMUSE_BASE_SAMPLERATE));
+ if (_lowLatencyMode) {
+ _internalFeedSize *= 2;
+ }
+
_splayer = nullptr;
_isEarlyDiMUSE = (_vm->_game.id == GID_FT || (_vm->_game.id == GID_DIG && _vm->_game.features & GF_DEMO));
@@ -83,7 +88,6 @@ IMuseDigital::IMuseDigital(ScummEngine_v7 *scumm, int sampleRate, Audio::Mixer *
_isEngineDisabled = false;
_checkForUnderrun = false;
_underrunCooldown = 0;
- _lowLatencyMode = lowLatencyMode;
_audioNames = nullptr;
_numAudioNames = 0;
diff --git a/engines/scumm/imuse_digi/dimuse_tracks.cpp b/engines/scumm/imuse_digi/dimuse_tracks.cpp
index 5b9e7888391..811f08e8e32 100644
--- a/engines/scumm/imuse_digi/dimuse_tracks.cpp
+++ b/engines/scumm/imuse_digi/dimuse_tracks.cpp
@@ -259,8 +259,8 @@ void IMuseDigital::tracksLowLatencyCallback() {
int idx = track ? track->index : -1;
// We use a separate queue cardinality handling, since SMUSH audio and iMUSE audio can overlap...
- bool canQueueBufs = (int)_internalMixer->getStream(idx)->numQueuedStreams() < _maxQueuedStreams;
- bool canQueueFtSmush = (int)_internalMixer->getStream(-1)->numQueuedStreams() < _maxQueuedStreams;
+ bool canQueueBufs = (int)_internalMixer->getStream(idx)->numQueuedStreams() < (_maxQueuedStreams + 1);
+ bool canQueueFtSmush = (int)_internalMixer->getStream(-1)->numQueuedStreams() < (_maxQueuedStreams + 1);
if (canQueueBufs) {
if (track)
More information about the Scummvm-git-logs
mailing list