scummvm/engines/scumm/imuse_digi/dimuse_tracks.cpp
Torbjörn Andersson f300e9f87e SCUMM: Initialize data to avoid Valgrind warnings on saving
This is enough to silence all the Valgrind warnings I got when saving in
The Dig.
2024-11-16 18:40:51 +02:00

819 lines
23 KiB
C++

/* ScummVM - Graphic Adventure Engine
*
* ScummVM is the legal property of its developers, whose names
* are too numerous to list here. Please refer to the COPYRIGHT
* file distributed with this source distribution.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "scumm/imuse_digi/dimuse_engine.h"
namespace Scumm {
int IMuseDigital::tracksInit() {
_trackCount = _lowLatencyMode ? DIMUSE_MAX_TRACKS : 6;
_tracksPauseTimer = 0;
_trackList = nullptr;
if (waveOutInit(&waveOutSettings))
return -1;
if (_internalMixer->init(waveOutSettings.bytesPerSample,
waveOutSettings.numChannels,
waveOutSettings.mixBuf,
waveOutSettings.mixBufSize,
waveOutSettings.sizeSampleKB,
_trackCount) ||
dispatchInit() ||
streamerInit()) {
return -1;
}
for (int l = 0; l < _trackCount; l++) {
_tracks[l].index = l;
_tracks[l].prev = nullptr;
_tracks[l].next = nullptr;
_tracks[l].dispatchPtr = dispatchGetDispatchByTrackId(l);
_tracks[l].dispatchPtr->trackPtr = &_tracks[l];
_tracks[l].soundId = 0;
_tracks[l].group = 0;
_tracks[l].marker = 0;
_tracks[l].priority = 0;
_tracks[l].vol = 0;
_tracks[l].effVol = 0;
_tracks[l].pan = 0;
_tracks[l].detune = 0;
_tracks[l].transpose = 0;
_tracks[l].pitchShift = 0;
_tracks[l].mailbox = 0;
_tracks[l].jumpHook = 0;
_tracks[l].syncSize_0 = 0;
_tracks[l].syncSize_1 = 0;
_tracks[l].syncSize_2 = 0;
_tracks[l].syncSize_3 = 0;
_tracks[l].syncPtr_0 = nullptr;
_tracks[l].syncPtr_1 = nullptr;
_tracks[l].syncPtr_2 = nullptr;
_tracks[l].syncPtr_3 = nullptr;
}
return 0;
}
void IMuseDigital::tracksPause() {
_tracksPauseTimer = 1;
}
void IMuseDigital::tracksResume() {
_tracksPauseTimer = 0;
}
void IMuseDigital::tracksSaveLoad(Common::Serializer &ser) {
Common::StackLock lock(*_mutex);
dispatchSaveLoad(ser);
for (int l = 0; l < _trackCount; l++) {
ser.syncAsSint32LE(_tracks[l].soundId, VER(103));
ser.syncAsSint32LE(_tracks[l].marker, VER(103));
ser.syncAsSint32LE(_tracks[l].group, VER(103));
ser.syncAsSint32LE(_tracks[l].priority, VER(103));
ser.syncAsSint32LE(_tracks[l].vol, VER(103));
ser.syncAsSint32LE(_tracks[l].effVol, VER(103));
ser.syncAsSint32LE(_tracks[l].pan, VER(103));
ser.syncAsSint32LE(_tracks[l].detune, VER(103));
ser.syncAsSint32LE(_tracks[l].transpose, VER(103));
ser.syncAsSint32LE(_tracks[l].pitchShift, VER(103));
ser.syncAsSint32LE(_tracks[l].mailbox, VER(103));
ser.syncAsSint32LE(_tracks[l].jumpHook, VER(103));
if (_vm->_game.id == GID_CMI) {
ser.syncAsSint32LE(_tracks[l].syncSize_0, VER(103));
ser.syncAsSint32LE(_tracks[l].syncSize_1, VER(103));
ser.syncAsSint32LE(_tracks[l].syncSize_2, VER(103));
ser.syncAsSint32LE(_tracks[l].syncSize_3, VER(103));
if (_tracks[l].syncSize_0) {
if (ser.isLoading())
_tracks[l].syncPtr_0 = (byte *)malloc(_tracks[l].syncSize_0);
ser.syncArray(_tracks[l].syncPtr_0, _tracks[l].syncSize_0, Common::Serializer::Byte, VER(103));
}
if (_tracks[l].syncSize_1) {
if (ser.isLoading())
_tracks[l].syncPtr_1 = (byte *)malloc(_tracks[l].syncSize_1);
ser.syncArray(_tracks[l].syncPtr_1, _tracks[l].syncSize_1, Common::Serializer::Byte, VER(103));
}
if (_tracks[l].syncSize_2) {
if (ser.isLoading())
_tracks[l].syncPtr_2 = (byte *)malloc(_tracks[l].syncSize_2);
ser.syncArray(_tracks[l].syncPtr_2, _tracks[l].syncSize_2, Common::Serializer::Byte, VER(103));
}
if (_tracks[l].syncSize_3) {
if (ser.isLoading())
_tracks[l].syncPtr_3 = (byte *)malloc(_tracks[l].syncSize_3);
ser.syncArray(_tracks[l].syncPtr_3, _tracks[l].syncSize_3, Common::Serializer::Byte, VER(103));
}
}
}
if (ser.isLoading()) {
for (int l = 0; l < _trackCount; l++) {
_tracks[l].prev = nullptr;
_tracks[l].next = nullptr;
_tracks[l].dispatchPtr = dispatchGetDispatchByTrackId(l);
_tracks[l].dispatchPtr->trackPtr = &_tracks[l];
if (_tracks[l].soundId) {
addTrackToList(&_trackList, &_tracks[l]);
}
}
dispatchRestoreStreamZones();
}
}
void IMuseDigital::tracksSetGroupVol() {
IMuseDigiTrack* curTrack = _trackList;
while (curTrack) {
curTrack->effVol = ((curTrack->vol + 1) * _groupsHandler->getGroupVol(curTrack->group)) / 128;
curTrack = curTrack->next;
}
}
void IMuseDigital::tracksCallback() {
if (_tracksPauseTimer) {
if (++_tracksPauseTimer < 3)
return;
_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) {
if (!_isEarlyDiMUSE)
dispatchPredictFirstStream();
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);
}
if (!_tracksPauseTimer) {
IMuseDigiTrack *track = _trackList;
while (track) {
IMuseDigiTrack *next = track->next;
if (_isEarlyDiMUSE) {
dispatchProcessDispatches(track, _outputFeedSize);
} else {
dispatchProcessDispatches(track, _outputFeedSize, _outputSampleRate);
}
track = next;
};
}
_internalMixer->loop(&_outputAudioBuffer, _outputFeedSize);
// The Dig tries to write a second time
if (!_isEarlyDiMUSE && _vm->_game.id == GID_DIG) {
waveOutWrite(&_outputAudioBuffer, _outputFeedSize, _outputSampleRate);
}
}
}
}
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 + 1);
bool canQueueFtSmush = _internalMixer->getStream(-1) != nullptr;
if (canQueueFtSmush) {
canQueueFtSmush &= (int)_internalMixer->getStream(-1)->numQueuedStreams() < (_maxQueuedStreams + 1);
}
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);
debug(5, "IMuseDigital::tracksStartSound(): sound %d with priority %d and group %d", soundId, priority, group);
IMuseDigiTrack *allocatedTrack = tracksReserveTrack(priority);
if (!allocatedTrack) {
debug(5, "IMuseDigital::tracksStartSound(): ERROR: couldn't find a spare track to allocate sound %d", soundId);
return -6;
}
allocatedTrack->soundId = soundId;
allocatedTrack->marker = 0;
allocatedTrack->group = 0;
allocatedTrack->priority = priority;
allocatedTrack->vol = 127;
allocatedTrack->effVol = _groupsHandler->getGroupVol(0);
allocatedTrack->pan = 64;
allocatedTrack->detune = 0;
allocatedTrack->transpose = 0;
allocatedTrack->pitchShift = 256;
allocatedTrack->mailbox = 0;
allocatedTrack->jumpHook = 0;
allocatedTrack->syncSize_0 = 0;
allocatedTrack->syncPtr_0 = nullptr;
allocatedTrack->syncSize_1 = 0;
allocatedTrack->syncPtr_1 = nullptr;
allocatedTrack->syncSize_2 = 0;
allocatedTrack->syncPtr_2 = nullptr;
allocatedTrack->syncSize_3 = 0;
allocatedTrack->syncPtr_3 = nullptr;
if (dispatchAllocateSound(allocatedTrack, group)) {
debug(5, "IMuseDigital::tracksStartSound(): ERROR: dispatch couldn't start sound %d", soundId);
allocatedTrack->soundId = 0;
return -1;
}
_mutex->lock();
addTrackToList(&_trackList, allocatedTrack);
_mutex->unlock();
return 0;
}
int IMuseDigital::tracksStopSound(int soundId) {
if (!_trackList)
return -1;
IMuseDigiTrack *nextTrack = _trackList;
IMuseDigiTrack *curTrack;
while (nextTrack) {
curTrack = nextTrack;
nextTrack = curTrack->next;
if (curTrack->soundId == soundId) {
tracksClear(curTrack);
}
}
return 0;
}
int IMuseDigital::tracksStopAllSounds() {
Common::StackLock lock(*_mutex);
IMuseDigiTrack *nextTrack = _trackList;
IMuseDigiTrack *curTrack;
while (nextTrack) {
curTrack = nextTrack;
nextTrack = curTrack->next;
tracksClear(curTrack);
}
_filesHandler->closeAllSounds();
return 0;
}
int IMuseDigital::tracksGetNextSound(int soundId) {
int foundSoundId = 0;
IMuseDigiTrack *track = _trackList;
while (track) {
if (track->soundId > soundId) {
if (!foundSoundId || track->soundId < foundSoundId) {
foundSoundId = track->soundId;
}
}
track = track->next;
};
return foundSoundId;
}
int IMuseDigital::tracksQueryStream(int soundId, int32 &bufSize, int32 &criticalSize, int32 &freeSpace, int &paused) {
if (!_trackList) {
debug(5, "IMuseDigital::tracksQueryStream(): WARNING: empty trackList, ignoring call...");
return isFTSoundEngine() ? 0 : -1;
}
IMuseDigiTrack *track = _trackList;
if (isFTSoundEngine()) {
IMuseDigiTrack *chosenTrack = nullptr;
do {
if (track->soundId > soundId && (!chosenTrack || track->soundId < chosenTrack->soundId)) {
if (track->dispatchPtr->streamPtr)
chosenTrack = track;
}
track = track->next;
} while (track);
if (!chosenTrack)
return 0;
streamerQueryStream(chosenTrack->dispatchPtr->streamPtr, bufSize, criticalSize, freeSpace, paused);
return chosenTrack->soundId;
} else {
do {
if (track->soundId) {
if (soundId == track->soundId && track->dispatchPtr->streamPtr) {
streamerQueryStream(track->dispatchPtr->streamPtr, bufSize, criticalSize, freeSpace, paused);
return 0;
}
}
track = track->next;
} while (track);
debug(5, "IMuseDigital::tracksQueryStream(): WARNING: couldn't find sound %d in trackList, ignoring call...", soundId);
return -1;
}
}
int IMuseDigital::tracksFeedStream(int soundId, uint8 *srcBuf, int32 sizeToFeed, int paused) {
if (!_trackList)
return -1;
IMuseDigiTrack *track = _trackList;
do {
if (track->soundId != 0) {
if (track->soundId == soundId && track->dispatchPtr->streamPtr) {
streamerFeedStream(track->dispatchPtr->streamPtr, srcBuf, sizeToFeed, paused);
return 0;
}
}
track = track->next;
} while (track);
return -1;
}
void IMuseDigital::tracksClear(IMuseDigiTrack *trackPtr) {
if (_vm->_game.id == GID_CMI) {
if (trackPtr->syncPtr_0) {
trackPtr->syncSize_0 = 0;
free(trackPtr->syncPtr_0);
trackPtr->syncPtr_0 = nullptr;
}
if (trackPtr->syncPtr_1) {
trackPtr->syncSize_1 = 0;
free(trackPtr->syncPtr_1);
trackPtr->syncPtr_1 = nullptr;
}
if (trackPtr->syncPtr_2) {
trackPtr->syncSize_2 = 0;
free(trackPtr->syncPtr_2);
trackPtr->syncPtr_2 = nullptr;
}
if (trackPtr->syncPtr_3) {
trackPtr->syncSize_3 = 0;
free(trackPtr->syncPtr_3);
trackPtr->syncPtr_3 = nullptr;
}
}
removeTrackFromList(&_trackList, trackPtr);
dispatchRelease(trackPtr);
_fadesHandler->clearFadeStatus(trackPtr->soundId, -1);
_triggersHandler->clearTrigger(trackPtr->soundId, _emptyMarker, -1);
// Unlock the sound, if it's loaded as a resource
if (trackPtr->soundId < 1000 && trackPtr->soundId) {
_vm->_res->unlock(rtSound, trackPtr->soundId);
}
if (_lowLatencyMode)
waveOutEmptyBuffer(trackPtr->index);
trackPtr->soundId = 0;
}
int IMuseDigital::tracksSetParam(int soundId, int opcode, int value) {
if (!_trackList)
return -4;
IMuseDigiTrack *track = _trackList;
while (track) {
if (track->soundId == soundId) {
switch (opcode) {
case DIMUSE_P_GROUP:
if (value >= 16)
return -5;
track->group = value;
track->effVol = ((track->vol + 1) * _groupsHandler->getGroupVol(value)) / 128;
return 0;
case DIMUSE_P_PRIORITY:
if (value > 127)
return -5;
track->priority = value;
return 0;
case DIMUSE_P_VOLUME:
if (value > 127)
return -5;
track->vol = value;
track->effVol = ((value + 1) * _groupsHandler->getGroupVol(track->group)) / 128;
return 0;
case DIMUSE_P_PAN:
if (value > 127)
return -5;
track->pan = value;
return 0;
case DIMUSE_P_DETUNE:
if (value < -9216 || value > 9216)
return -5;
track->detune = value;
track->pitchShift = value + track->transpose * 256;
return 0;
case DIMUSE_P_TRANSPOSE:
if (_vm->_game.id == GID_DIG || _vm->_game.id == GID_FT) {
if (value < -12 || value > 12)
return -5;
if (value == 0) {
track->transpose = 0;
} else {
track->transpose = clampTuning(track->detune + value, -12, 12);
}
track->pitchShift = track->detune + (track->transpose * 256);
} else if (_vm->_game.id == GID_CMI) {
if (value < 0 || value > 4095)
return -5;
track->pitchShift = value;
}
return 0;
case DIMUSE_P_MAILBOX:
track->mailbox = value;
return 0;
default:
debug(5, "IMuseDigital::tracksSetParam(): unknown opcode %d", opcode);
return -5;
}
}
track = track->next;
}
return -4;
}
int IMuseDigital::tracksGetParam(int soundId, int opcode) {
if (!_trackList) {
if (opcode != DIMUSE_P_SND_TRACK_NUM)
return -4;
else
return 0;
}
IMuseDigiTrack *track = _trackList;
int l = 0;
do {
if (track)
l++;
if (track->soundId == soundId) {
switch (opcode) {
case DIMUSE_P_BOGUS_ID:
return -1;
case DIMUSE_P_SND_TRACK_NUM:
return l;
case DIMUSE_P_TRIGS_SNDS:
return -1;
case DIMUSE_P_MARKER:
return track->marker;
case DIMUSE_P_GROUP:
return track->group;
case DIMUSE_P_PRIORITY:
return track->priority;
case DIMUSE_P_VOLUME:
return track->vol;
case DIMUSE_P_PAN:
return track->pan;
case DIMUSE_P_DETUNE:
return track->detune;
case DIMUSE_P_TRANSPOSE:
return track->transpose;
case DIMUSE_P_MAILBOX:
return track->mailbox;
case DIMUSE_P_SND_HAS_STREAM:
return (track->dispatchPtr->streamPtr != 0);
case DIMUSE_P_STREAM_BUFID:
return track->dispatchPtr->streamBufID;
case DIMUSE_P_SND_POS_IN_MS: // getCurSoundPositionInMs
if (track->dispatchPtr->wordSize == 0)
return 0;
if (track->dispatchPtr->sampleRate == 0)
return 0;
if (track->dispatchPtr->channelCount == 0)
return 0;
return (track->dispatchPtr->currentOffset * 5) / (((track->dispatchPtr->wordSize / 8) * track->dispatchPtr->sampleRate * track->dispatchPtr->channelCount) / 200);
default:
return -5;
}
}
track = track->next;
} while (track);
return 0;
}
int IMuseDigital::tracksLipSync(int soundId, int syncId, int msPos, int32 &width, int32 &height) {
int32 w, h;
byte *syncPtr = nullptr;
int32 syncSize = 0;
IMuseDigiTrack *curTrack;
int16 val;
w = 0;
h = 0;
curTrack = _trackList;
if (msPos >= 0) {
// Check for an invalid timestamp:
// this has to be a suitable 2-bytes word...
if (((msPos >> 4) & 0xFFFF0000) != 0) {
return -5;
} else {
if (_trackList) {
do {
if (curTrack->soundId == soundId)
break;
curTrack = curTrack->next;
} while (curTrack);
}
if (curTrack) {
if (syncId >= 0 && syncId < 4) {
if (syncId == 0) {
syncPtr = curTrack->syncPtr_0;
syncSize = curTrack->syncSize_0;
} else if (syncId == 1) {
syncPtr = curTrack->syncPtr_1;
syncSize = curTrack->syncSize_1;
} else if (syncId == 2) {
syncPtr = curTrack->syncPtr_2;
syncSize = curTrack->syncSize_2;
} else if (syncId == 3) {
syncPtr = curTrack->syncPtr_3;
syncSize = curTrack->syncSize_3;
}
if (syncSize && syncPtr) {
// SYNC data is packed in a number of 4-bytes entries, in the following order:
// - Width and height values, packed as one byte each, next to each other;
// - The time position of said values, packed as an unsigned word (2-bytes).
// Given an input timestamp (in ms), we're going to get its representation as 60Hz
// increments by dividing it by 16, then we're going to search the SYNC data from
// the beginning to find the first entry with a timestamp being equal or greater
// our 60Hz timestamp.
uint16 inputTs = msPos >> 4;
int32 numOfEntries = (syncSize >> 2);
uint16 *syncDataWordPtr = (uint16 *)syncPtr;
uint16 curEntryTs = 0;
int idx;
for (idx = 0; idx < numOfEntries; idx++) {
curEntryTs = READ_LE_UINT16(&syncDataWordPtr[idx * 2 + 1]);
if (curEntryTs >= inputTs) {
break;
}
}
// If no relevant entry is found, or if the found entry timestamp is strictly greater
// than ours, then we get the previous entry. If no entry was found, this will get the
// last entry in our data block.
if (idx == numOfEntries || curEntryTs > inputTs) {
idx--;
}
// Finally, extract width and height values and remove
// their signs by performing AND operations with 0x7F...
val = READ_LE_INT16(&syncDataWordPtr[idx * 2]);
w = (val >> 8) & 0x7F;
h = val & 0x7F;
}
}
} else {
return -4;
}
}
}
width = w;
height = h;
return 0;
}
int IMuseDigital::tracksSetHook(int soundId, int hookId) {
if (_isEarlyDiMUSE)
return -2;
if (hookId > 128)
return -5;
if (!_trackList)
return -4;
IMuseDigiTrack *track = _trackList;
while (track->soundId != soundId) {
track = track->next;
if (!track)
return -4;
}
track->jumpHook = hookId;
return 0;
}
int IMuseDigital::tracksGetHook(int soundId) {
if (_isEarlyDiMUSE)
return -2;
if (!_trackList)
return -4;
IMuseDigiTrack *track = _trackList;
while (track->soundId != soundId) {
track = track->next;
if (!track)
return -4;
}
return track->jumpHook;
}
IMuseDigiTrack *IMuseDigital::tracksReserveTrack(int priority) {
IMuseDigiTrack *curTrack;
IMuseDigiTrack *reservedTrack = nullptr;
int minPriorityFound;
// Pick the track from the pool of free tracks
for (int i = 0; i < _trackCount; i++) {
reservedTrack = &_tracks[i];
if (!reservedTrack->soundId) {
return reservedTrack;
}
}
// If no free track is found, steal the lower priority one
curTrack = _trackList;
for (minPriorityFound = 127; curTrack; curTrack = curTrack->next) {
if (curTrack->priority <= minPriorityFound) {
minPriorityFound = curTrack->priority;
reservedTrack = curTrack;
}
}
if (reservedTrack && priority >= minPriorityFound) {
tracksClear(reservedTrack);
}
return reservedTrack;
}
void IMuseDigital::tracksDeinit() {
tracksStopAllSounds();
}
} // End of namespace Scumm