[Scummvm-git-logs] scummvm master -> 97de0d932d6c8b362dd1bc9de7045c6986be5347
sev-
noreply at scummvm.org
Fri Aug 18 20:21:10 UTC 2023
This automated email contains information about 50 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
7540bfc822 SCUMM: HE: Begin audio engine rewrite
454a1d1593 SCUMM: HE (Sound): Clean-up magic numbers and implement modifySound
9b8992f0c1 SCUMM: HE (Sound): Fix incorrect frequency values
f4455e773e SCUMM: Relabel part of the general sound code
f2d409c3a8 SCUMM: HE (Sound): Implement some resource loading routines
736a2a2621 SCUMM: HE (Sound): Implement RIFF format handling
4b42a4bb44 SCUMM: HE (Sound): Add empty mixer files
d11e49ade2 SCUMM: HE (Sound): Implement XSOU handling
44ae88f230 SCUMM: HE (Sound): Implement most audio engine functions
e457f75908 SCUMM: HE (Sound): Fix crashes in HE versions without sound modifiers
9f89e3e823 SCUMM: HE (Sound): Finish wiring up high level functions
806ff5147a SCUMM: HE (Sound): Suddenly... sound!
f733881360 SCUMM: HE (Sound): Implement Miles feedMixer() for Moonbase
a18e86d077 SCUMM: HE (Sound): Fix clicking sound at the end of wav files
0ef7127dd8 SCUMM: HE (Sound): Fix in-battle music for Moonbase Commander
4babb8ffd3 SCUMM: HE (Sound): Fix HE70 return value for getSoundPosition()
14bc03c4e1 SCUMM: HE (Sound): Full audio support for Moonbase Commander
4d7d4a8706 SCUMM: HE (Sound): Add preliminary Miles support for other games
f3c625437d SCUMM: HE (Sound): Implement some ADPCM stuff
4037e4889f SCUMM: HE (Sound): Finish IMA ADPCM support for voice and SFX
a63178262f SCUMM: HE (Sound): Fix crash in Moonbase with invalid modifiers
2a02ec9b7a SCUMM: HE (Sound): Add support for PJGames and Backyard Soccer 2004
f9e54b5006 SCUMM: HE (Sound): Complete support for all games using Miles Sound System
815e280da4 SCUMM: HE (Sound): Implement software mixer for all the other games
85cde3582d SCUMM: HE (Sound): Remove leftover debug messages
26b6a9d6ef SCUMM: HE (Sound): Reimplement support for Lokalizator.big files
d6812afea7 SCUMM: HE: Remove leftover comment
6da4114418 SCUMM: HE (Sound): Fix subtitles behaviour
7dd3014087 SCUMM: HE (Sound): Implement early callbacks and getChannelPosition()
79803bbe54 SCUMM: HE (Sound): Reimplement separate volume controls
47301a17ff SCUMM: HE (Sound): Fix music reloading after exiting save screen
ae49dc0c7e SCUMM: HE (Sound): Reimplement separate volume controls (pt.2)
3e65e4c4aa SCUMM: HE (Sound): Fix early callbacks cutting sound too early
7287a5ff8e SCUMM: HE (Sound): Reimplement audio modding
972c3ca797 SCUMM: HE (Sound): Fix lipsync regression
6482de2194 SCUMM: HE (Sound): Wire up pause routines
cd32ede377 SCUMM: HE (Sound): Fix warnings
ce7f42238b SCUMM: HE (Sound): Fix warnings pt.2
081ab64940 SCUMM: HE (Sound): Revert some warning fixes as they broke createSound()
e394ce8b7b SCUMM: HE (Sound): Add temp workaround for sounds created within createSound()
b014bd8d9c SCUMM: HE (Sound): Fix miscalculation of channel timer
e4f408604a SCUMM: HE (Sound): Wait for the spooling stream to end to callback
9b4cba48c9 SCUMM: HE (Sound): Slightly simplify lipsync code
3b4f57d33f SCUMM: HE (Sound): Change position of he_mixer.o in module.mk
3ec70bf972 SCUMM: HE (Sound): Address code review comments
989eda7b08 SCUMM: HE (Sound): Show message box for dev code path
65655e9908 SCUMM: HE (Sound): Fix update of age member of _heChannel
3bd897e26f SCUMM: HE (Sound): Fix const warning
4aa6c3d34a SCUMM: HE (Sound): Remove workaround for looping sounds
97de0d932d SCUMM: HE (Sound): Fix compiling when HE engine is not enabled
Commit: 7540bfc8222333111969e544ba6fff558fafb820
https://github.com/scummvm/scummvm/commit/7540bfc8222333111969e544ba6fff558fafb820
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE: Begin audio engine rewrite
Changed paths:
engines/scumm/he/script_v100he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/resource.cpp
engines/scumm/script.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/vars.cpp
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index fa090ce09a0..aeb92b6ca1e 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -747,18 +747,17 @@ void ScummEngine_v100he::o100_createSound() {
byte subOp = fetchScriptByte();
switch (subOp) {
- case 0:
+ case SO_INIT:
_heSndResId = pop();
break;
- case 53:
+ case SO_NEW:
createSound(_heSndResId, -1);
break;
- case 92:
- // dummy case
- break;
- case 128:
+ case SO_SOUND_ADD:
createSound(_heSndResId, pop());
break;
+ case SO_END:
+ break;
default:
error("o100_createSound: default case %d", subOp);
}
@@ -1738,7 +1737,7 @@ void ScummEngine_v100he::o100_setSystemMessage() {
void ScummEngine_v100he::o100_soundOps() {
byte filename[260];
int var, value;
-
+ //da qui
byte subOp = fetchScriptByte();
switch (subOp) {
@@ -2469,6 +2468,7 @@ void ScummEngine_v100he::o100_getResourceSize() {
break;
case SO_SOUND:
push(getSoundResourceSize(resid));
+ // TODO: Implement the case in which resid < _numSounds
return;
default:
error("o100_getResourceSize: default type %d", subOp);
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 279e82c286a..d6f5c09d457 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -44,13 +44,14 @@
namespace Scumm {
-SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer)
+SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
:
Sound(parent, mixer, false),
_vm((ScummEngine_v60he *)parent),
_overrideFreq(0),
_heMusic(nullptr),
- _heMusicTracks(0) {
+ _heMusicTracks(0),
+ _mutex(mutex) {
memset(_heChannel, 0, sizeof(_heChannel));
_heSoundChannels = new Audio::SoundHandle[8]();
@@ -160,9 +161,9 @@ void SoundHE::stopSound(int sound) {
if (_heChannel[i].sound == sound) {
_heChannel[i].sound = 0;
_heChannel[i].priority = 0;
- _heChannel[i].rate = 0;
+ _heChannel[i].frequency = 0;
_heChannel[i].timer = 0;
- _heChannel[i].sbngBlock = 0;
+ _heChannel[i].hasSoundTokens = false;
_heChannel[i].codeOffs = 0;
memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
}
@@ -181,6 +182,27 @@ void SoundHE::stopAllSounds() {
Sound::stopAllSounds();
}
+int SoundHE::findSoundChannel(int sound) {
+ if (sound >= HSND_CHANNEL_0) {
+ int channel = sound - HSND_CHANNEL_0;
+
+ if (channel < 0 || channel > HSND_MAX_CHANNELS - 1) {
+ error("SoundHE::findSoundChannel(): ERROR: Channel %d out of range (%d-%d)",
+ channel, 0, HSND_MAX_CHANNELS - 1);
+ }
+
+ return channel;
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_heChannel); ++i) {
+ if (sound == _heChannel[i].sound) {
+ return i;
+ }
+ }
+
+ return -1;
+}
+
void SoundHE::setupSound() {
Sound::setupSound();
@@ -190,7 +212,7 @@ void SoundHE::setupSound() {
}
void SoundHE::stopSoundChannel(int chan) {
- if (_heChannel[chan].sound == 1) {
+ if (_heChannel[chan].sound == HSND_TALKIE_SLOT || chan == _vm->VAR(_vm->VAR_TALK_CHANNEL)) {
_vm->_haveMsg = 3;
_vm->_talkDelay = 0;
}
@@ -199,9 +221,9 @@ void SoundHE::stopSoundChannel(int chan) {
_heChannel[chan].sound = 0;
_heChannel[chan].priority = 0;
- _heChannel[chan].rate = 0;
+ _heChannel[chan].frequency = 0;
_heChannel[chan].timer = 0;
- _heChannel[chan].sbngBlock = 0;
+ _heChannel[chan].hasSoundTokens = false;
_heChannel[chan].codeOffs = 0;
memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
@@ -218,9 +240,9 @@ void SoundHE::stopSoundChannel(int chan) {
int SoundHE::findFreeSoundChannel() {
int chan, min;
- min = _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS);
+ min = _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS);
if (min == 0) {
- _vm->VAR(_vm->VAR_RESERVED_SOUND_CHANNELS) = 8;
+ _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS) = 8;
return 1;
}
@@ -236,7 +258,7 @@ int SoundHE::findFreeSoundChannel() {
return min;
}
-int SoundHE::isSoundCodeUsed(int sound) {
+bool SoundHE::isSoundCodeUsed(int sound) {
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
if (_heChannel[i].sound == sound)
@@ -244,9 +266,9 @@ int SoundHE::isSoundCodeUsed(int sound) {
}
if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
- return _heChannel[chan].sbngBlock;
+ return _heChannel[chan].hasSoundTokens;
} else {
- return 0;
+ return false;
}
}
@@ -258,7 +280,7 @@ int SoundHE::getSoundPos(int sound) {
}
if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
- int time = _vm->getHETimer(chan + 4) * _heChannel[chan].rate / 1000;
+ int time = _vm->getHETimer(chan + 4) * _heChannel[chan].frequency / 1000;
return time;
} else {
return 0;
@@ -351,71 +373,188 @@ bool SoundHE::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
return 0;
}
-void SoundHE::processSoundCode() {
- byte *codePtr;
- int chan, tmr, size, time;
+void SoundHE::handleSoundFrame() {
+ _soundsDebugFrameCounter++;
- for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
- if (_heChannel[chan].sound == 0) {
- continue;
+ if (_stopActorTalkingFlag) {
+ _vm->stopTalk();
+ _stopActorTalkingFlag = false;
+ }
+
+ unqueueSoundCallbackScripts();
+
+ runSoundCode();
+ checkSoundTimeouts();
+}
+
+void SoundHE::unqueueSoundCallbackScripts() {
+ Common::StackLock lock(*_mutex);
+
+ for (int i = 0; i < _soundCallbacksQueueSize; i++) {
+
+ if (_soundCallbackScripts[i].sound) {
+ int args[NUM_SCRIPT_LOCAL];
+
+ memset(args, 0, sizeof(args));
+ args[0] = _soundCallbackScripts[i].sound;
+ args[1] = _soundCallbackScripts[i].channel;
+ args[2] = 0;
+ args[3] = 0;
+
+ _vm->runScript(_vm->VAR(_vm->VAR_SOUND_CALLBACK_SCRIPT), 0, 0, args);
}
- if (_heChannel[chan].codeOffs == -1) {
+ _soundCallbackScripts[i].sound = 0;
+ _soundCallbackScripts[i].channel = 0;
+ _soundCallbackScripts[i].whatFrame = 0;
+ }
+
+ _soundCallbacksQueueSize = 0;
+}
+
+void SoundHE::checkSoundTimeouts() {
+ byte *soundPtr;
+ int chan, soundPos, len, freq;
+
+ for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
+ if (_heChannel[chan].sound == 0 || _heChannel[chan].timer == 0)
continue;
+
+ if (_vm->getHETimer(chan + HSND_TIMER_SLOT) > _heChannel[chan].timer) {
+ digitalSoundCallback(HSND_SOUND_TIMEOUT, chan);
}
+ }
+}
- tmr = _vm->getHETimer(chan + 4) * _heChannel[chan].rate / 1000;
- tmr += _vm->VAR(_vm->VAR_SOUNDCODE_TMR);
- if (tmr < 0)
- tmr = 0;
+void SoundHE::digitalSoundCallback(int message, int channel) {
+ // The action done for each sound is always the same;
+ // it's useful to keep track of the message for debugging
+ // purposes though...
+ switch (message) {
+ case HSND_SOUND_TIMEOUT:
+ debug(5, "SoundHE::digitalSoundCallback(): TIMEOUT, channel %d", channel);
+ break;
+ case HSND_SOUND_ENDED:
+ debug(5, "SoundHE::digitalSoundCallback(): ENDED, channel %d", channel);
+ break;
+ case HSND_SOUND_STOPPED:
+ debug(5, "SoundHE::digitalSoundCallback(): STOPPED, channel %d", channel);
+ break;
+ default:
+ warning("SoundHE::digitalSoundCallback(): WARNING: invalid message (%d), channel = %d", message, channel);
+ break;
+ }
- if (_heChannel[chan].sound > _vm->_numSounds) {
- codePtr = _vm->getResourceAddress(rtSpoolBuffer, chan);
- } else {
- codePtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
- }
- assert(codePtr);
- codePtr += _heChannel[chan].codeOffs;
+ _inSoundCallbackFlag = true;
- while (1) {
- size = READ_LE_UINT16(codePtr);
- time = READ_LE_UINT32(codePtr + 2);
+ switch (message) {
+ case HSND_SOUND_TIMEOUT:
+ case HSND_SOUND_ENDED:
+ case HSND_SOUND_STOPPED:
- if (size == 0) {
- _heChannel[chan].codeOffs = -1;
- break;
+ if (_heChannel[channel].sound == HSND_TALKIE_SLOT) {
+ // In the original this was used to prevent an edge case bug
+ // which caused recursive resource accesses/allocations.
+ // I think there's no harm in doing that ourselves too...
+ if (_vm->_insideCreateResource == 0) {
+ _vm->stopTalk();
+ } else {
+ _stopActorTalkingFlag = true;
}
+ }
- debug(5, "Channel %d Timer %d Time %d", chan, tmr, time);
- if (time >= tmr)
- break;
+ _heChannel[channel].clearChannel();
- processSoundOpcodes(_heChannel[chan].sound, codePtr + 6, _heChannel[chan].soundVars);
+ queueSoundCallbackScript(_heChannel[channel].sound, channel, message);
+ break;
+ }
+
+ _inSoundCallbackFlag = false;
+}
+
+void SoundHE::queueSoundCallbackScript(int sound, int channel, int message) {
+ // Avoid queueing up a sound callback if the mixer is not available.
+ if (!_mixer->isReady())
+ return;
+
+ // Check if we are about to duplicate this event...
+ for (int i = 0; i < _soundCallbacksQueueSize; i++) {
- codePtr += size;
- _heChannel[chan].codeOffs += size;
+ if ((sound == _soundCallbackScripts[i].sound) &&
+ (channel == _soundCallbackScripts[i].channel)) {
+
+ _soundAlreadyInQueueCount++;
+
+ debug(5, "SoundHE::queueSoundCallbackScript(): callback for channel %d, sound %d, already in list.",
+ channel, sound);
+
+ return;
}
}
+ // Finally queue up sound...
+ _soundCallbackScripts[_soundCallbacksQueueSize].sound = sound;
+ _soundCallbackScripts[_soundCallbacksQueueSize].channel = channel;
+ _soundCallbackScripts[_soundCallbacksQueueSize].whatFrame = _soundsDebugFrameCounter;
+
+ _soundCallbacksQueueSize++;
+
+ if (_soundCallbacksQueueSize >= HSND_MAX_CALLBACK_SCRIPTS) {
+ error("SoundHE::queueSoundCallbackScript(): ERROR: Got too many sound callbacks (got %d, max %d), message %d",
+ _soundCallbacksQueueSize,
+ HSND_MAX_CALLBACK_SCRIPTS,
+ message);
+ }
+}
+
+void SoundHE::runSoundCode() {
+ byte *soundPtr;
+ int chan, soundPos, len, freq;
+
for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
- if (_heChannel[chan].sound == 0)
+ if (_heChannel[chan].sound == 0) {
continue;
+ }
- if (_heChannel[chan].timer == 0)
+ if (_heChannel[chan].codeOffs == -1) {
continue;
+ }
- if (_vm->getHETimer(chan + 4) > _heChannel[chan].timer) {
- if (_heChannel[chan].sound == 1) {
- _vm->stopTalk();
- }
+ soundPos = _vm->getHETimer(chan + HSND_TIMER_SLOT) * _heChannel[chan].frequency / 1000;
+ soundPos += _vm->VAR(_vm->VAR_SOUND_TOKEN_OFFSET);
+
+ if (soundPos < 0)
+ soundPos = 0;
+
+ if (_heChannel[chan].codeBuffer == nullptr) {
+ soundPtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
+ } else {
+ soundPtr = _heChannel[chan].codeBuffer;
+ }
+
+ assert(soundPtr);
+
+ soundPtr += _heChannel[chan].codeOffs;
+
+ len = READ_LE_UINT16(soundPtr);
+ freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
+
+ while (soundPos > freq) {
+ debug(5, "Channel %d Timer %d Time %d", chan, soundPos, freq);
+
+ processSoundOpcodes(_heChannel[chan].sound, soundPtr + sizeof(uint16) + sizeof(uint32), _heChannel[chan].soundVars);
- _heChannel[chan].sound = 0;
- _heChannel[chan].priority = 0;
- _heChannel[chan].rate = 0;
- _heChannel[chan].timer = 0;
- _heChannel[chan].sbngBlock = 0;
- _heChannel[chan].codeOffs = 0;
- _heChannel[chan].soundVars[0] = 0;
+ _heChannel[chan].codeOffs += len;
+
+ soundPtr += len;
+
+ len = READ_LE_UINT16(soundPtr);
+ freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
+
+ if (len == 0) {
+ _heChannel[chan].codeOffs = -1;
+ break;
+ }
}
}
}
@@ -548,7 +687,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
if (heChannel == -1)
- heChannel = (_vm->VAR_RESERVED_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
+ heChannel = (_vm->VAR_START_DYN_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
@@ -642,8 +781,8 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
_vm->setHETimer(heChannel + 4);
_heChannel[heChannel].sound = soundID;
_heChannel[heChannel].priority = priority;
- _heChannel[heChannel].rate = rate;
- _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0;
+ _heChannel[heChannel].frequency = rate;
+ _heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
_heChannel[heChannel].codeOffs = codeOffs;
memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
@@ -736,8 +875,8 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
_vm->setHETimer(heChannel + 4);
_heChannel[heChannel].sound = soundID;
_heChannel[heChannel].priority = priority;
- _heChannel[heChannel].rate = rate;
- _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0;
+ _heChannel[heChannel].frequency = rate;
+ _heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
_heChannel[heChannel].codeOffs = codeOffs;
memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
@@ -919,8 +1058,8 @@ void SoundHE::startHETalkSound(uint32 offset) {
}
#ifdef ENABLE_HE
-void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
- byte *snd1Ptr, *snd2Ptr;
+void ScummEngine_v80he::createSound(int baseSound, int sound) {
+ byte *baseSoundPtr, *soundPtr;
byte *sbng1Ptr, *sbng2Ptr;
byte *sdat1Ptr, *sdat2Ptr;
byte *src, *dst;
@@ -930,35 +1069,30 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
sbng1Ptr = NULL;
sbng2Ptr = NULL;
- if (snd2id == -1) {
+ if (sound == -1) {
_sndPtrOffs = 0;
_sndTmrOffs = 0;
_sndDataSize = 0;
return;
}
- if (snd1id != _curSndId) {
- _curSndId = snd1id;
+ if (baseSound != _curSndId) {
+ _curSndId = baseSound;
_sndPtrOffs = 0;
_sndTmrOffs = 0;
_sndDataSize = 0;
}
- snd1Ptr = getResourceAddress(rtSound, snd1id);
- assert(snd1Ptr);
- snd2Ptr = getResourceAddress(rtSound, snd2id);
- assert(snd2Ptr);
+ baseSoundPtr = getResourceAddress(rtSound, baseSound);
+ assert(baseSoundPtr);
+ soundPtr = getResourceAddress(rtSound, sound);
+ assert(soundPtr);
- int i;
- int chan = -1;
- for (i = 0; i < ARRAYSIZE(((SoundHE *)_sound)->_heChannel); i++) {
- if (((SoundHE *)_sound)->_heChannel[i].sound == snd1id)
- chan = i;
- }
+ int chan = ((SoundHE *)_sound)->findSoundChannel(baseSound);
- if (!findSoundTag(MKTAG('d','a','t','a'), snd1Ptr)) {
- sbng1Ptr = heFindResource(MKTAG('S','B','N','G'), snd1Ptr);
- sbng2Ptr = heFindResource(MKTAG('S','B','N','G'), snd2Ptr);
+ if (!findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr)) {
+ sbng1Ptr = heFindResource(MKTAG('S','B','N','G'), baseSoundPtr);
+ sbng2Ptr = heFindResource(MKTAG('S','B','N','G'), soundPtr);
}
if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
@@ -966,10 +1100,10 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
// Copy any code left over to the beginning of the code block
int curOffs = ((SoundHE *)_sound)->_heChannel[chan].codeOffs;
- src = snd1Ptr + curOffs;
+ src = baseSoundPtr + curOffs;
dst = sbng1Ptr + 8;
size = READ_BE_UINT32(sbng1Ptr + 4);
- len = sbng1Ptr - snd1Ptr + size - curOffs;
+ len = sbng1Ptr - baseSoundPtr + size - curOffs;
memmove(dst, src, len);
@@ -983,8 +1117,7 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
}
// Reset the current code offset to the beginning of the code block
- if (chan >= 0)
- ((SoundHE *)_sound)->_heChannel[chan].codeOffs = sbng1Ptr - snd1Ptr + 8;
+ ((SoundHE *)_sound)->_heChannel[chan].codeOffs = sbng1Ptr - baseSoundPtr + 8;
// Seek to the end of the code block for sound 2
byte *tmp = sbng2Ptr + 8;
@@ -1008,10 +1141,10 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
}
// Find the data pointers and sizes
- if (findSoundTag(MKTAG('d','a','t','a'), snd1Ptr)) {
- sdat1Ptr = findSoundTag(MKTAG('d','a','t','a'), snd1Ptr);
+ if (findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr)) {
+ sdat1Ptr = findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr);
assert(sdat1Ptr);
- sdat2Ptr = findSoundTag(MKTAG('d','a','t','a'), snd2Ptr);
+ sdat2Ptr = findSoundTag(MKTAG('d','a','t','a'), soundPtr);
assert(sdat2Ptr);
if (!_sndDataSize)
@@ -1019,9 +1152,9 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
sdat2size = READ_LE_UINT32(sdat2Ptr + 4) - 8;
} else {
- sdat1Ptr = heFindResource(MKTAG('S','D','A','T'), snd1Ptr);
+ sdat1Ptr = heFindResource(MKTAG('S','D','A','T'), baseSoundPtr);
assert(sdat1Ptr);
- sdat2Ptr = heFindResource(MKTAG('S','D','A','T'), snd2Ptr);
+ sdat2Ptr = heFindResource(MKTAG('S','D','A','T'), soundPtr);
assert(sdat2Ptr);
_sndDataSize = READ_BE_UINT32(sdat1Ptr + 4) - 8;
@@ -1063,6 +1196,8 @@ void ScummEngine_v80he::createSound(int snd1id, int snd2id) {
_sndPtrOffs = sdat2size - sdat1size;
_sndTmrOffs += sdat2size;
}
+
+ // TODO: PX_SoftRemixAllChannels()
}
#endif
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index ada523e1017..8aa0c7098b2 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -23,11 +23,31 @@
#define SCUMM_HE_SOUND_HE_H
#include "common/scummsys.h"
+#include "common/mutex.h"
#include "scumm/sound.h"
#include "audio/audiostream.h"
namespace Scumm {
+#define HSND_SOUND_STOPPED 1
+#define HSND_SOUND_ENDED 2
+#define HSND_SOUND_TIMEOUT 3
+#define HSND_TALKIE_SLOT 4
+#define HSND_TIMER_SLOT 4
+#define HSND_MAX_CALLBACK_SCRIPTS 20
+#define HSND_MAX_CHANNELS 8
+#define HSND_CHANNEL_0 10000
+#define HSND_CHANNEL_1 10001
+#define HSND_CHANNEL_2 10002
+#define HSND_CHANNEL_3 10003
+#define HSND_CHANNEL_4 10004
+#define HSND_CHANNEL_5 10005
+#define HSND_CHANNEL_6 10006
+#define HSND_CHANNEL_7 10007
+#define HSND_MAX_SOUND_VARS 26
+#define HSND_DEFAULT_FREQUENCY 11025
+#define HSND_BASE_FREQ_FACTOR 1024
+
class ScummEngine_v60he;
class SoundHE : public Sound {
@@ -35,11 +55,21 @@ protected:
ScummEngine_v60he *_vm;
int _overrideFreq;
+ Common::Mutex *_mutex;
+
+ struct HESoundCallbackItem {
+ int32 sound;
+ int32 channel;
+ int32 whatFrame;
+ };
+ HESoundCallbackItem _soundCallbackScripts[HSND_MAX_CALLBACK_SCRIPTS];
struct HEMusic {
int32 id;
int32 offset;
int32 size;
+
+ char filename[128];
};
HEMusic *_heMusic;
int16 _heMusicTracks;
@@ -50,15 +80,30 @@ public: // Used by createSound()
struct {
int sound;
int codeOffs;
+ byte *codeBuffer;
int priority;
- int rate;
+ int frequency;
int timer;
- int sbngBlock;
- int soundVars[27];
- } _heChannel[8];
+ bool hasSoundTokens;
+ int soundVars[HSND_MAX_SOUND_VARS];
+ int age;
+
+ void clearChannel() {
+ sound = 0;
+ codeOffs = 0;
+ codeBuffer = nullptr;
+ priority = 0;
+ frequency = 0;
+ timer = 0;
+ hasSoundTokens = false;
+ memset(soundVars, 0, sizeof(soundVars));
+ age = 0;
+ }
+
+ } _heChannel[HSND_MAX_CHANNELS];
public:
- SoundHE(ScummEngine *parent, Audio::Mixer *mixer);
+ SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex);
~SoundHE() override;
void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
@@ -67,16 +112,22 @@ public:
int isSoundRunning(int sound) const override;
void stopSound(int sound) override;
void stopAllSounds() override;
+ int findSoundChannel(int sound);
void setupSound() override;
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
int findFreeSoundChannel();
- int isSoundCodeUsed(int sound);
+ bool isSoundCodeUsed(int sound);
int getSoundPos(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
void playHESound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
- void processSoundCode();
+ void handleSoundFrame();
+ void unqueueSoundCallbackScripts();
+ void checkSoundTimeouts();
+ void digitalSoundCallback(int message, int channel);
+ void queueSoundCallbackScript(int sound, int channel, int message);
+ void runSoundCode();
void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
void setOverrideFreq(int freq);
void setupHEMusicFile();
@@ -88,6 +139,11 @@ protected:
private:
int _heTalkOffset;
+ bool _stopActorTalkingFlag = false;
+ bool _inSoundCallbackFlag = false;
+ int _soundCallbacksQueueSize = 0;
+ int _soundAlreadyInQueueCount = 0;
+ int _soundsDebugFrameCounter = 0;
Audio::RewindableAudioStream *tryLoadAudioOverride(int soundID, int *duration = nullptr);
};
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index 2501962fada..aee73264769 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -839,8 +839,12 @@ byte ResourceManager::Resource::getResourceCounter() const {
byte *ResourceManager::createResource(ResType type, ResId idx, uint32 size) {
debugC(DEBUG_RESOURCE, "_res->createResource(%s,%d,%d)", nameOfResType(type), idx, size);
- if (!validateResource("allocating", type, idx))
+ _vm->_insideCreateResource++; // For the HE sound engine
+
+ if (!validateResource("allocating", type, idx)) {
+ _vm->_insideCreateResource--;
return nullptr;
+ }
if (_vm->_game.version <= 2) {
// Nuking and reloading a resource can be harmful in some
@@ -866,6 +870,8 @@ byte *ResourceManager::createResource(ResType type, ResId idx, uint32 size) {
_types[type][idx]._size = size;
setResourceCounter(type, idx, 1);
return ptr;
+
+ _vm->_insideCreateResource--;
}
ResourceManager::Resource::Resource() {
diff --git a/engines/scumm/script.cpp b/engines/scumm/script.cpp
index 47942c9ad21..e76d866b5e9 100644
--- a/engines/scumm/script.cpp
+++ b/engines/scumm/script.cpp
@@ -69,7 +69,7 @@ void ScummEngine::runScript(int script, bool freezeResistant, bool recursive, in
}
if (cycle == 0)
- cycle = (_game.heversion >= 90) ? VAR(VAR_SCRIPT_CYCLE) : 1;
+ cycle = (_game.heversion >= 90) ? VAR(VAR_DEFAULT_SCRIPT_PRIORITY) : 1;
slot = getScriptSlot();
@@ -118,7 +118,7 @@ void ScummEngine::runObjectScript(int object, int entry, bool freezeResistant, b
return;
if (cycle == 0)
- cycle = (_game.heversion >= 90) ? VAR(VAR_SCRIPT_CYCLE) : 1;
+ cycle = (_game.heversion >= 90) ? VAR(VAR_DEFAULT_SCRIPT_PRIORITY) : 1;
s = &vm.slot[slot];
s->number = object;
@@ -989,7 +989,7 @@ void ScummEngine::runAllScripts() {
vm.slot[i].didexec = false;
_currentScript = 0xFF;
- int numCycles = (_game.heversion >= 90) ? VAR(VAR_NUM_SCRIPT_CYCLES) : 1;
+ int numCycles = (_game.heversion >= 90) ? VAR(VAR_LAST_SCRIPT_PRIORITY) : 1;
for (int cycle = 1; cycle <= numCycles; cycle++) {
for (i = 0; i < NUM_SCRIPT_SLOT; i++) {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index a99aae21e05..28823ba6a7b 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -1389,7 +1389,7 @@ void ScummEngine::setupScumm(const Common::String &macResourceFile) {
// Create the sound manager
if (_game.heversion > 0)
- _sound = new SoundHE(this, _mixer);
+ _sound = new SoundHE(this, _mixer, &_resourceAccessMutex);
else
_sound = new Sound(this, _mixer, useReplacementAudioTracks);
@@ -2651,7 +2651,7 @@ load_game:
}
if (_game.heversion >= 80) {
- ((SoundHE *)_sound)->processSoundCode();
+ ((SoundHE *)_sound)->handleSoundFrame();
}
if (_game.version < 8) {
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 1bb4b83dcdc..26083721e54 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -545,6 +545,7 @@ public:
/** Central resource data. */
ResourceManager *_res = nullptr;
+ int _insideCreateResource = 0; // Counter for HE sound
bool _enableEnhancements = false;
bool _useOriginalGUI = true;
@@ -1790,19 +1791,25 @@ public:
// HE specific variables
byte VAR_REDRAW_ALL_ACTORS = 0xFF; // Used in setActorRedrawFlags()
- byte VAR_SKIP_RESET_TALK_ACTOR = 0xFF; // Used in setActorCostume()
+ byte VAR_SKIP_RESET_TALK_ACTOR = 0xFF; // Used in setActorCostume()
- byte VAR_SOUND_CHANNEL = 0xFF; // Used in o_startSound()
- byte VAR_TALK_CHANNEL = 0xFF; // Used in startHETalkSound()
- byte VAR_SOUNDCODE_TMR = 0xFF; // Used in processSoundCode()
- byte VAR_RESERVED_SOUND_CHANNELS = 0xFF; // Used in findFreeSoundChannel()
+ byte VAR_SOUND_CHANNEL = 0xFF; // Used in o_startSound()
+ byte VAR_TALK_CHANNEL = 0xFF; // Used in startHETalkSound()
+ byte VAR_SOUND_TOKEN_OFFSET = 0xFF; // Used in handleSoundFrame()
+ byte VAR_START_DYN_SOUND_CHANNELS = 0xFF; // Used in findFreeSoundChannel()
+ byte VAR_SOUND_CALLBACK_SCRIPT = 0xFF;
- byte VAR_MAIN_SCRIPT = 0xFF; // Used in scummLoop()
+ byte VAR_EARLY_CHAN_0_CALLBACK = 0xFF;
+ byte VAR_EARLY_CHAN_1_CALLBACK = 0xFF;
+ byte VAR_EARLY_CHAN_2_CALLBACK = 0xFF;
+ byte VAR_EARLY_CHAN_3_CALLBACK = 0xFF;
- byte VAR_SCRIPT_CYCLE = 0xFF; // Used in runScript()/runObjectScript()
- byte VAR_NUM_SCRIPT_CYCLES = 0xFF; // Used in runAllScripts()
+ byte VAR_MAIN_SCRIPT = 0xFF; // Used in scummLoop()
- byte VAR_QUIT_SCRIPT = 0xFF; // Used in confirmExitDialog()
+ byte VAR_DEFAULT_SCRIPT_PRIORITY = 0xFF; // Used in runScript()/runObjectScript()
+ byte VAR_LAST_SCRIPT_PRIORITY = 0xFF; // Used in runAllScripts()
+
+ byte VAR_QUIT_SCRIPT = 0xFF; // Used in confirmExitDialog()
// Exists both in V7 and in V72HE:
byte VAR_NUM_GLOBAL_OBJS = 0xFF;
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 781e82a9a15..0db7b60a28b 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -270,7 +270,10 @@ void ScummEngine_v72he::setupScummVars() {
VAR_LAST_SOUND = 50;
VAR_TALK_CHANNEL = 51;
VAR_SOUND_CHANNEL = 52;
-
+ VAR_EARLY_CHAN_0_CALLBACK = 53;
+ VAR_EARLY_CHAN_1_CALLBACK = 54;
+ VAR_EARLY_CHAN_2_CALLBACK = 55;
+ VAR_EARLY_CHAN_3_CALLBACK = 56;
VAR_MEMORY_PERFORMANCE = 57;
VAR_VIDEO_PERFORMANCE = 58;
VAR_NEW_ROOM = 59;
@@ -308,8 +311,9 @@ void ScummEngine_v80he::setupScummVars() {
VAR_PLATFORM_VERSION = 79;
VAR_CURRENT_CHARSET = 80;
- VAR_SOUNDCODE_TMR = 84;
+ VAR_SOUND_TOKEN_OFFSET = 84;
VAR_KEY_STATE = 86;
+ VAR_SOUND_CALLBACK_SCRIPT = 87;
VAR_NUM_SOUND_CHANNELS = 88;
VAR_COLOR_DEPTH = 89;
VAR_REDRAW_ALL_ACTORS = 95;
@@ -320,8 +324,8 @@ void ScummEngine_v90he::setupScummVars() {
VAR_TIMER = 97;
VAR_QUIT_SCRIPT = 102;
- VAR_SCRIPT_CYCLE = 103;
- VAR_NUM_SCRIPT_CYCLES = 104;
+ VAR_DEFAULT_SCRIPT_PRIORITY = 103;
+ VAR_LAST_SCRIPT_PRIORITY = 104;
if (_game.heversion >= 95) {
VAR_NUM_SPRITE_GROUPS = 105;
@@ -329,7 +333,7 @@ void ScummEngine_v90he::setupScummVars() {
VAR_U32_VERSION = 107;
VAR_U32_ARRAY_UNK = 116;
VAR_WIZ_TCOLOR = 117;
- VAR_RESERVED_SOUND_CHANNELS = 120;
+ VAR_START_DYN_SOUND_CHANNELS = 120;
}
if (_game.heversion >= 98) {
VAR_SKIP_RESET_TALK_ACTOR = 125;
@@ -715,14 +719,14 @@ void ScummEngine_v80he::resetScummVars() {
void ScummEngine_v90he::resetScummVars() {
ScummEngine_v80he::resetScummVars();
- VAR(VAR_SCRIPT_CYCLE) = 1;
- VAR(VAR_NUM_SCRIPT_CYCLES) = 1;
+ VAR(VAR_DEFAULT_SCRIPT_PRIORITY) = 1;
+ VAR(VAR_LAST_SCRIPT_PRIORITY) = 1;
if (_game.heversion >= 95) {
VAR(VAR_NUM_SPRITE_GROUPS) = MAX(64, _numSprites / 4) - 1;
VAR(VAR_NUM_SPRITES) = _numSprites - 1;
VAR(VAR_WIZ_TCOLOR) = 5;
- VAR(VAR_RESERVED_SOUND_CHANNELS) = 9;
+ VAR(VAR_START_DYN_SOUND_CHANNELS) = 9;
}
if (_game.heversion >= 98) {
VAR(VAR_U32_VERSION) = _logicHE->versionID();
Commit: 454a1d1593494e3e93c42316d8175834747bff56
https://github.com/scummvm/scummvm/commit/454a1d1593494e3e93c42316d8175834747bff56
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Clean-up magic numbers and implement modifySound
Changed paths:
engines/scumm/he/intern_he.h
engines/scumm/he/script_v100he.cpp
engines/scumm/he/script_v70he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.cpp
engines/scumm/sound.h
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index 30eaa379fa4..afa8929e043 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -135,6 +135,7 @@ protected:
class ScummEngine_v70he : public ScummEngine_v60he {
friend class ResExtractor;
+ friend class SoundHE;
protected:
enum HESndFlags {
@@ -192,7 +193,8 @@ protected:
byte *_heV7RoomOffsets;
uint32 *_heV7RoomIntOffsets;
- int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndSoundFreq, _heSndPan, _heSndVol;
+ int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol;
+ bool _heSndStartNewSoundFlag;
int _numStoredFlObjects;
ObjectData *_storedFlObjects;
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index aeb92b6ca1e..e936ad0f72a 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -1737,7 +1737,7 @@ void ScummEngine_v100he::o100_setSystemMessage() {
void ScummEngine_v100he::o100_soundOps() {
byte filename[260];
int var, value;
- //da qui
+
byte subOp = fetchScriptByte();
switch (subOp) {
@@ -1761,7 +1761,11 @@ void ScummEngine_v100he::o100_soundOps() {
((SoundHE *)_sound)->setSoundVar(_heSndSoundId, var, value);
break;
case SO_END:
- _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndSoundFreq, _heSndPan, _heSndVol);
+ if (_heSndStartNewSoundFlag) {
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol);
+ } else {
+ _sound->modifySound(_heSndSoundId, _heSndOffset, _heSndFrequencyShift, _heSndPan, _heSndVol, _heSndFlags);
+ }
break;
case SO_SOUND_ADD:
_heSndFlags |= HE_SND_APPEND;
@@ -1771,17 +1775,19 @@ void ScummEngine_v100he::o100_soundOps() {
break;
case SO_SOUND_FREQUENCY:
_heSndFlags |= HE_SND_FREQUENCY;
- _heSndSoundFreq = pop();
+ _heSndFrequencyShift = pop();
break;
case SO_SOUND_LOOPING:
_heSndFlags |= HE_SND_LOOP;
break;
case SO_SOUND_MODIFY:
case SO_SOUND_START:
+ _heSndStartNewSoundFlag = (SO_SOUND_START == subOp);
_heSndSoundId = pop();
_heSndOffset = 0;
- _heSndSoundFreq = 11025;
+ _heSndFrequencyShift = HSND_BASE_FREQ_FACTOR;
_heSndChannel = VAR(VAR_SOUND_CHANNEL);
+ _heSndVol = HSND_MAX_VOLUME;
_heSndFlags = 0;
break;
case SO_SOUND_PAN:
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index 1626e473498..b578ea46541 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -81,7 +81,7 @@ void ScummEngine_v70he::o70_soundOps() {
// WORKAROUND: For errors in room script 240 (room 4) of maze
break;
case SO_SOUND_FREQUENCY:
- _heSndSoundFreq = pop();
+ _heSndFrequencyShift = pop();
break;
case SO_SOUND_CHANNEL:
_heSndChannel = pop();
@@ -92,14 +92,14 @@ void ScummEngine_v70he::o70_soundOps() {
case SO_SOUND_START:
_heSndSoundId = pop();
_heSndOffset = 0;
- _heSndSoundFreq = 11025;
+ _heSndFrequencyShift = 11025;
_heSndChannel = VAR(VAR_SOUND_CHANNEL);
break;
case SO_SOUND_LOOPING:
_heSndFlags |= HE_SND_LOOP;
break;
case SO_END:
- _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndSoundFreq);
+ _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift);
_heSndFlags = 0;
break;
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index d6f5c09d457..242111f936c 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -66,7 +66,7 @@ void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlag
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
- if (heFlags & 8) {
+ if (heFlags & ScummEngine_v70he::HESndFlags::HE_SND_QUICK_START) {
playHESound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
} else {
Sound::addSoundToQueue(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
@@ -76,13 +76,35 @@ void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlag
void SoundHE::addSoundToQueue2(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
int i = _soundQue2Pos;
while (i--) {
- if (_soundQue2[i].sound == sound && !(heFlags & 2))
+ if (_soundQue2[i].sound == sound && !(heFlags & ScummEngine_v70he::HESndFlags::HE_SND_APPEND))
+ // Sound is already queued
return;
}
Sound::addSoundToQueue2(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
+void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) {
+ int chan = findSoundChannel(sound);
+ if (chan >= 0 && _heChannel[chan].sound) {
+ // Modify the current playing sound
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)
+ _mixer->setChannelVolume(_heSoundChannels[chan], volume);
+
+ // Convert the pan range from (0, 127) to (-127, 127)
+ int scaledPan = (pan != 64) ? 2 * pan - 127 : 0;
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)
+ _mixer->setChannelBalance(_heSoundChannels[chan], scaledPan);
+
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
+ int newFrequency = (_heChannel[chan].frequency * frequencyShift) / HSND_BASE_FREQ_FACTOR;
+ if (newFrequency)
+ _mixer->setChannelRate(_heSoundChannels[chan], newFrequency);
+ }
+ }
+ // TODO: Implement spooled sound case
+}
+
void SoundHE::processSoundQueues() {
int snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol;
@@ -413,10 +435,7 @@ void SoundHE::unqueueSoundCallbackScripts() {
}
void SoundHE::checkSoundTimeouts() {
- byte *soundPtr;
- int chan, soundPos, len, freq;
-
- for (chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
+ for (int chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
if (_heChannel[chan].sound == 0 || _heChannel[chan].timer == 0)
continue;
@@ -565,18 +584,20 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
while (READ_LE_UINT16(codePtr) != 0) {
codePtr += 2;
opcode = READ_LE_UINT16(codePtr); codePtr += 2;
- opcode = (opcode & 0xFFF) >> 4;
- arg = opcode & 3;
- opcode &= ~3;
+ opcode = (opcode & ~HSND_SBNG_MAGIC_MASK) >> 4;
+ arg = opcode & HSND_SBNG_VARORVAL;
+ opcode &= ~HSND_SBNG_VARORVAL;
+
debug(5, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
+
switch (opcode) {
- case 0: // Continue
+ case HSND_SBNG_END: // Continue
break;
- case 16: // Set talk state
+ case HSND_SBNG_FACE: // Set talk state
val = READ_LE_UINT16(codePtr); codePtr += 2;
setSoundVar(sound, 19, val);
break;
- case 32: // Set var
+ case HSND_SBNG_SET_SET: // Set var
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -584,7 +605,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
}
setSoundVar(sound, var, val);
break;
- case 48: // Add
+ case HSND_SBNG_SET_ADD: // Add
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -593,7 +614,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) + val;
setSoundVar(sound, var, val);
break;
- case 56: // Subtract
+ case HSND_SBNG_SET_SUB: // Subtract
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -602,7 +623,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) - val;
setSoundVar(sound, var, val);
break;
- case 64: // Multiple
+ case HSND_SBNG_SET_MUL: // Multiple
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -611,7 +632,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) * val;
setSoundVar(sound, var, val);
break;
- case 80: // Divide
+ case HSND_SBNG_SET_DIV: // Divide
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -624,12 +645,12 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) / val;
setSoundVar(sound, var, val);
break;
- case 96: // Increment
+ case HSND_SBNG_SET_INC: // Increment
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) + 1;
setSoundVar(sound, var, val);
break;
- case 104: // Decrement
+ case HSND_SBNG_SET_DEC: // Decrement
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) - 1;
setSoundVar(sound, var, val);
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 8aa0c7098b2..1b4b1d340b4 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -47,6 +47,27 @@ namespace Scumm {
#define HSND_MAX_SOUND_VARS 26
#define HSND_DEFAULT_FREQUENCY 11025
#define HSND_BASE_FREQ_FACTOR 1024
+#define HSND_MAX_VOLUME 255
+#define HSND_FIRST_SPOOLING_SOUND 4000
+
+#define HSND_SBNG_TYPE_ALL 0xF8
+#define HSND_SBNG_DATA_ALL 0x07
+
+#define HSND_SBNG_MAGIC_NUMBER 0x2000
+#define HSND_SBNG_MAGIC_MASK 0xF000
+
+#define HSND_SBNG_END 0x00
+#define HSND_SBNG_FACE 0x10
+#define HSND_SBNG_SET_SET 0x20
+#define HSND_SBNG_SET_ADD 0x30
+#define HSND_SBNG_SET_SUB 0x38
+#define HSND_SBNG_SET_MUL 0x40
+#define HSND_SBNG_SET_DIV 0x50
+#define HSND_SBNG_SET_INC 0x60
+#define HSND_SBNG_SET_DEC 0x68
+#define HSND_SBNG_VARORVAL 0x03
+#define HSND_SBNG_VARVAL 0x02
+
class ScummEngine_v60he;
@@ -108,6 +129,7 @@ public:
void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
+ void modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) override;
int isSoundRunning(int sound) const override;
void stopSound(int sound) override;
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 28823ba6a7b..0a8b8624ad6 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -656,9 +656,10 @@ ScummEngine_v70he::ScummEngine_v70he(OSystem *syst, const DetectorResult &dr)
_heSndOffset = 0;
_heSndChannel = 0;
_heSndFlags = 0;
- _heSndSoundFreq = 0;
+ _heSndFrequencyShift = 0;
_heSndPan = 0;
_heSndVol = 0;
+ _heSndStartNewSoundFlag = false;
_numStoredFlObjects = 0;
_storedFlObjects = (ObjectData *)calloc(100, sizeof(ObjectData));
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 09befb94b16..99bfcad0b57 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -120,6 +120,7 @@ public:
virtual void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0);
virtual void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0);
void processSound();
+ virtual void modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) {};
void playSound(int soundID);
void startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle = NULL);
Commit: 9b8992f0c1b02848545cff4898ce884e2b37096b
https://github.com/scummvm/scummvm/commit/9b8992f0c1b02848545cff4898ce884e2b37096b
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix incorrect frequency values
Changed paths:
engines/scumm/he/script_v100he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index e936ad0f72a..e0211be6d26 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -1785,9 +1785,10 @@ void ScummEngine_v100he::o100_soundOps() {
_heSndStartNewSoundFlag = (SO_SOUND_START == subOp);
_heSndSoundId = pop();
_heSndOffset = 0;
- _heSndFrequencyShift = HSND_BASE_FREQ_FACTOR;
+ _heSndFrequencyShift = HSND_SOUND_FREQ_BASE;
_heSndChannel = VAR(VAR_SOUND_CHANNEL);
_heSndVol = HSND_MAX_VOLUME;
+ _heSndPan = HSND_SOUND_PAN_CENTER;
_heSndFlags = 0;
break;
case SO_SOUND_PAN:
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 242111f936c..e19ebc5bd56 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -97,7 +97,7 @@ void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, in
_mixer->setChannelBalance(_heSoundChannels[chan], scaledPan);
if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
- int newFrequency = (_heChannel[chan].frequency * frequencyShift) / HSND_BASE_FREQ_FACTOR;
+ int newFrequency = (_heChannel[chan].frequency * frequencyShift) / HSND_SOUND_FREQ_BASE;
if (newFrequency)
_mixer->setChannelRate(_heSoundChannels[chan], newFrequency);
}
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 1b4b1d340b4..08dff32393e 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -47,7 +47,9 @@ namespace Scumm {
#define HSND_MAX_SOUND_VARS 26
#define HSND_DEFAULT_FREQUENCY 11025
#define HSND_BASE_FREQ_FACTOR 1024
+#define HSND_SOUND_FREQ_BASE 1024
#define HSND_MAX_VOLUME 255
+#define HSND_SOUND_PAN_CENTER 64
#define HSND_FIRST_SPOOLING_SOUND 4000
#define HSND_SBNG_TYPE_ALL 0xF8
Commit: f4455e773ef8abc771b34e9d36d8a3d7b008f26d
https://github.com/scummvm/scummvm/commit/f4455e773ef8abc771b34e9d36d8a3d7b008f26d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: Relabel part of the general sound code
Albeit not 100% necessary for rewriting the HE sound engine,
I decided to make this change in order to:
- Give proper meaning to magic numbers and variables
which were given temporary names 20 years ago;
- Avoid going insane when making comparison with HE code
and ours, when rewriting the sound code.
Changed paths:
engines/scumm/actor.cpp
engines/scumm/akos.cpp
engines/scumm/costume.cpp
engines/scumm/he/script_v100he.cpp
engines/scumm/he/script_v70he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/input.cpp
engines/scumm/script_v2.cpp
engines/scumm/script_v5.cpp
engines/scumm/script_v6.cpp
engines/scumm/scumm.cpp
engines/scumm/sound.cpp
engines/scumm/sound.h
engines/scumm/string.cpp
engines/scumm/string_v7.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 708850b7d19..1f74d7627ba 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2160,7 +2160,7 @@ void ScummEngine::playActorSounds() {
}
// fast mode will flood the queue with walk sounds
if (!_fastMode) {
- _sound->addSoundToQueue(sound);
+ _sound->startSound(sound);
}
for (j = 1; j < _numActors; j++) {
_actors[j]->_cost.soundCounter = 0;
@@ -3124,7 +3124,7 @@ void ScummEngine::stopTalk() {
_haveMsg = 0;
_talkDelay = 0;
- _sound->_sfxMode = 0;
+ _sound->_digiSndMode = DIGI_SND_MODE_EMPTY;
act = getTalkingActor();
if (act && act < 0x80) {
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index 74f004dedb3..3009dee4ca4 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -1647,7 +1647,7 @@ void ScummEngine_v6::akos_processQueue() {
a->putActor(0, 0, 0);
break;
case AKQC_StartSound:
- _sound->addSoundToQueue(param1, 0, -1, 0);
+ _sound->startSound(param1, 0, -1, 0);
break;
case AKQC_StartAnimation:
a->startAnimActor(param1);
@@ -1678,7 +1678,7 @@ void ScummEngine_v6::akos_processQueue() {
break;
case AKQC_SoftStartSound:
- _sound->addSoundToQueue(param1, 0, -1, 4);
+ _sound->startSound(param1, 0, -1, 4);
break;
default:
error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param1, param2);
diff --git a/engines/scumm/costume.cpp b/engines/scumm/costume.cpp
index ff54025c23c..429110972dc 100644
--- a/engines/scumm/costume.cpp
+++ b/engines/scumm/costume.cpp
@@ -1162,7 +1162,7 @@ bool ClassicCostumeLoader::increaseAnim(Actor *a, int slot) {
if (_vm->_game.version >= 6) {
if (nc >= 0x71 && nc <= 0x78) {
uint sound = (_vm->_game.heversion == 60) ? 0x78 - nc : nc - 0x71;
- _vm->_sound->addSoundToQueue2(a->_sound[sound]);
+ _vm->_sound->addSoundToQueue(a->_sound[sound]);
if (a->_cost.start[slot] != end)
continue;
}
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index e0211be6d26..556a610a110 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -1762,7 +1762,7 @@ void ScummEngine_v100he::o100_soundOps() {
break;
case SO_END:
if (_heSndStartNewSoundFlag) {
- _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol);
+ _sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol);
} else {
_sound->modifySound(_heSndSoundId, _heSndOffset, _heSndFrequencyShift, _heSndPan, _heSndVol, _heSndFlags);
}
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index b578ea46541..2eca4a41d32 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -69,7 +69,7 @@ void ScummEngine_v70he::o70_soundOps() {
case SO_SOUND_VOLUME:
value = pop();
_heSndSoundId = pop();
- _sound->addSoundToQueue(_heSndSoundId, 0, 0, HE_SND_VOL, 0, 0, value);
+ _sound->startSound(_heSndSoundId, 0, 0, HE_SND_VOL, 0, 0, value);
break;
case SO_NOW:
_heSndFlags |= HE_SND_QUICK_START;
@@ -99,7 +99,7 @@ void ScummEngine_v70he::o70_soundOps() {
_heSndFlags |= HE_SND_LOOP;
break;
case SO_END:
- _sound->addSoundToQueue(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift);
+ _sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift);
_heSndFlags = 0;
break;
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index e19ebc5bd56..ac799bba4b1 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -62,26 +62,26 @@ SoundHE::~SoundHE() {
delete[] _heSoundChannels;
}
-void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::startSound(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
if (heFlags & ScummEngine_v70he::HESndFlags::HE_SND_QUICK_START) {
- playHESound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerDigitalSound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
} else {
- Sound::addSoundToQueue(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ Sound::startSound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
}
-void SoundHE::addSoundToQueue2(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
- int i = _soundQue2Pos;
+void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+ int i = _soundQueuePos;
while (i--) {
- if (_soundQue2[i].sound == sound && !(heFlags & ScummEngine_v70he::HESndFlags::HE_SND_APPEND))
+ if (_soundQueue[i].sound == sound && !(heFlags & ScummEngine_v70he::HESndFlags::HE_SND_APPEND))
// Sound is already queued
return;
}
- Sound::addSoundToQueue2(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ Sound::addSoundToQueue(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) {
@@ -109,30 +109,30 @@ void SoundHE::processSoundQueues() {
int snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol;
if (_vm->_game.heversion >= 72) {
- for (int i = 0; i <_soundQue2Pos; i++) {
- snd = _soundQue2[i].sound;
- heOffset = _soundQue2[i].offset;
- heChannel = _soundQue2[i].channel;
- heFlags = _soundQue2[i].flags;
- heFreq = _soundQue2[_soundQue2Pos].freq;
- hePan = _soundQue2[_soundQue2Pos].pan;
- heVol = _soundQue2[_soundQue2Pos].vol;
+ for (int i = 0; i <_soundQueuePos; i++) {
+ snd = _soundQueue[i].sound;
+ heOffset = _soundQueue[i].offset;
+ heChannel = _soundQueue[i].channel;
+ heFlags = _soundQueue[i].flags;
+ heFreq = _soundQueue[_soundQueuePos].freq;
+ hePan = _soundQueue[_soundQueuePos].pan;
+ heVol = _soundQueue[_soundQueuePos].vol;
if (snd)
- playHESound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerDigitalSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
- _soundQue2Pos = 0;
+ _soundQueuePos = 0;
} else {
- while (_soundQue2Pos) {
- _soundQue2Pos--;
- snd = _soundQue2[_soundQue2Pos].sound;
- heOffset = _soundQue2[_soundQue2Pos].offset;
- heChannel = _soundQue2[_soundQue2Pos].channel;
- heFlags = _soundQue2[_soundQue2Pos].flags;
- heFreq = _soundQue2[_soundQue2Pos].freq;
- hePan = _soundQue2[_soundQue2Pos].pan;
- heVol = _soundQue2[_soundQue2Pos].vol;
+ while (_soundQueuePos) {
+ _soundQueuePos--;
+ snd = _soundQueue[_soundQueuePos].sound;
+ heOffset = _soundQueue[_soundQueuePos].offset;
+ heChannel = _soundQueue[_soundQueuePos].channel;
+ heFlags = _soundQueue[_soundQueuePos].flags;
+ heFreq = _soundQueue[_soundQueuePos].freq;
+ hePan = _soundQueue[_soundQueuePos].pan;
+ heVol = _soundQueue[_soundQueuePos].vol;
if (snd)
- playHESound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerDigitalSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
}
@@ -249,12 +249,12 @@ void SoundHE::stopSoundChannel(int chan) {
_heChannel[chan].codeOffs = 0;
memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
- for (int i = 0; i < ARRAYSIZE(_soundQue2); i++) {
- if (_soundQue2[i].channel == chan) {
- _soundQue2[i].sound = 0;
- _soundQue2[i].offset = 0;
- _soundQue2[i].channel = 0;
- _soundQue2[i].flags = 0;
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].channel == chan) {
+ _soundQueue[i].sound = 0;
+ _soundQueue[i].offset = 0;
+ _soundQueue[i].channel = 0;
+ _soundQueue[i].flags = 0;
}
}
}
@@ -693,7 +693,7 @@ byte *findSoundTag(uint32 tag, byte *ptr) {
return nullptr;
}
-void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
Audio::RewindableAudioStream *stream = nullptr;
byte *ptr, *spoolPtr;
int size = -1;
@@ -710,7 +710,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
if (heChannel == -1)
heChannel = (_vm->VAR_START_DYN_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
- debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
+ debug(5,"triggerDigitalSound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
if (soundID >= 10000) {
// Special codes, used in pjgames
@@ -723,11 +723,11 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
Common::String buf(_vm->generateFilename(-4));
if (musicFile.open(buf) == false) {
- warning("playHESound: Can't open music file %s", buf.c_str());
+ warning("triggerDigitalSound: Can't open music file %s", buf.c_str());
return;
}
if (!getHEMusicDetails(soundID, music_offs, size)) {
- debug(0, "playHESound: musicID %d not found", soundID);
+ debug(0, "triggerDigitalSound: musicID %d not found", soundID);
return;
}
@@ -791,7 +791,7 @@ void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags,
Common::MemoryReadStream memStream(ptr, size);
if (!Audio::loadWAVFromStream(memStream, size, rate, flags, &compType, &blockAlign, &samplesPerBlock)) {
- error("playHESound: Not a valid WAV file (%d)", soundID);
+ error("triggerDigitalSound: Not a valid WAV file (%d)", soundID);
}
assert(heOffset >= 0 && heOffset < size);
@@ -1063,7 +1063,7 @@ void SoundHE::startHETalkSound(uint32 offset) {
// _heTalkOffset is used at tryLoadSoundOverride.
_heTalkOffset = offset;
- _sfxMode |= 2;
+ _digiSndMode |= DIGI_SND_MODE_TALKIE;
_vm->_res->nukeResource(rtSound, 1);
file.seek(offset + 4, SEEK_SET);
@@ -1075,7 +1075,7 @@ void SoundHE::startHETalkSound(uint32 offset) {
file.read(ptr, size);
int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
- addSoundToQueue2(1, 0, channel, 0);
+ addSoundToQueue(1, 0, channel, 0);
}
#ifdef ENABLE_HE
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 08dff32393e..cd93948ec55 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -129,8 +129,8 @@ public:
SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex);
~SoundHE() override;
+ void startSound(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
- void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0) override;
void modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) override;
int isSoundRunning(int sound) const override;
@@ -145,7 +145,7 @@ public:
int getSoundPos(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
- void playHESound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerDigitalSound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
void handleSoundFrame();
void unqueueSoundCallbackScripts();
void checkSoundTimeouts();
diff --git a/engines/scumm/input.cpp b/engines/scumm/input.cpp
index 48698459fd8..2c3699ecc8e 100644
--- a/engines/scumm/input.cpp
+++ b/engines/scumm/input.cpp
@@ -1288,7 +1288,7 @@ void ScummEngine::processKeyboard(Common::KeyState lastKeyHit) {
} else if (talkstopKeyEnabled && lastKeyHit.ascii == '.') {
_talkDelay = 0;
- if (_sound->_sfxMode & 2)
+ if (_sound->_digiSndMode & DIGI_SND_MODE_TALKIE)
stopTalk();
} else if (cutsceneExitKeyEnabled && (lastKeyHit.keycode == Common::KEYCODE_ESCAPE && lastKeyHit.hasFlags(0))) {
diff --git a/engines/scumm/script_v2.cpp b/engines/scumm/script_v2.cpp
index e3d6a869a5e..5d17f4c94c4 100644
--- a/engines/scumm/script_v2.cpp
+++ b/engines/scumm/script_v2.cpp
@@ -1598,7 +1598,7 @@ void ScummEngine_v2::o2_pickupObject() {
runInventoryScript(1);
if (_game.platform == Common::kPlatformNES)
- _sound->addSoundToQueue(51); // play 'pickup' sound
+ _sound->startSound(51); // play 'pickup' sound
}
void ScummEngine_v2::o2_cursorCommand() { // TODO: Define the magic numbers
diff --git a/engines/scumm/script_v5.cpp b/engines/scumm/script_v5.cpp
index af2218003f6..c376a5bca99 100644
--- a/engines/scumm/script_v5.cpp
+++ b/engines/scumm/script_v5.cpp
@@ -2641,7 +2641,7 @@ void ScummEngine_v5::o5_startMusic() {
debugC(DEBUG_GENERAL,"o5_startMusic(%d)", b);
setResult(result);
} else {
- _sound->addSoundToQueue(getVarOrDirectByte(PARAM_1));
+ _sound->startSound(getVarOrDirectByte(PARAM_1));
}
}
@@ -2669,7 +2669,7 @@ void ScummEngine_v5::o5_startSound() {
if (VAR_MUSIC_TIMER != 0xFF)
VAR(VAR_MUSIC_TIMER) = 0;
- _sound->addSoundToQueue(sound);
+ _sound->startSound(sound);
}
void ScummEngine_v5::o5_stopMusic() {
diff --git a/engines/scumm/script_v6.cpp b/engines/scumm/script_v6.cpp
index 86049cbb391..192859e54ce 100644
--- a/engines/scumm/script_v6.cpp
+++ b/engines/scumm/script_v6.cpp
@@ -1159,7 +1159,7 @@ void ScummEngine_v6::o6_startSound() {
_imuseDigital->startSfx(pop(), 64);
else
#endif
- _sound->addSoundToQueue(pop(), offset);
+ _sound->startSound(pop(), offset);
}
void ScummEngine_v6::o6_stopSound() {
@@ -1170,7 +1170,7 @@ void ScummEngine_v6::o6_startMusic() {
if (_game.version >= 7)
error("o6_startMusic() It shouldn't be called here for imuse digital");
- _sound->addSoundToQueue(pop());
+ _sound->startSound(pop());
}
void ScummEngine_v6::o6_stopObjectScript() {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 0a8b8624ad6..d5968f71065 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2970,7 +2970,7 @@ void ScummEngine_v3::terminateSaveMenuScript() {
// Restore the previous sound
if (readVar(305)) {
- _sound->addSoundToQueue(readVar(305));
+ _sound->startSound(readVar(305));
}
// Show the cursor
@@ -3030,7 +3030,7 @@ void ScummEngine_v3::terminateSaveMenuScript() {
// Restore the previous sound
if (readVar(0x4007)) {
- _sound->addSoundToQueue(readVar(0x4007));
+ _sound->startSound(readVar(0x4007));
}
// Terminate the cutscene state
@@ -3237,7 +3237,7 @@ void ScummEngine_v3::scummLoop_handleSaveLoad() {
if (_game.platform == Common::kPlatformNES) {
runScript(5, 0, 0, nullptr);
if (VAR(224))
- _sound->addSoundToQueue(VAR(224));
+ _sound->startSound(VAR(224));
}
} else if (_game.platform != Common::kPlatformMacintosh) {
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 156a795d3af..d6a7f6955c3 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -64,19 +64,19 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
_cdMusicTimerMod(0),
_cdMusicTimer(0),
_speechTimerMod(0),
- _soundQuePos(0),
- _soundQue2Pos(0),
+ _midiQueuePos(0),
+ _soundQueuePos(0),
_sfxFilename(),
_sfxFileEncByte(0),
_offsetTable(nullptr),
_numSoundEffects(0),
_soundMode(kVOCMode),
- _talk_sound_a1(0),
- _talk_sound_a2(0),
- _talk_sound_b1(0),
- _talk_sound_b2(0),
- _talk_sound_mode(0),
- _talk_sound_channel(0),
+ _queuedSfxOffset(0),
+ _queuedTalkieOffset(0),
+ _queuedSfxLen(0),
+ _queuedTalkieLen(0),
+ _queuedSoundMode(DIGI_SND_MODE_EMPTY),
+ _queuedSfxChannel(0),
_mouthSyncMode(false),
_endOfMouthSync(false),
_curSoundPos(0),
@@ -84,10 +84,10 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
_currentMusic(0),
_lastSound(0),
_soundsPaused(false),
- _sfxMode(0) {
+ _digiSndMode(DIGI_SND_MODE_EMPTY) {
- memset(_soundQue, 0, sizeof(_soundQue));
- memset(_soundQue2, 0, sizeof(_soundQue2));
+ memset(_midiQueue, 0, sizeof(_midiQueue));
+ memset(_soundQueue, 0, sizeof(_soundQueue));
memset(_mouthSyncTimes, 0, sizeof(_mouthSyncTimes));
_musicType = MDT_NONE;
@@ -202,7 +202,7 @@ void Sound::updateMusicTimer() {
_musicTimer = 277;
}
-void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void Sound::startSound(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
_lastSound = sound;
@@ -211,19 +211,19 @@ void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags,
if (sound <= _vm->_numSounds)
_vm->ensureResourceLoaded(rtSound, sound);
- addSoundToQueue2(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ addSoundToQueue(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
-void Sound::addSoundToQueue2(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
- assert(_soundQue2Pos < ARRAYSIZE(_soundQue2));
- _soundQue2[_soundQue2Pos].sound = sound;
- _soundQue2[_soundQue2Pos].offset = heOffset;
- _soundQue2[_soundQue2Pos].channel = heChannel;
- _soundQue2[_soundQue2Pos].flags = heFlags;
- _soundQue2[_soundQue2Pos].freq = heFreq;
- _soundQue2[_soundQue2Pos].pan = hePan;
- _soundQue2[_soundQue2Pos].vol = heVol;
- _soundQue2Pos++;
+void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+ assert(_soundQueuePos < ARRAYSIZE(_soundQueue));
+ _soundQueue[_soundQueuePos].sound = sound;
+ _soundQueue[_soundQueuePos].offset = heOffset;
+ _soundQueue[_soundQueuePos].channel = heChannel;
+ _soundQueue[_soundQueuePos].flags = heFlags;
+ _soundQueue[_soundQueuePos].freq = heFreq;
+ _soundQueue[_soundQueuePos].pan = hePan;
+ _soundQueue[_soundQueuePos].vol = heVol;
+ _soundQueuePos++;
}
void Sound::processSound() {
@@ -242,23 +242,23 @@ void Sound::processSoundQueues() {
int snd;
int data[16];
- while (_soundQue2Pos) {
- _soundQue2Pos--;
- snd = _soundQue2[_soundQue2Pos].sound;
+ while (_soundQueuePos) {
+ _soundQueuePos--;
+ snd = _soundQueue[_soundQueuePos].sound;
if (snd)
- playSound(snd);
+ triggerSound(snd);
}
- while (i < _soundQuePos) {
- num = _soundQue[i++];
- if (i + num > _soundQuePos) {
+ while (i < _midiQueuePos) {
+ num = _midiQueue[i++];
+ if (i + num > _midiQueuePos) {
error("processSoundQues: invalid num value");
break;
}
memset(data, 0, sizeof(data));
if (num > 0) {
for (int j = 0; j < num; j++)
- data[j] = _soundQue[i + j];
+ data[j] = _midiQueue[i + j];
i += num;
debugC(DEBUG_IMUSE, "processSoundQues(%d,%d,%d,%d,%d,%d,%d,%d,%d)",
@@ -271,7 +271,7 @@ void Sound::processSoundQueues() {
_vm->VAR(_vm->VAR_SOUNDRESULT) = (short)_vm->_imuse->doCommand(num, data);
}
}
- _soundQuePos = 0;
+ _midiQueuePos = 0;
}
int Sound::getReplacementAudioTrack(int soundID) {
@@ -326,7 +326,7 @@ int Sound::getReplacementAudioTrack(int soundID) {
return trackNr;
}
-void Sound::playSound(int soundID) {
+void Sound::triggerSound(int soundID) {
byte *ptr;
byte *sound;
Audio::AudioStream *stream;
@@ -378,7 +378,7 @@ void Sound::playSound(int soundID) {
return;
}
- debugC(DEBUG_SOUND, "playSound #%d", soundID);
+ debugC(DEBUG_SOUND, "triggerSound #%d", soundID);
ptr = _vm->getResourceAddress(rtSound, soundID);
@@ -530,7 +530,7 @@ void Sound::playSound(int soundID) {
_currentCDSound = soundID;
} else {
// All other sound types are ignored
- warning("Scumm::Sound::playSound: encountered audio resource with chunk type 'SOUN' and sound type %d", type);
+ warning("Scumm::Sound::triggerSound: encountered audio resource with chunk type 'SOUN' and sound type %d", type);
}
}
else if ((_vm->_game.platform == Common::kPlatformMacintosh) && (_vm->_game.id == GID_INDY3) && READ_BE_UINT16(ptr + 8) == 0x1C) {
@@ -612,16 +612,16 @@ void Sound::playSound(int soundID) {
void Sound::processSfxQueues() {
- if (_talk_sound_mode != 0) {
- if (_talk_sound_mode & 1)
- startTalkSound(_talk_sound_a1, _talk_sound_b1, 1);
- if (_talk_sound_mode & 2)
- startTalkSound(_talk_sound_a2, _talk_sound_b2, 2, _talkChannelHandle);
- _talk_sound_mode = 0;
+ if (_queuedSoundMode != DIGI_SND_MODE_EMPTY) {
+ if (_queuedSoundMode & DIGI_SND_MODE_SFX)
+ startTalkSound(_queuedSfxOffset, _queuedSfxLen, 1);
+ if (_queuedSoundMode & DIGI_SND_MODE_TALKIE)
+ startTalkSound(_queuedTalkieOffset, _queuedTalkieLen, 2, _talkChannelHandle);
+ _queuedSoundMode = DIGI_SND_MODE_EMPTY;
}
const int act = _vm->getTalkingActor();
- if ((_sfxMode & 2) && act != 0) {
+ if ((_digiSndMode & DIGI_SND_MODE_TALKIE) && act != 0) {
Actor *a;
bool finished;
@@ -677,9 +677,9 @@ void Sound::processSfxQueues() {
}
}
- if (_sfxMode & 1) {
+ if (_digiSndMode & DIGI_SND_MODE_SFX) {
if (isSfxFinished()) {
- _sfxMode &= ~1;
+ _digiSndMode &= ~DIGI_SND_MODE_SFX;
}
}
}
@@ -719,7 +719,7 @@ static Audio::AudioStream *checkForBrokenIndy4Sample(Common::SeekableReadStream
);
}
-void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle) {
+void Sound::startTalkSound(uint32 offset, uint32 length, int mode, Audio::SoundHandle *handle) {
int num = 0, i;
int id = -1;
int size = 0;
@@ -727,7 +727,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
if (_vm->_game.id == GID_CMI || (_vm->_game.id == GID_DIG && !(_vm->_game.features & GF_DEMO))) {
// COMI (full & demo), DIG (full)
- _sfxMode |= mode;
+ _digiSndMode |= mode;
if (_vm->_game.id == GID_DIG)
resetSpeechTimer();
@@ -735,12 +735,12 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
return;
} else if (_vm->_game.id == GID_DIG && (_vm->_game.features & GF_DEMO) &&
_vm->_voiceMode != 2) {
- _sfxMode |= mode;
+ _digiSndMode |= mode;
char filename[30];
char roomname[10];
int roomNumber = offset;
- int fileNumber = b;
+ int fileNumber = length;
if (roomNumber == 1)
Common::strlcpy(roomname, "logo", sizeof(roomname));
@@ -822,7 +822,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
file->setEnc(_sfxFileEncByte);
file->seek(offset + 4 + 4, SEEK_SET); // Skip "VCTL" and the block size
- vctlBlockSize = b;
+ vctlBlockSize = length;
if (vctlBlockSize > 8) {
num = (vctlBlockSize - 8) >> 1;
@@ -836,7 +836,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
_mouthSyncTimes[i] = file->readUint16BE();
_mouthSyncTimes[i] = 0xFFFF;
- _sfxMode |= mode;
+ _digiSndMode |= mode;
resetSpeechTimer();
_mouthSyncMode = true;
@@ -877,13 +877,13 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
// automatically stop any other that may be playing at that time. So
// that is what we do here, but we make an exception for speech.
- if (mode == 1 && (_vm->_game.id == GID_TENTACLE || _vm->_game.id == GID_SAMNMAX)) {
- id = 777777 + _talk_sound_channel;
+ if (mode == DIGI_SND_MODE_SFX && (_vm->_game.id == GID_TENTACLE || _vm->_game.id == GID_SAMNMAX)) {
+ id = 777777 + _queuedSfxChannel;
_mixer->stopID(id);
}
- if (b > 8) {
- num = (b - 8) >> 1;
+ if (length > 8) {
+ num = (length - 8) >> 1;
}
if (_offsetTable != nullptr) {
@@ -898,7 +898,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
return;
}
if (2 * num != result->num_tags) {
- warning("startTalkSound: number of tags do not match (%d - %d)", b,
+ warning("startTalkSound: number of tags do not match (%d - %d)", length,
result->num_tags);
num = result->num_tags;
}
@@ -936,7 +936,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
// size -= num * 2;
_mouthSyncTimes[i] = 0xFFFF;
- _sfxMode |= mode;
+ _digiSndMode |= mode;
resetSpeechTimer();
_mouthSyncMode = true;
}
@@ -1000,7 +1000,7 @@ void Sound::startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle
}
void Sound::stopTalkSound() {
- if (_sfxMode & 2) {
+ if (_digiSndMode & DIGI_SND_MODE_TALKIE) {
if (_vm->_imuseDigital) {
#ifdef ENABLE_SCUMM_7_8
_vm->_imuseDigital->stopSound(kTalkSoundID);
@@ -1010,7 +1010,7 @@ void Sound::stopTalkSound() {
} else {
_mixer->stopHandle(*_talkChannelHandle);
}
- _sfxMode &= ~2;
+ _digiSndMode &= ~DIGI_SND_MODE_TALKIE;
}
}
@@ -1106,18 +1106,18 @@ bool Sound::isSoundInUse(int sound) const {
bool Sound::isSoundInQueue(int sound) const {
int i, num;
- i = _soundQue2Pos;
+ i = _soundQueuePos;
while (i--) {
- if (_soundQue2[i].sound == sound)
+ if (_soundQueue[i].sound == sound)
return true;
}
i = 0;
- while (i < _soundQuePos) {
- num = _soundQue[i++];
+ while (i < _midiQueuePos) {
+ num = _midiQueue[i++];
if (num > 0) {
- if (_soundQue[i + 0] == 0x10F && _soundQue[i + 1] == 8 && _soundQue[i + 2] == sound)
+ if (_midiQueue[i + 0] == 0x10F && _midiQueue[i + 1] == 8 && _midiQueue[i + 2] == sound)
return true;
i += num;
}
@@ -1142,15 +1142,15 @@ void Sound::stopSound(int sound) {
if (_vm->_musicEngine)
_vm->_musicEngine->stopSound(sound);
- for (i = 0; i < ARRAYSIZE(_soundQue2); i++) {
- if (_soundQue2[i].sound == sound) {
- _soundQue2[i].sound = 0;
- _soundQue2[i].offset = 0;
- _soundQue2[i].channel = 0;
- _soundQue2[i].flags = 0;
- _soundQue2[i].freq = 0;
- _soundQue2[i].pan = 0;
- _soundQue2[i].vol = 0;
+ for (i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].sound == sound) {
+ _soundQueue[i].sound = 0;
+ _soundQueue[i].offset = 0;
+ _soundQueue[i].channel = 0;
+ _soundQueue[i].flags = 0;
+ _soundQueue[i].freq = 0;
+ _soundQueue[i].pan = 0;
+ _soundQueue[i].vol = 0;
}
}
}
@@ -1166,8 +1166,8 @@ void Sound::stopAllSounds() {
// Clear the (secondary) sound queue
_lastSound = 0;
- _soundQue2Pos = 0;
- memset(_soundQue2, 0, sizeof(_soundQue2));
+ _soundQueuePos = 0;
+ memset(_soundQueue, 0, sizeof(_soundQueue));
if (_vm->_musicEngine) {
_vm->_musicEngine->stopAllSounds();
@@ -1194,25 +1194,25 @@ void Sound::soundKludge(int *list, int num) {
if (list[0] == -1) {
processSound();
} else {
- _soundQue[_soundQuePos++] = num;
+ _midiQueue[_midiQueuePos++] = num;
for (i = 0; i < num; i++) {
- _soundQue[_soundQuePos++] = list[i];
+ _midiQueue[_midiQueuePos++] = list[i];
}
}
}
-void Sound::talkSound(uint32 a, uint32 b, int mode, int channel) {
- if (mode == 1) {
- _talk_sound_a1 = a;
- _talk_sound_b1 = b;
- _talk_sound_channel = channel;
+void Sound::talkSound(uint32 offset, uint32 length, int mode, int channel) {
+ if (mode == DIGI_SND_MODE_SFX) {
+ _queuedSfxOffset = offset;
+ _queuedSfxLen = length;
+ _queuedSfxChannel = channel;
} else {
- _talk_sound_a2 = a;
- _talk_sound_b2 = b;
+ _queuedTalkieOffset = offset;
+ _queuedTalkieLen = length;
}
- _talk_sound_mode |= mode;
+ _queuedSoundMode |= mode;
}
void Sound::setupSound() {
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 99bfcad0b57..836fb51da5b 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -35,6 +35,10 @@
#define DEFAULT_LOOM_OVERTURE_TRANSITION 1160
+#define DIGI_SND_MODE_EMPTY 0
+#define DIGI_SND_MODE_SFX 1
+#define DIGI_SND_MODE_TALKIE 2
+
namespace Audio {
class Mixer;
class SoundHandle;
@@ -65,8 +69,8 @@ protected:
ScummEngine *_vm;
Audio::Mixer *_mixer;
- int16 _soundQuePos, _soundQue[0x100];
- int16 _soundQue2Pos;
+ int16 _midiQueuePos, _midiQueue[0x100];
+ int16 _soundQueuePos;
struct {
int16 sound;
@@ -76,7 +80,7 @@ protected:
int16 freq;
int16 pan;
int16 vol;
- } _soundQue2[10];
+ } _soundQueue[10];
Common::String _sfxFilename;
byte _sfxFileEncByte;
@@ -84,8 +88,8 @@ protected:
MP3OffsetTable *_offsetTable; // For compressed audio
int _numSoundEffects; // For compressed audio
- uint32 _talk_sound_a1, _talk_sound_a2, _talk_sound_b1, _talk_sound_b2;
- byte _talk_sound_mode, _talk_sound_channel;
+ uint32 _queuedSfxOffset, _queuedTalkieOffset, _queuedSfxLen, _queuedTalkieLen;
+ byte _queuedSoundMode, _queuedSfxChannel;
bool _mouthSyncMode;
bool _endOfMouthSync;
uint16 _mouthSyncTimes[64];
@@ -106,7 +110,7 @@ public:
Audio::SoundHandle *_talkChannelHandle; // Handle of mixer channel actor is talking on
bool _soundsPaused;
- byte _sfxMode;
+ byte _digiSndMode;
uint _lastSound;
uint32 _cdMusicTimerMod;
uint32 _cdMusicTimer;
@@ -117,12 +121,12 @@ public:
public:
Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioTracks);
~Sound() override;
+ virtual void startSound(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0);
virtual void addSoundToQueue(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0);
- virtual void addSoundToQueue2(int sound, int heOffset = 0, int heChannel = 0, int heFlags = 0, int heFreq = 0, int hePan = 0, int heVol = 0);
void processSound();
virtual void modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) {};
- void playSound(int soundID);
+ void triggerSound(int soundID);
void startTalkSound(uint32 offset, uint32 b, int mode, Audio::SoundHandle *handle = NULL);
void stopTalkSound();
bool isMouthSyncOff(uint pos);
@@ -131,7 +135,7 @@ public:
virtual void stopSound(int sound);
virtual void stopAllSounds();
void soundKludge(int *list, int num);
- void talkSound(uint32 a, uint32 b, int mode, int channel = 0);
+ void talkSound(uint32 offset, uint32 length, int mode, int channel = 0);
virtual void setupSound();
void pauseSounds(bool pause);
bool isSfxFileCompressed();
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index af906813274..c3bb9bb7dd1 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -129,11 +129,11 @@ void ScummEngine::debugMessage(const byte *msg) {
}
if (buffer[0] == 0xFF && buffer[1] == 10) {
- uint32 a, b;
+ uint32 offset, length;
int channel = 0;
- a = buffer[2] | (buffer[3] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
- b = buffer[10] | (buffer[11] << 8) | (buffer[14] << 16) | (buffer[15] << 24);
+ offset = buffer[2] | (buffer[3] << 8) | (buffer[6] << 16) | (buffer[7] << 24);
+ length = buffer[10] | (buffer[11] << 8) | (buffer[14] << 16) | (buffer[15] << 24);
// Sam and Max uses a caching system, printing empty messages
// and setting VAR_V6_SOUNDMODE beforehand. See patch #8051.
@@ -141,7 +141,7 @@ void ScummEngine::debugMessage(const byte *msg) {
channel = VAR(VAR_V6_SOUNDMODE);
if (channel != 2)
- _sound->talkSound(a, b, 1, channel);
+ _sound->talkSound(offset, length, DIGI_SND_MODE_SFX, channel);
}
}
@@ -173,8 +173,8 @@ void ScummEngine::showMessageDialog(const byte *msg) {
bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
- uint32 talk_sound_a = 0;
- uint32 talk_sound_b = 0;
+ uint32 digiTalkieOffset = 0;
+ uint32 digiTalkieLength = 0;
int color, frme, c = 0, oldy;
bool endLoop = false;
byte *buffer = _charsetBuffer + _charsetBufPos;
@@ -419,17 +419,17 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
break;
case 10:
// Note the similarity to the code in debugMessage()
- talk_sound_a = buffer[0] | (buffer[1] << 8) | (buffer[4] << 16) | (buffer[5] << 24);
- talk_sound_b = buffer[8] | (buffer[9] << 8) | (buffer[12] << 16) | (buffer[13] << 24);
+ digiTalkieOffset = buffer[0] | (buffer[1] << 8) | (buffer[4] << 16) | (buffer[5] << 24);
+ digiTalkieLength = buffer[8] | (buffer[9] << 8) | (buffer[12] << 16) | (buffer[13] << 24);
buffer += 14;
if (_game.heversion >= 60) {
#ifdef ENABLE_HE
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
+ ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
#else
- ((SoundHE *)_sound)->startHETalkSound(talk_sound_a);
+ ((SoundHE *)_sound)->startHETalkSound(digiTalkieOffset);
#endif
} else {
- _sound->talkSound(talk_sound_a, talk_sound_b, 2);
+ _sound->talkSound(digiTalkieOffset, digiTalkieLength, DIGI_SND_MODE_TALKIE);
}
_haveActorSpeechMsg = false;
break;
@@ -464,8 +464,8 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
#ifdef ENABLE_HE
bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
const int charsetCode = (_game.heversion >= 80) ? 127 : 64;
- uint32 talk_sound_a = 0;
- //uint32 talk_sound_b = 0;
+ uint32 digiTalkieOffset = 0;
+ //uint32 digiTalkieLength = 0;
int i, c = 0;
char value[32];
bool endLoop = false;
@@ -487,7 +487,7 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
i++;
}
value[i] = 0;
- talk_sound_a = atoi(value);
+ digiTalkieOffset = atoi(value);
i = 0;
c = *buffer++;
while (c != charsetCode) {
@@ -496,8 +496,8 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
i++;
}
value[i] = 0;
- //talk_sound_b = atoi(value);
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
+ //digiTalkieLength = atoi(value);
+ ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
break;
case 104:
_haveMsg = 0;
@@ -518,9 +518,9 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
i++;
}
value[i] = 0;
- talk_sound_a = atoi(value);
- //talk_sound_b = 0;
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(talk_sound_a) : talk_sound_a);
+ digiTalkieOffset = atoi(value);
+ //digiTalkieLength = 0;
+ ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
break;
case 119:
_haveMsg = 0xFF;
@@ -929,7 +929,7 @@ void ScummEngine::CHARSET_1() {
if (_sound->isSoundRunning(1) == 0)
stopTalk();
} else {
- if ((_sound->_sfxMode & 2) == 0)
+ if ((_sound->_digiSndMode & DIGI_SND_MODE_TALKIE) == 0)
stopTalk();
}
return;
@@ -2009,7 +2009,7 @@ void ScummEngine_v7::playSpeech(const byte *ptr) {
_sound->stopTalkSound();
_imuseDigital->stopSound(kTalkSoundID);
_imuseDigital->startVoice(kTalkSoundID, pointerStr.c_str(), _actorToPrintStrFor);
- _sound->talkSound(0, 0, 2);
+ _sound->talkSound(0, 0, DIGI_SND_MODE_TALKIE);
}
}
diff --git a/engines/scumm/string_v7.cpp b/engines/scumm/string_v7.cpp
index 7218651f01f..7253bc77b96 100644
--- a/engines/scumm/string_v7.cpp
+++ b/engines/scumm/string_v7.cpp
@@ -676,7 +676,7 @@ void ScummEngine_v7::CHARSET_1() {
return;
if ((!usingOldSystem && VAR(VAR_HAVE_MSG)) || (usingOldSystem && _haveMsg != 1)) {
- if ((_sound->_sfxMode & 2) == 0) {
+ if ((_sound->_digiSndMode & DIGI_SND_MODE_TALKIE) == 0) {
stopTalk();
}
return;
Commit: f2d409c3a85814744662730b29330be91c2b779e
https://github.com/scummvm/scummvm/commit/f2d409c3a85814744662730b29330be91c2b779e
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement some resource loading routines
Changed paths:
engines/scumm/he/script_v100he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.h
engines/scumm/sound.cpp
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 556a610a110..88ecd10ed2e 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -2474,9 +2474,19 @@ void ScummEngine_v100he::o100_getResourceSize() {
type = rtScript;
break;
case SO_SOUND:
- push(getSoundResourceSize(resid));
- // TODO: Implement the case in which resid < _numSounds
- return;
+ if (resid >= _numSounds) {
+ push(getSoundResourceSize(resid));
+ return;
+ } else {
+ if (getResourceAddress(rtSound, resid) == nullptr) {
+ type = rtSound;
+ } else {
+ push(getSoundResourceSize(resid));
+ return;
+ }
+
+ break;
+ }
default:
error("o100_getResourceSize: default type %d", subOp);
}
@@ -2484,6 +2494,12 @@ void ScummEngine_v100he::o100_getResourceSize() {
ptr = getResourceAddress(type, resid);
assert(ptr);
size = READ_BE_UINT32(ptr + 4) - 8;
+
+ // Remove the size of the sound header
+ if (type == rtSound) {
+ size -= 40;
+ }
+
push(size);
}
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index ac799bba4b1..5300b9bf4e9 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -49,7 +49,7 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
Sound(parent, mixer, false),
_vm((ScummEngine_v60he *)parent),
_overrideFreq(0),
- _heMusic(nullptr),
+ _heSpoolingMusicTable(nullptr),
_heMusicTracks(0),
_mutex(mutex) {
@@ -58,8 +58,11 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
}
SoundHE::~SoundHE() {
- free(_heMusic);
+ free(_heSpoolingMusicTable);
delete[] _heSoundChannels;
+
+ if (_spoolingMusicFile.isOpen())
+ _spoolingMusicFile.close();
}
void SoundHE::startSound(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
@@ -67,7 +70,7 @@ void SoundHE::startSound(int sound, int heOffset, int heChannel, int heFlags, in
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
if (heFlags & ScummEngine_v70he::HESndFlags::HE_SND_QUICK_START) {
- triggerDigitalSound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerDigitalSound(sound, heOffset, heChannel, heFlags);
} else {
Sound::startSound(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
@@ -118,7 +121,7 @@ void SoundHE::processSoundQueues() {
hePan = _soundQueue[_soundQueuePos].pan;
heVol = _soundQueue[_soundQueuePos].vol;
if (snd)
- triggerDigitalSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
_soundQueuePos = 0;
} else {
@@ -132,7 +135,7 @@ void SoundHE::processSoundQueues() {
hePan = _soundQueue[_soundQueuePos].pan;
heVol = _soundQueue[_soundQueuePos].vol;
if (snd)
- triggerDigitalSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
}
}
@@ -158,7 +161,7 @@ int SoundHE::isSoundRunning(int sound) const {
if (isSoundInQueue(sound))
return sound;
- if (_vm->_musicEngine &&_vm->_musicEngine->getSoundStatus(sound))
+ if (_vm->_musicEngine && _vm->_musicEngine->getSoundStatus(sound))
return sound;
return 0;
@@ -259,25 +262,42 @@ void SoundHE::stopSoundChannel(int chan) {
}
}
-int SoundHE::findFreeSoundChannel() {
- int chan, min;
+int SoundHE::getNextDynamicChannel() {
+ int firstChan, minAge, chosenChan;
+
+ firstChan = _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS);
- min = _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS);
- if (min == 0) {
- _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS) = 8;
- return 1;
+ if (firstChan < 0) {
+ _vm->VAR(_vm->VAR_START_DYN_SOUND_CHANNELS) = HSND_MAX_CHANNELS;
+ firstChan = HSND_MAX_CHANNELS;
}
- if (min < 8) {
- for (chan = min; chan < ARRAYSIZE(_heChannel); chan++) {
- if (_mixer->isSoundHandleActive(_heSoundChannels[chan]) == 0)
- return chan;
+ if (firstChan < HSND_MAX_CHANNELS) {
+ for (int i = firstChan; i < HSND_MAX_CHANNELS; i++) {
+ if (!_heChannel[i].sound)
+ return i;
}
- } else {
- return 1;
+
+ minAge = INT_MAX;
+ chosenChan = -1;
+
+ for (int i = firstChan; i < HSND_MAX_CHANNELS; i++) {
+
+ if (_heChannel[i].age <= minAge) {
+ minAge = _heChannel[i].age;
+ chosenChan = i;
+ }
+ }
+
+ // Found it!
+ if (chosenChan != -1)
+ return chosenChan;
+
+ // ...just get the first one, then :-)
+ return firstChan;
}
- return min;
+ return 1;
}
bool SoundHE::isSoundCodeUsed(int sound) {
@@ -350,34 +370,83 @@ void SoundHE::setOverrideFreq(int freq) {
}
void SoundHE::setupHEMusicFile() {
- int i;
- Common::File musicFile;
- Common::String buf(_vm->generateFilename(-4));
-
- if (musicFile.open(buf) == true) {
- musicFile.seek(4, SEEK_SET);
- /*int total_size =*/ musicFile.readUint32BE();
- musicFile.seek(16, SEEK_SET);
- _heMusicTracks = musicFile.readUint32LE();
- debug(5, "Total music tracks %d", _heMusicTracks);
-
- int musicStart = (_vm->_game.heversion >= 80) ? 56 : 20;
- musicFile.seek(musicStart, SEEK_SET);
-
- _heMusic = (HEMusic *)malloc((_heMusicTracks + 1) * sizeof(HEMusic));
- for (i = 0; i < _heMusicTracks; i++) {
- _heMusic[i].id = musicFile.readUint32LE();
- _heMusic[i].offset = musicFile.readUint32LE();
- _heMusic[i].size = musicFile.readUint32LE();
-
- if (_vm->_game.heversion >= 80) {
- musicFile.seek(+9, SEEK_CUR);
+ uint32 id, len;
+ Common::String musicFilename(_vm->generateFilename(-4));
+
+ if (_spoolingMusicFile.open(musicFilename)) {
+
+ id = _spoolingMusicFile.readUint32BE();
+ len = _spoolingMusicFile.readUint32BE();
+ if (id == MKTAG('S', 'O', 'N', 'G')) {
+
+ // Older versions had a much simpler file structure
+ if (_vm->_game.heversion < 80) {
+ // Skip header wrapping file
+ _spoolingMusicFile.seek(16, SEEK_SET);
+ _heMusicTracks = _spoolingMusicFile.readUint32LE();
} else {
- musicFile.seek(+13, SEEK_CUR);
+ // HE 80 and above
+ id = _spoolingMusicFile.readUint32BE();
+ len = _spoolingMusicFile.readUint32BE();
+ if (id == MKTAG('S', 'G', 'H', 'D')) {
+ _heMusicTracks = _spoolingMusicFile.readUint32LE();
+ _spoolingMusicFile.seek(len - 8 - 4, SEEK_CUR);
+ } else {
+ _spoolingMusicFile.close();
+ debug(5, "setupHEMusicFile(): Invalid spooling file '%s', couldn't find SGHD tag, found %s", musicFilename.c_str(), tag2str(id));
+ return;
+ }
}
- }
- musicFile.close();
+ debug(5, "setupHEMusicFile(): music files count = %d", _heMusicTracks);
+ _heSpoolingMusicTable = (HESpoolingMusicItem *)malloc(_heMusicTracks * sizeof(HESpoolingMusicItem));
+
+ if (_heSpoolingMusicTable != nullptr) {
+ for (int i = 0; i < _heMusicTracks; i++) {
+
+ // For later versions we check that we are actually reading a SGEN section...
+ if (_vm->_game.heversion >= 80) {
+ id = _spoolingMusicFile.readUint32BE();
+ len = _spoolingMusicFile.readUint32BE();
+ if (id != MKTAG('S', 'G', 'E', 'N')) {
+ _spoolingMusicFile.close();
+ debug(5, "setupHEMusicFile(): Invalid spooling file '%s', couldn't find SGEN tag, found %s", musicFilename.c_str(), tag2str(id));
+ return;
+ }
+ }
+
+ _heSpoolingMusicTable[i].song = _spoolingMusicFile.readSint32LE();
+ _heSpoolingMusicTable[i].offset = _spoolingMusicFile.readSint32LE();
+ _heSpoolingMusicTable[i].size = _spoolingMusicFile.readSint32LE();
+
+ int amountToRead = _vm->_game.heversion >= 80 ? 9 : 13;
+ int readAmount = 0;
+ for (readAmount = 0; readAmount < amountToRead; readAmount++) {
+ _heSpoolingMusicTable[i].filename[readAmount] = _spoolingMusicFile.readByte();
+
+ // Early string termination
+ if (_heSpoolingMusicTable[i].filename[readAmount] == '\0')
+ break;
+ }
+ // Not all filenames have a string termination char, let's include it;
+ // we don't need to do that if we had an early string termination
+ if (readAmount == amountToRead)
+ _heSpoolingMusicTable[i].filename[amountToRead] = '\0';
+
+ debug(5, "setupHEMusicFile(): read music file '%s' song %d, offset %d, size %d",
+ _heSpoolingMusicTable[i].filename,
+ _heSpoolingMusicTable[i].song,
+ _heSpoolingMusicTable[i].offset,
+ _heSpoolingMusicTable[i].size);
+ }
+ } else {
+ debug(5, "setupHEMusicFile(): Can't allocate table for spooling music file '%s'", musicFilename.c_str());
+ }
+ } else {
+ debug(5, "setupHEMusicFile(): Invalid file '%s', couldn't find SONG tag, found %s", musicFilename.c_str(), tag2str(id));
+ }
+ } else {
+ debug(5, "setupHEMusicFile(): Can't open spooling music file '%s'", musicFilename.c_str());
}
}
@@ -385,9 +454,9 @@ bool SoundHE::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
int i;
for (i = 0; i < _heMusicTracks; i++) {
- if (_heMusic[i].id == id) {
- musicOffs = _heMusic[i].offset;
- musicSize = _heMusic[i].size;
+ if (_heSpoolingMusicTable[i].song == id) {
+ musicOffs = _heSpoolingMusicTable[i].offset;
+ musicSize = _heSpoolingMusicTable[i].size;
return 1;
}
}
@@ -693,7 +762,47 @@ byte *findSoundTag(uint32 tag, byte *ptr) {
return nullptr;
}
-void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+ if (_vm->_game.heversion >= 95) {
+ if (heChannel == HSND_DYN_SOUND_CHAN) {
+ heChannel = getNextDynamicChannel();
+ }
+ }
+
+ if (soundId >= _vm->_numSounds) {
+ triggerSpoolingSound(soundId, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ return;
+ }
+
+ byte *soundAddr = (byte *)_vm->getResourceAddress(rtSound, soundId);
+
+ if ((READ_BE_UINT32(soundAddr) == MKTAG('D', 'I', 'G', 'I')) || (READ_BE_UINT32(soundAddr) == MKTAG('T', 'A', 'L', 'K'))) {
+ triggerDigitalSound(soundId, heOffset, heChannel, heFlags);
+ } else if (READ_BE_UINT32(soundAddr) == MKTAG('M', 'I', 'D', 'I')) {
+ // Nothing happens in the original as well
+ return;
+ } else if (READ_BE_UINT32(soundAddr) == MKTAG('W', 'S', 'O', 'U')) {
+ triggerRIFFSound(soundId, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ } else if (READ_BE_UINT32(soundAddr) == MKTAG('X', 'S', 'O', 'U')) {
+ triggerXSOUSound(soundId, heOffset, heChannel, heFlags);
+ } else {
+ error("SoundHE::triggerSound(): Illegal sound %d type %s", soundId, tag2str(READ_BE_UINT32(soundAddr)));
+ }
+}
+
+void SoundHE::triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+
+}
+
+void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+
+}
+
+void SoundHE::triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags) {
+ error("SoundHE::triggerXSOUSound(): unimplemented XSOU format for sound %d", soundId);
+}
+
+void SoundHE::triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags) {
Audio::RewindableAudioStream *stream = nullptr;
byte *ptr, *spoolPtr;
int size = -1;
@@ -701,23 +810,23 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
byte flags = Audio::FLAG_UNSIGNED;
Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType;
- if (soundID > _vm->_numSounds)
+ if (soundId > _vm->_numSounds)
type = Audio::Mixer::kMusicSoundType;
- else if (soundID == 1)
+ else if (soundId == 1)
type = Audio::Mixer::kSpeechSoundType;
if (heChannel == -1)
- heChannel = (_vm->VAR_START_DYN_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1;
+ heChannel = (_vm->VAR_START_DYN_SOUND_CHANNELS != 0xFF) ? getNextDynamicChannel() : 1;
- debug(5,"triggerDigitalSound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags);
+ debug(5,"triggerDigitalSound: soundId %d heOffset %d heChannel %d heFlags %d", soundId, heOffset, heChannel, heFlags);
- if (soundID >= 10000) {
+ if (soundId >= 10000) {
// Special codes, used in pjgames
return;
}
- if (soundID > _vm->_numSounds) {
+ if (soundId > _vm->_numSounds) {
int music_offs;
Common::File musicFile;
Common::String buf(_vm->generateFilename(-4));
@@ -726,8 +835,8 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
warning("triggerDigitalSound: Can't open music file %s", buf.c_str());
return;
}
- if (!getHEMusicDetails(soundID, music_offs, size)) {
- debug(0, "triggerDigitalSound: musicID %d not found", soundID);
+ if (!getHEMusicDetails(soundId, music_offs, size)) {
+ debug(0, "triggerDigitalSound: musicID %d not found", soundId);
return;
}
@@ -741,21 +850,21 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
if (_vm->_game.heversion == 70) {
// Try to load high quality audio file if found
- stream = tryLoadAudioOverride(soundID);
+ stream = tryLoadAudioOverride(soundId);
if (!stream) {
stream = Audio::makeRawStream(spoolPtr, size, 11025, flags, DisposeAfterUse::NO);
}
- _mixer->playStream(type, &_heSoundChannels[heChannel], stream, soundID);
+ _mixer->playStream(type, &_heSoundChannels[heChannel], stream, soundId);
return;
}
}
- if (soundID > _vm->_numSounds) {
+ if (soundId > _vm->_numSounds) {
ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
} else {
- ptr = _vm->getResourceAddress(rtSound, soundID);
+ ptr = _vm->getResourceAddress(rtSound, soundId);
}
if (!ptr) {
@@ -769,7 +878,7 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
int samplesPerBlock;
int codeOffs = -1;
- priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18);
+ priority = (soundId > _vm->_numSounds) ? 255 : *(ptr + 18);
byte *sbngPtr = findSoundTag(MKTAG('S','B','N','G'), ptr);
if (sbngPtr != nullptr) {
@@ -778,9 +887,9 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
int curSnd = _heChannel[heChannel].sound;
- if (curSnd == 1 && soundID != 1)
+ if (curSnd == 1 && soundId != 1)
return;
- if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority)
+ if (curSnd != 0 && curSnd != 1 && soundId != 1 && _heChannel[heChannel].priority > priority)
return;
}
@@ -791,7 +900,7 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
Common::MemoryReadStream memStream(ptr, size);
if (!Audio::loadWAVFromStream(memStream, size, rate, flags, &compType, &blockAlign, &samplesPerBlock)) {
- error("triggerDigitalSound: Not a valid WAV file (%d)", soundID);
+ error("triggerDigitalSound: Not a valid WAV file (%d)", soundId);
}
assert(heOffset >= 0 && heOffset < size);
@@ -800,7 +909,7 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
heOffset = 0;
_vm->setHETimer(heChannel + 4);
- _heChannel[heChannel].sound = soundID;
+ _heChannel[heChannel].sound = soundId;
_heChannel[heChannel].priority = priority;
_heChannel[heChannel].frequency = rate;
_heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
@@ -845,14 +954,14 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
stream = Audio::makeRawStream(ptr + memStream.pos() + heOffset, size - heOffset, rate, flags, DisposeAfterUse::NO);
}
_mixer->playStream(type, &_heSoundChannels[heChannel],
- Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundID);
+ Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundId);
}
// Support for sound in Humongous Entertainment games
else if (READ_BE_UINT32(ptr) == MKTAG('D','I','G','I') || READ_BE_UINT32(ptr) == MKTAG('T','A','L','K')) {
byte *sndPtr = ptr;
int codeOffs = -1;
- priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18);
+ priority = (soundId > _vm->_numSounds) ? 255 : *(ptr + 18);
rate = READ_LE_UINT16(ptr + 22);
// Skip DIGI/TALK (8) and HSHD (24) blocks
@@ -860,9 +969,9 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
int curSnd = _heChannel[heChannel].sound;
- if (curSnd == 1 && soundID != 1)
+ if (curSnd == 1 && soundId != 1)
return;
- if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority)
+ if (curSnd != 0 && curSnd != 1 && soundId != 1 && _heChannel[heChannel].priority > priority)
return;
}
@@ -887,14 +996,14 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
// Try to load high quality audio file if found
int newDuration;
- stream = tryLoadAudioOverride(soundID, &newDuration);
- if (stream != nullptr && soundID == 1) {
+ stream = tryLoadAudioOverride(soundId, &newDuration);
+ if (stream != nullptr && soundId == 1) {
// Disable lip sync if the speech audio was overriden
codeOffs = -1;
}
_vm->setHETimer(heChannel + 4);
- _heChannel[heChannel].sound = soundID;
+ _heChannel[heChannel].sound = soundId;
_heChannel[heChannel].priority = priority;
_heChannel[heChannel].frequency = rate;
_heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
@@ -918,7 +1027,7 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
stream = Audio::makeRawStream(ptr + heOffset + 8, size, rate, flags, DisposeAfterUse::NO);
}
_mixer->playStream(type, &_heSoundChannels[heChannel],
- Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundID);
+ Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundId);
}
// Support for PCM music in 3DO versions of Humongous Entertainment games
else if (READ_BE_UINT32(ptr) == MKTAG('M','R','A','W')) {
@@ -935,10 +1044,10 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
memcpy(sound, ptr + 8, size);
_mixer->stopID(_currentMusic);
- _currentMusic = soundID;
+ _currentMusic = soundId;
stream = Audio::makeRawStream(sound, size, rate, 0);
- _mixer->playStream(Audio::Mixer::kMusicSoundType, nullptr, stream, soundID);
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, nullptr, stream, soundId);
}
else if (READ_BE_UINT32(ptr) == MKTAG('M','I','D','I')) {
if (_vm->_imuse) {
@@ -946,17 +1055,17 @@ void SoundHE::triggerDigitalSound(int soundID, int heOffset, int heChannel, int
// Birthday Surprise to change the note on the piano
// when not using a digitized instrument.
_vm->_imuse->stopSound(_currentMusic);
- _currentMusic = soundID;
- _vm->_imuse->startSoundWithNoteOffset(soundID, heOffset);
+ _currentMusic = soundId;
+ _vm->_imuse->startSoundWithNoteOffset(soundId, heOffset);
} else if (_vm->_musicEngine) {
_vm->_musicEngine->stopSound(_currentMusic);
- _currentMusic = soundID;
- _vm->_musicEngine->startSoundWithTrackID(soundID, heOffset);
+ _currentMusic = soundId;
+ _vm->_musicEngine->startSoundWithTrackID(soundId, heOffset);
}
}
}
-Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundID, int *duration) {
+Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *duration) {
if (!_vm->_enableAudioOverride) {
return nullptr;
}
@@ -993,20 +1102,20 @@ Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundID, int *du
);
const char *type;
- if (soundID == 1) {
+ if (soundId == 1) {
// Speech audio doesn't have a unique ID,
// so we use the file offset instead.
// _heTalkOffset is set at startHETalkSound.
type = "speech";
- soundID = _heTalkOffset;
+ soundId = _heTalkOffset;
} else {
// Music and sfx share the same prefix.
type = "sound";
}
for (int i = 0; i < ARRAYSIZE(formats); i++) {
- Common::Path pathDir(Common::String::format("%s%d.%s", type, soundID, formats[i]));
- Common::Path pathSub(Common::String::format("%s/%d.%s", type, soundID, formats[i]));
+ Common::Path pathDir(Common::String::format("%s%d.%s", type, soundId, formats[i]));
+ Common::Path pathSub(Common::String::format("%s/%d.%s", type, soundId, formats[i]));
debug(5, "tryLoadAudioOverride: %s or %s", pathSub.toString().c_str(), pathDir.toString().c_str());
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index cd93948ec55..d69dc0095a8 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -29,6 +29,7 @@
namespace Scumm {
+#define HSND_DYN_SOUND_CHAN -1
#define HSND_SOUND_STOPPED 1
#define HSND_SOUND_ENDED 2
#define HSND_SOUND_TIMEOUT 3
@@ -87,17 +88,19 @@ protected:
};
HESoundCallbackItem _soundCallbackScripts[HSND_MAX_CALLBACK_SCRIPTS];
- struct HEMusic {
- int32 id;
+ struct HESpoolingMusicItem {
+ int32 song;
int32 offset;
int32 size;
char filename[128];
};
- HEMusic *_heMusic;
- int16 _heMusicTracks;
+
+ HESpoolingMusicItem *_heSpoolingMusicTable;
+ int32 _heMusicTracks;
Audio::SoundHandle *_heSoundChannels;
+ Common::File _spoolingMusicFile;
public: // Used by createSound()
struct {
@@ -140,12 +143,16 @@ public:
void setupSound() override;
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
- int findFreeSoundChannel();
+ int getNextDynamicChannel();
bool isSoundCodeUsed(int sound);
int getSoundPos(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
- void triggerDigitalSound(int soundID, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags);
+ void triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags);
void handleSoundFrame();
void unqueueSoundCallbackScripts();
void checkSoundTimeouts();
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 26083721e54..92955f69a3d 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1796,7 +1796,7 @@ public:
byte VAR_SOUND_CHANNEL = 0xFF; // Used in o_startSound()
byte VAR_TALK_CHANNEL = 0xFF; // Used in startHETalkSound()
byte VAR_SOUND_TOKEN_OFFSET = 0xFF; // Used in handleSoundFrame()
- byte VAR_START_DYN_SOUND_CHANNELS = 0xFF; // Used in findFreeSoundChannel()
+ byte VAR_START_DYN_SOUND_CHANNELS = 0xFF; // Used in getNextDynamicChannel()
byte VAR_SOUND_CALLBACK_SCRIPT = 0xFF;
byte VAR_EARLY_CHAN_0_CALLBACK = 0xFF;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index d6a7f6955c3..ddff09676b6 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1754,11 +1754,30 @@ int ScummEngine::readSoundResource(ResId idx) {
case MKTAG('D','I','G','I'):
case MKTAG('C','r','e','a'):
case 0x460e200d: // WORKAROUND bug #2221
- _fileHandle->seek(-12, SEEK_CUR);
- total_size = _fileHandle->readUint32BE();
- ptr = _res->createResource(rtSound, idx, total_size);
- _fileHandle->read(ptr, total_size - 8);
- //dumpResource("sound-", idx, ptr);
+ // Some HE games take WAV files and puts a WSOU header around it
+ if (_game.heversion > 0 && basetag == MKTAG('R','I','F','F')) {
+ _fileHandle->seek(-4, SEEK_CUR);
+
+ // The chunksize field in a RIFF header is a LE field
+ total_size = _fileHandle->readUint32LE();
+
+ // Make space for the WSOU tag and for the size field
+ ptr = _res->createResource(rtSound, idx, total_size + 8);
+
+ ((uint32 *)ptr)[0] = TO_BE_32(MKTAG('W', 'S', 'O', 'U'));
+ ((uint32 *)ptr)[1] = total_size + 8;
+
+ // Move the ptr forward for the actual data allocation,
+ // so that our new header doesn't get rewritten
+ ptr += 8;
+ } else {
+ _fileHandle->seek(-12, SEEK_CUR);
+ total_size = _fileHandle->readUint32BE();
+ ptr = _res->createResource(rtSound, idx, total_size);
+ _fileHandle->read(ptr, total_size - 8);
+ //dumpResource("sound-", idx, ptr);
+ }
+
return 1;
case MKTAG('H','S','H','D'):
Commit: 736a2a262160b579e4a65334906f6284238b5f5d
https://github.com/scummvm/scummvm/commit/736a2a262160b579e4a65334906f6284238b5f5d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement RIFF format handling
Changed paths:
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/sound.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 5300b9bf4e9..b1603ab166d 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -117,11 +117,12 @@ void SoundHE::processSoundQueues() {
heOffset = _soundQueue[i].offset;
heChannel = _soundQueue[i].channel;
heFlags = _soundQueue[i].flags;
- heFreq = _soundQueue[_soundQueuePos].freq;
- hePan = _soundQueue[_soundQueuePos].pan;
- heVol = _soundQueue[_soundQueuePos].vol;
+ heFreq = _soundQueue[i].freq;
+ hePan = _soundQueue[i].pan;
+ heVol = _soundQueue[i].vol;
+
if (snd)
- triggerSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
}
_soundQueuePos = 0;
} else {
@@ -134,8 +135,9 @@ void SoundHE::processSoundQueues() {
heFreq = _soundQueue[_soundQueuePos].freq;
hePan = _soundQueue[_soundQueuePos].pan;
heVol = _soundQueue[_soundQueuePos].vol;
+
if (snd)
- triggerSound(snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
}
}
@@ -762,7 +764,7 @@ byte *findSoundTag(uint32 tag, byte *ptr) {
return nullptr;
}
-void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
if (_vm->_game.heversion >= 95) {
if (heChannel == HSND_DYN_SOUND_CHAN) {
heChannel = getNextDynamicChannel();
@@ -770,7 +772,7 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
}
if (soundId >= _vm->_numSounds) {
- triggerSpoolingSound(soundId, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerSpoolingSound(soundId, heOffset, heChannel, heFlags, modifiers);
return;
}
@@ -782,7 +784,7 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
// Nothing happens in the original as well
return;
} else if (READ_BE_UINT32(soundAddr) == MKTAG('W', 'S', 'O', 'U')) {
- triggerRIFFSound(soundId, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ triggerRIFFSound(soundId, heOffset, heChannel, heFlags, modifiers);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('X', 'S', 'O', 'U')) {
triggerXSOUSound(soundId, heOffset, heChannel, heFlags);
} else {
@@ -790,18 +792,158 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
}
}
-void SoundHE::triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
}
-void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void SoundHE::triggerRIFFSound(int sound, int offset, int channel, int flags, HESoundModifiers modifiers) {
+ PCMWaveFormat pFmt;
+ uint8 *soundDataPtr = nullptr;
+ int sampleCount = 0;
+ int soundPriority = 128;
+ int soundCodeOffset = -1;
+ bool parsedFmt = false;
+
+ // Let's begin by fetching the sound address...
+ uint8 *wsouPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
+
+ // We only accept the WSOU format compliant files,
+ // which are WAV files with a WSOU header wrapped around.
+ // Still, let's not use an assertion like the original does,
+ // and let's bail out gracefully instead....
+ if (READ_BE_UINT32(wsouPtr) != MKTAG('W', 'S', 'O', 'U')) {
+ debug("SoundHE::triggerRIFFSound(): Couldn't find WSOU tag for sound %d, bailing out...", sound);
+ return;
+ }
+
+ // Skip over the WSOU header and hope to find a RIFF header...
+ uint8 *soundPtr = wsouPtr + 8;
+ if (READ_BE_UINT32(soundPtr) != MKTAG('R', 'I', 'F', 'F')) {
+ debug("SoundHE::triggerRIFFSound(): Couldn't find RIFF tag for sound %d, bailing out...", sound);
+ return;
+ }
+
+ // Since all sub-blocks must be padded to be even, we want the RIFF block length to be even...
+ int riffLength = READ_LE_UINT32(soundPtr + 4);
+ if ((riffLength & 1) != 0) {
+ debug("SoundHE::triggerRIFFSound(): RIFF block length not even (%d) for sound %d, bailing out...", riffLength, sound);
+ return;
+ }
+
+ uint8 *wavePtr = soundPtr + 8;
+ if (READ_BE_UINT32(wavePtr) != MKTAG('W', 'A', 'V', 'E')) {
+ debug("SoundHE::triggerRIFFSound(): Couldn't find WAVE tag for sound %d, bailing out...", sound);
+ return;
+ }
+
+ // Now that we've made all the safety checks needed, fetch the sound data...
+ wavePtr += 4;
+ riffLength -= 4;
+
+ // Parse the various blocks...
+ while (riffLength > 0) {
+ int chunkId = READ_BE_UINT32(wavePtr);
+ int chunkLength = READ_LE_UINT32(wavePtr + 4);
+ wavePtr += 8;
+ riffLength -= 8;
+
+ if (chunkLength < 0) {
+ debug("SoundHE::triggerRIFFSound(): Illegal chunk length - %d bytes, bailing out...", chunkLength);
+ return;
+ }
+
+ if (chunkLength > riffLength) {
+ debug("SoundHE::triggerRIFFSound(): Chunk extends beyond file end - %d versus %d, bailing out...", chunkLength, riffLength);
+ return;
+ }
+
+ switch (chunkId) {
+ case MKTAG('f', 'm', 't', ' '):
+ {
+ uint8 *tempPtr = wavePtr;
+ pFmt.wFormatTag = READ_LE_UINT16(tempPtr); tempPtr += 2;
+ pFmt.wChannels = READ_LE_UINT16(tempPtr); tempPtr += 2;
+ pFmt.dwSamplesPerSec = READ_LE_UINT32(tempPtr); tempPtr += 4;
+ pFmt.dwAvgBytesPerSec = READ_LE_UINT32(tempPtr); tempPtr += 4;
+ pFmt.wBlockAlign = READ_LE_UINT16(tempPtr); tempPtr += 2;
+ pFmt.wBitsPerSample = READ_LE_UINT16(tempPtr); tempPtr += 2;
+
+ if (pFmt.wFormatTag != WAVE_FORMAT_PCM && pFmt.wFormatTag != WAVE_FORMAT_IMA_ADPCM) {
+ debug("SoundHE::triggerRIFFSound(): Unsupported .wav sound type %d - only PCM and IMA ADPCM are supported.", pFmt.wFormatTag);
+ return;
+ }
+
+ parsedFmt = true;
+ break;
+ }
+ case MKTAG('d', 'a', 't', 'a'):
+ assert(parsedFmt);
+ soundDataPtr = wavePtr;
+ sampleCount = (chunkLength * 8) / (pFmt.wChannels * pFmt.wBitsPerSample);
+ break;
+
+ case MKTAG('X', 'S', 'H', '2'):
+ {
+ // Check for the optional sound flag block
+ int optionalBlockFlags = READ_BE_UINT32(wavePtr);
+ if (optionalBlockFlags & XSH2_FLAG_HAS_PRIORITY)
+ soundPriority = READ_BE_UINT32(wavePtr + 4);
+
+ break;
+ }
+
+ case MKTAG('S', 'B', 'N', 'G'):
+ soundCodeOffset = wavePtr - wsouPtr;
+ break;
+
+ default:
+ break;
+ }
+
+ // Round up to the next multiple of two
+ chunkLength = (chunkLength + 1) & ~1;
+ wavePtr += chunkLength;
+ riffLength -= chunkLength;
+ }
+
+ if (riffLength != 0 || !parsedFmt || soundDataPtr == nullptr)
+ return;
+
+ int sampleFrequency = pFmt.dwSamplesPerSec;
+ int bitsPerSample = pFmt.wBitsPerSample;
+ int sampleChannelCount = pFmt.wChannels;
+
+ int soundDataOffset = soundDataPtr - soundPtr;
+
+ // Make sure that the sound has a high enough priority to play
+ if (_heChannel[channel].sound && sound != HSND_TALKIE_SLOT && _heChannel[channel].sound != HSND_TALKIE_SLOT) {
+ if (soundPriority < _heChannel[channel].priority)
+ return;
+ }
+
+ // Finally start the sound
+ hsStartDigitalSound(sound, offset, soundPtr, soundDataOffset, rtSound, sound,
+ sampleCount, sampleFrequency, channel, soundPriority, soundCodeOffset,
+ flags, bitsPerSample, sampleChannelCount, modifiers);
+ return;
}
void SoundHE::triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags) {
error("SoundHE::triggerXSOUSound(): unimplemented XSOU format for sound %d", soundId);
}
+void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int sound_data,
+ int globType, int globNum, int sampleCount, int frequency, int channel, int priority,
+ int soundCode, int flags, int bitsPerSample, int soundChannelCount, HESoundModifiers modifiers) {
+
+ return;
+}
+
+void SoundHE::hsStopDigitalSound(int sound) {
+ return;
+}
+
void SoundHE::triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags) {
Audio::RewindableAudioStream *stream = nullptr;
byte *ptr, *spoolPtr;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index d69dc0095a8..36bdd5c143b 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -30,10 +30,10 @@
namespace Scumm {
#define HSND_DYN_SOUND_CHAN -1
+#define HSND_TALKIE_SLOT 1
#define HSND_SOUND_STOPPED 1
#define HSND_SOUND_ENDED 2
#define HSND_SOUND_TIMEOUT 3
-#define HSND_TALKIE_SLOT 4
#define HSND_TIMER_SLOT 4
#define HSND_MAX_CALLBACK_SCRIPTS 20
#define HSND_MAX_CHANNELS 8
@@ -50,8 +50,12 @@ namespace Scumm {
#define HSND_BASE_FREQ_FACTOR 1024
#define HSND_SOUND_FREQ_BASE 1024
#define HSND_MAX_VOLUME 255
+#define HSND_SOUND_PAN_LEFT 0
#define HSND_SOUND_PAN_CENTER 64
+#define HSND_SOUND_PAN_RIGHT 127
#define HSND_FIRST_SPOOLING_SOUND 4000
+#define HSND_MAX_FREQ_RATIO 4
+
#define HSND_SBNG_TYPE_ALL 0xF8
#define HSND_SBNG_DATA_ALL 0x07
@@ -71,6 +75,10 @@ namespace Scumm {
#define HSND_SBNG_VARORVAL 0x03
#define HSND_SBNG_VARVAL 0x02
+#define XSH2_FLAG_HAS_PRIORITY 0x01
+
+#define WAVE_FORMAT_PCM 1
+#define WAVE_FORMAT_IMA_ADPCM 17
class ScummEngine_v60he;
@@ -95,8 +103,39 @@ protected:
char filename[128];
};
-
HESpoolingMusicItem *_heSpoolingMusicTable;
+
+ struct PCMWaveFormat {
+ uint16 wFormatTag;
+ uint16 wChannels;
+ uint32 dwSamplesPerSec;
+ uint32 dwAvgBytesPerSec;
+ uint16 wBlockAlign;
+ uint16 wBitsPerSample;
+ };
+
+ struct HESoundModifiers {
+ HESoundModifiers(int mFrequencyShift, int mPan, int mVolume) {
+ assert(mFrequencyShift >= HSND_SOUND_FREQ_BASE / HSND_MAX_FREQ_RATIO);
+ assert(mFrequencyShift <= HSND_SOUND_FREQ_BASE * HSND_MAX_FREQ_RATIO);
+ assert(mPan >= HSND_SOUND_PAN_LEFT && mPan <= HSND_SOUND_PAN_RIGHT);
+ assert(mVolume >= 0 && mVolume <= HSND_MAX_VOLUME);
+ frequencyShift = mFrequencyShift;
+ pan = mPan;
+ volume = mVolume;
+ }
+
+ HESoundModifiers() {
+ frequencyShift = HSND_SOUND_FREQ_BASE;
+ pan = HSND_SOUND_PAN_CENTER;
+ volume = HSND_MAX_VOLUME;
+ }
+
+ int frequencyShift;
+ int pan;
+ int volume;
+ };
+
int32 _heMusicTracks;
Audio::SoundHandle *_heSoundChannels;
@@ -148,11 +187,17 @@ public:
int getSoundPos(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
- void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
- void triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
+ void triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags);
- void triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol);
+ void triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags);
+
+ void hsStartDigitalSound(int sound, int offset, byte *addr, int sound_data, int globType, int globNum,
+ int sampleCount, int frequency, int channel, int priority, int soundCode,
+ int flags, int bitsPerSample, int soundChannelCount, HESoundModifiers modifiers);
+ void hsStopDigitalSound(int sound);
+
void handleSoundFrame();
void unqueueSoundCallbackScripts();
void checkSoundTimeouts();
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index ddff09676b6..39f6ecec7cd 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1770,6 +1770,8 @@ int ScummEngine::readSoundResource(ResId idx) {
// Move the ptr forward for the actual data allocation,
// so that our new header doesn't get rewritten
ptr += 8;
+ _fileHandle->seek(-8, SEEK_CUR);
+ _fileHandle->read(ptr, total_size);
} else {
_fileHandle->seek(-12, SEEK_CUR);
total_size = _fileHandle->readUint32BE();
Commit: 4b42a4bb44d4b96befc0443608cfa97b09782116
https://github.com/scummvm/scummvm/commit/4b42a4bb44d4b96befc0443608cfa97b09782116
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Add empty mixer files
Changed paths:
A engines/scumm/he/mixer_he.cpp
A engines/scumm/he/mixer_he.h
engines/scumm/module.mk
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
new file mode 100644
index 00000000000..8be52e7f21b
--- /dev/null
+++ b/engines/scumm/he/mixer_he.cpp
@@ -0,0 +1,24 @@
+/* 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/>.
+ *
+ */
+
+namespace Scumm {
+
+} // End of namespace Scumm
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
new file mode 100644
index 00000000000..521419f7d4b
--- /dev/null
+++ b/engines/scumm/he/mixer_he.h
@@ -0,0 +1,26 @@
+/* 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 "common/util.h"
+
+namespace Scumm {
+
+} // End of namespace Scumm
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index e60ded31e65..b155953d244 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -24,6 +24,7 @@ MODULE_OBJS := \
he/script_v60he.o \
he/script_v70he.o \
he/sound_he.o \
+ he/mixer_he.o \
help.o \
imuse/imuse.o \
imuse/imuse_part.o \
Commit: d11e49ade2f79735e73fab0b228af7948dddbeb3
https://github.com/scummvm/scummvm/commit/d11e49ade2f79735e73fab0b228af7948dddbeb3
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement XSOU handling
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index b1603ab166d..22e03d8cf4e 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -796,7 +796,7 @@ void SoundHE::triggerSpoolingSound(int soundId, int heOffset, int heChannel, int
}
-void SoundHE::triggerRIFFSound(int sound, int offset, int channel, int flags, HESoundModifiers modifiers) {
+void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
PCMWaveFormat pFmt;
uint8 *soundDataPtr = nullptr;
int sampleCount = 0;
@@ -805,34 +805,34 @@ void SoundHE::triggerRIFFSound(int sound, int offset, int channel, int flags, HE
bool parsedFmt = false;
// Let's begin by fetching the sound address...
- uint8 *wsouPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
+ uint8 *wsouPtr = (byte *)_vm->getResourceAddress(rtSound, soundId);
// We only accept the WSOU format compliant files,
// which are WAV files with a WSOU header wrapped around.
// Still, let's not use an assertion like the original does,
// and let's bail out gracefully instead....
if (READ_BE_UINT32(wsouPtr) != MKTAG('W', 'S', 'O', 'U')) {
- debug("SoundHE::triggerRIFFSound(): Couldn't find WSOU tag for sound %d, bailing out...", sound);
+ debug("SoundHE::triggerRIFFSound(): Couldn't find WSOU tag for sound %d, bailing out...", soundId);
return;
}
// Skip over the WSOU header and hope to find a RIFF header...
uint8 *soundPtr = wsouPtr + 8;
if (READ_BE_UINT32(soundPtr) != MKTAG('R', 'I', 'F', 'F')) {
- debug("SoundHE::triggerRIFFSound(): Couldn't find RIFF tag for sound %d, bailing out...", sound);
+ debug("SoundHE::triggerRIFFSound(): Couldn't find RIFF tag for sound %d, bailing out...", soundId);
return;
}
// Since all sub-blocks must be padded to be even, we want the RIFF block length to be even...
int riffLength = READ_LE_UINT32(soundPtr + 4);
if ((riffLength & 1) != 0) {
- debug("SoundHE::triggerRIFFSound(): RIFF block length not even (%d) for sound %d, bailing out...", riffLength, sound);
+ debug("SoundHE::triggerRIFFSound(): RIFF block length not even (%d) for sound %d, bailing out...", riffLength, soundId);
return;
}
uint8 *wavePtr = soundPtr + 8;
if (READ_BE_UINT32(wavePtr) != MKTAG('W', 'A', 'V', 'E')) {
- debug("SoundHE::triggerRIFFSound(): Couldn't find WAVE tag for sound %d, bailing out...", sound);
+ debug("SoundHE::triggerRIFFSound(): Couldn't find WAVE tag for sound %d, bailing out...", soundId);
return;
}
@@ -916,21 +916,74 @@ void SoundHE::triggerRIFFSound(int sound, int offset, int channel, int flags, HE
int soundDataOffset = soundDataPtr - soundPtr;
// Make sure that the sound has a high enough priority to play
- if (_heChannel[channel].sound && sound != HSND_TALKIE_SLOT && _heChannel[channel].sound != HSND_TALKIE_SLOT) {
- if (soundPriority < _heChannel[channel].priority)
+ if (_heChannel[heChannel].sound && soundId != HSND_TALKIE_SLOT && _heChannel[heChannel].sound != HSND_TALKIE_SLOT) {
+ if (soundPriority < _heChannel[heChannel].priority)
return;
}
// Finally start the sound
- hsStartDigitalSound(sound, offset, soundPtr, soundDataOffset, rtSound, sound,
- sampleCount, sampleFrequency, channel, soundPriority, soundCodeOffset,
- flags, bitsPerSample, sampleChannelCount, modifiers);
-
- return;
+ hsStartDigitalSound(soundId, heOffset, soundPtr, soundDataOffset, rtSound, soundId,
+ sampleCount, sampleFrequency, heChannel, soundPriority, soundCodeOffset,
+ heFlags, bitsPerSample, sampleChannelCount, modifiers);
}
-void SoundHE::triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags) {
- error("SoundHE::triggerXSOUSound(): unimplemented XSOU format for sound %d", soundId);
+void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heFlags) {
+ int sampleFrequency, bitsPerSample, sampleChannelCount, soundPriority;
+ int soundCodeOffset, soundDataOffset, sampleCount;
+ byte *soundCodeBlockPtr, *soundHeaderBlock, *soundDataPtr, *soundPtr;
+ byte *optionalHeaderBlock;
+ int32 optionalBlockFlags;
+
+ soundPtr = (byte *)_vm->getResourceAddress(rtSound, heSound);
+
+ // Fetch the sound data format...
+ soundHeaderBlock = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('X', 'S', 'H', 'D'), soundPtr, 0, true);
+ soundHeaderBlock += 8;
+
+ sampleCount = READ_BE_UINT32(soundHeaderBlock + 0);
+ sampleFrequency = READ_BE_UINT32(soundHeaderBlock + 4);
+ bitsPerSample = READ_BE_UINT32(soundHeaderBlock + 8);
+ sampleChannelCount = READ_BE_UINT32(soundHeaderBlock + 12);
+
+ soundDataPtr = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('X', 'D', 'A', 'T'), soundPtr, 0, true);
+ soundDataOffset = ((soundDataPtr - soundPtr) + 8);
+
+ // Check for the optional sound flag block containing the priority...
+ soundPriority = 128;
+
+ optionalHeaderBlock = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('X', 'S', 'H', '2'), soundPtr, 0, false);
+
+ if (optionalHeaderBlock) {
+ optionalHeaderBlock += 8;
+ optionalBlockFlags = READ_BE_UINT32(optionalHeaderBlock);
+ optionalHeaderBlock += 4;
+
+ if (optionalBlockFlags & XSH2_FLAG_HAS_PRIORITY) {
+ soundPriority = READ_BE_UINT32(optionalHeaderBlock);
+ optionalHeaderBlock += 4;
+ }
+ }
+
+ // Check for the optional SBNG block...
+ soundCodeBlockPtr = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('S', 'B', 'N', 'G'), soundPtr, 0, false);
+
+ if (soundCodeBlockPtr == nullptr) {
+ soundCodeOffset = -1;
+ } else {
+ soundCodeOffset = (soundCodeBlockPtr - soundPtr) + 8;
+ }
+
+ // Make sure that the sound has a high enough priority to play
+ if (_heChannel[heChannel].sound && heSound != HSND_TALKIE_SLOT && _heChannel[heChannel].sound != HSND_TALKIE_SLOT) {
+ if (soundPriority < _heChannel[heChannel].priority)
+ return;
+ }
+
+ // Finally start the sound
+ hsStartDigitalSound(
+ heSound, heOffset, soundPtr, soundDataOffset, rtSound, heSound,
+ sampleCount, sampleFrequency, heChannel, soundPriority, soundCodeOffset,
+ heFlags, bitsPerSample, sampleChannelCount, HESoundModifiers());
}
void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int sound_data,
Commit: 44ae88f2304aaeec5f4da48522bb9e9c5c7d8310
https://github.com/scummvm/scummvm/commit/44ae88f2304aaeec5f4da48522bb9e9c5c7d8310
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement most audio engine functions
Also, added some mixer stub functions
Changed paths:
engines/scumm/he/intern_he.h
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/resource_he.cpp
engines/scumm/he/script_v100he.cpp
engines/scumm/he/script_v71he.cpp
engines/scumm/he/script_v80he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/sound.cpp
engines/scumm/vars.cpp
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index afa8929e043..40d2258bd45 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -492,7 +492,7 @@ protected:
class ScummEngine_v80he : public ScummEngine_v72he {
protected:
- int32 _heSndResId, _curSndId, _sndPtrOffs, _sndTmrOffs, _sndDataSize;
+ int32 _heSndResId;
enum SubOpType {
SO_CURSOR_IMAGE = 19,
SO_CURSOR_COLOR_IMAGE = 20,
@@ -516,8 +516,6 @@ protected:
void clearDrawQueues() override;
- void createSound(int snd1id, int snd2id);
-
void drawLine(int x1, int y1, int x, int unk1, int unk2, int type, int id);
void drawPixel(int x, int y, int flags);
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 8be52e7f21b..727ff4b3546 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -19,6 +19,138 @@
*
*/
+#include "mixer_he.h"
+
namespace Scumm {
+HEMixer::HEMixer(Audio::Mixer *mixer, bool useMilesSoundSystem) {
+ _mixer = mixer;
+ _useMilesSoundSystem = useMilesSoundSystem;
+}
+
+HEMixer::~HEMixer() {
+}
+
+void *HEMixer::getMilesSoundSystemObject() {
+ return nullptr;
+}
+
+bool HEMixer::initSoftMixerSubSystem() {
+ return false;
+}
+
+void HEMixer::deinitSoftMixerSubSystem() {
+}
+
+void HEMixer::endNeglectProcess() {
+}
+
+void HEMixer::startLongNeglectProcess() {
+}
+
+bool HEMixer::forceMusicBufferFill() {
+ return false;
+}
+
+bool HEMixer::isMixerDisabled() {
+ return false;
+}
+
+bool HEMixer::stopChannel(int channel) {
+ return false;
+}
+
+void HEMixer::stopAllChannels() {
+}
+
+bool HEMixer::changeChannelVolume(int channel, int volume, bool soft) {
+ return false;
+}
+
+void HEMixer::softRemixAllChannels() {
+}
+
+void HEMixer::premixUntilCritical() {
+}
+
+bool HEMixer::pauseMixerSubSystem(bool paused) {
+ return false;
+}
+
+void HEMixer::feedMixer() {
+}
+
+bool HEMixer::startChannelNew(
+ int channel, int globType, int globNum, uint32 soundData, uint32 offset,
+ int sampleLen, int frequency, int bitsPerSample, int sampleChannels,
+ const HESoundModifiers &modifiers, int callbackID, int32 flags, ...) {
+ va_list params;
+ bool retValue;
+
+ if (!_useMilesSoundSystem) {
+ if (bitsPerSample != 8) {
+ debug(5, "HEMixer::PX_StartChannel(): Glob(%d, %d) is %d bits per channel, must be 8 for software mixer", globType, globNum, bitsPerSample);
+ return false;
+ }
+
+ if (CHANNEL_CALLBACK_EARLY & flags) {
+ va_start(params, flags);
+
+ retValue = mixerStartChannel(
+ channel, globType, globNum, soundData + offset, sampleLen,
+ frequency, modifiers.volume, callbackID, flags, va_arg(params, int));
+
+ va_end(params);
+ return retValue;
+ } else {
+ return mixerStartChannel(
+ channel, globType, globNum, soundData + offset, sampleLen,
+ frequency, modifiers.volume, callbackID, flags);
+ }
+
+ } else {
+ flags &= ~CHANNEL_CALLBACK_EARLY;
+
+ return milesStartChannel(
+ channel, globType, globNum, soundData, offset, sampleLen,
+ bitsPerSample, sampleChannels, frequency, modifiers, callbackID, flags);
+ }
+
+ return true;
+}
+
+bool HEMixer::startChannel(int channel, int globType, int globNum, uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...) {
+ return false;
+}
+
+bool HEMixer::startSpoolingChannel(int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency, int volume, int callbackID, int32 flags, ...) {
+ return false;
+}
+
+bool HEMixer::isMilesActive() {
+ return _useMilesSoundSystem;
+}
+
+bool HEMixer::changeChannelVolume(int channel, int newVolume, int soft_flag) {
+ return false;
+}
+
+void HEMixer::milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers) {
+}
+
+int HEMixer::hsFindSoundQueue(int sound) {
+ return 0;
+}
+
+bool HEMixer::mixerStartChannel(int channel, int globType, int globNum, uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...) {
+ return false;
+}
+
+bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 sound_data, uint32 offset, int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
+ return false;
+}
+
+void HEMixer::milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags) {
+}
+
} // End of namespace Scumm
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 521419f7d4b..13b1883be72 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -19,8 +19,88 @@
*
*/
+#if !defined(SCUMM_HE_MIXER_HE_H)
+#define SCUMM_HE_MIXER_HE_H
+
+#include "scumm/he/sound_he.h"
#include "common/util.h"
+#include "common/file.h"
+#include "common/debug.h"
+
+namespace Audio {
+class AudioStream;
+class Mixer;
+class QueuingAudioStream;
+} // namespace Audio
namespace Scumm {
+#define CHANNEL_EMPTY_FLAGS 0x00000000
+#define CHANNEL_ACTIVE 0x00000001
+#define CHANNEL_FINISHED 0x00000002
+#define CHANNEL_LOOPING 0x00000004
+#define CHANNEL_LAST_CHUNK 0x00000008
+#define CHANNEL_SPOOLING 0x00000010
+#define CHANNEL_SPOOL_READ 0x00000020
+#define CHANNEL_SPOOL_CRITICAL 0x00000040
+#define CHANNEL_CALLBACK_EARLY 0x00000080
+#define CHANNEL_SOFT_REMIX 0x00000100
+
+struct HESoundModifiers;
+
+class HEMixer {
+protected:
+ Audio::Mixer *_mixer;
+ bool _useMilesSoundSystem;
+
+public:
+ HEMixer(Audio::Mixer *mixer, bool useMiles);
+ ~HEMixer();
+
+ void *getMilesSoundSystemObject();
+ bool initSoftMixerSubSystem();
+ void deinitSoftMixerSubSystem();
+ void endNeglectProcess();
+ void startLongNeglectProcess();
+ bool forceMusicBufferFill();
+ bool isMixerDisabled();
+ bool stopChannel(int channel);
+ void stopAllChannels();
+ bool changeChannelVolume(int channel, int volume, bool soft);
+ void softRemixAllChannels();
+ void premixUntilCritical();
+ bool pauseMixerSubSystem(bool paused);
+ void feedMixer();
+
+ bool startChannelNew(
+ int channel, int globType, int globNum, uint32 soundData, uint32 offset,
+ int sampleLen, int frequency, int bitsPerSample, int sampleChannels,
+ const HESoundModifiers &modifiers, int callbackId, int32 flags, ...);
+
+ bool startChannel(
+ int channel, int globType, int globNum, uint32 sampleDataOffset,
+ int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...);
+
+ bool startSpoolingChannel(
+ int channel, Common::File &spoolingFile, int sampleLen, int frequency,
+ int volume, int callbackID, int32 flags, ...);
+
+ bool isMilesActive();
+ bool changeChannelVolume(int channel, int newVolume, int soft_flag);
+ void milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers);
+ int hsFindSoundQueue(int sound);
+ bool mixerStartChannel(
+ int channel, int globType, int globNum, uint32 sampleDataOffset,
+ int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...);
+
+ bool milesStartChannel(
+ int channel, int globType, int globNum, uint32 sound_data, uint32 offset,
+ int sampleLen, int bitsPerSample, int sampleChannels,
+ int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...);
+
+ void milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags);
+};
+
} // End of namespace Scumm
+
+#endif
diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp
index 44648426c71..fba3235ca7e 100644
--- a/engines/scumm/he/resource_he.cpp
+++ b/engines/scumm/he/resource_he.cpp
@@ -358,9 +358,10 @@ byte *ScummEngine_v72he::getStringAddress(ResId idx) {
int ScummEngine_v72he::getSoundResourceSize(ResId id) {
const byte *ptr;
- int offs, size;
+ int offs;
+ int size = 0;
- if (id > _numSounds) {
+ if (id >= _numSounds) {
if (!((SoundHE *)_sound)->getHEMusicDetails(id, offs, size)) {
debug(0, "getSoundResourceSize: musicID %d not found", id);
return 0;
@@ -370,24 +371,31 @@ int ScummEngine_v72he::getSoundResourceSize(ResId id) {
if (!ptr)
return 0;
- if (READ_BE_UINT32(ptr) == MKTAG('R','I','F','F')) {
- byte flags;
- int rate;
+ if (_game.heversion < 95) {
+ if (_game.version >= 80) {
+ ptr = findResourceData(MKTAG('S', 'D', 'A', 'T'), ptr);
+ if (!ptr) {
+ return 0;
+ }
- size = READ_BE_UINT32(ptr + 4);
- Common::MemoryReadStream stream(ptr, size);
-
- if (!Audio::loadWAVFromStream(stream, size, rate, flags)) {
- error("getSoundResourceSize: Not a valid WAV file");
+ return READ_BE_UINT32(ptr + 4) - 8;
+ } else {
+ return READ_BE_UINT32(ptr + HSND_RES_OFFSET_LEN3) - 8;
}
} else {
- ptr += 8 + READ_BE_UINT32(ptr + 12);
- if (READ_BE_UINT32(ptr) == MKTAG('S','B','N','G')) {
- ptr += READ_BE_UINT32(ptr + 4);
+ if (READ_BE_UINT32(ptr) == MKTAG('W', 'S', 'O', 'U')) {
+ // Wrapped .wav file
+ byte *data = ((SoundHE *)_sound)->findWavBlock(MKTAG('d', 'a', 't', 'a'), ptr);
+ if (data)
+ return READ_LE_UINT32(data + 4);
+ } else {
+ ptr = findResourceData(MKTAG('S', 'D', 'A', 'T'), ptr);
+ if (!ptr) {
+ return 0;
+ }
+
+ return READ_BE_UINT32(ptr + 4) - 8;
}
-
- assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T'));
- size = READ_BE_UINT32(ptr + 4) - 8;
}
}
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 88ecd10ed2e..74d6901ccfc 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -751,10 +751,10 @@ void ScummEngine_v100he::o100_createSound() {
_heSndResId = pop();
break;
case SO_NEW:
- createSound(_heSndResId, -1);
+ ((SoundHE *)_sound)->createSound(_heSndResId, -1);
break;
case SO_SOUND_ADD:
- createSound(_heSndResId, pop());
+ ((SoundHE *)_sound)->createSound(_heSndResId, pop());
break;
case SO_END:
break;
diff --git a/engines/scumm/he/script_v71he.cpp b/engines/scumm/he/script_v71he.cpp
index c2aa250a375..a92d24eb0f1 100644
--- a/engines/scumm/he/script_v71he.cpp
+++ b/engines/scumm/he/script_v71he.cpp
@@ -70,7 +70,11 @@ byte *ScummEngine_v71he::heFindResource(uint32 tag, byte *searchin) {
return searchin;
}
- size = READ_BE_UINT32(searchin + 4);
+ if (READ_BE_UINT32(searchin) == MKTAG('R', 'I', 'F', 'F'))
+ size = READ_LE_UINT32(searchin + 4);
+ else
+ size = READ_BE_UINT32(searchin + 4);
+
if ((int32)size <= 0) {
error("(%s) Not found in %d... illegal block len %d", tag2str(tag), 0, size);
return NULL;
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index cde84776a9b..8bda0b7baac 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -68,10 +68,10 @@ void ScummEngine_v80he::o80_createSound() {
switch (subOp) {
case SO_ADD:
- createSound(_heSndResId, pop());
+ ((SoundHE *)_sound)->createSound(_heSndResId, pop());
break;
case SO_NEW:
- createSound(_heSndResId, -1);
+ ((SoundHE *)_sound)->createSound(_heSndResId, -1);
break;
case SO_SOUND_START:
_heSndResId = pop();
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 22e03d8cf4e..f2da8e91c17 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -25,6 +25,7 @@
#include "scumm/resource.h"
#include "scumm/scumm.h"
#include "scumm/he/sound_he.h"
+#include "scumm/he/mixer_he.h"
#include "scumm/he/intern_he.h"
#include "scumm/util.h"
@@ -50,19 +51,26 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
_vm((ScummEngine_v60he *)parent),
_overrideFreq(0),
_heSpoolingMusicTable(nullptr),
- _heMusicTracks(0),
+ _heSpoolingMusicCount(0),
_mutex(mutex) {
+ _createSndId = 0;
+ _createSndLastAppend = 0;
+ _createSndLastPos = 0;
+ _baseSndSize = 0;
+
memset(_heChannel, 0, sizeof(_heChannel));
_heSoundChannels = new Audio::SoundHandle[8]();
+ _useMilesSoundSystem = parent->_game.id == GID_MOONBASE;
+ _heMixer = new HEMixer(_mixer, _useMilesSoundSystem);
}
SoundHE::~SoundHE() {
free(_heSpoolingMusicTable);
delete[] _heSoundChannels;
- if (_spoolingMusicFile.isOpen())
- _spoolingMusicFile.close();
+ if (_heSpoolingMusicFile.isOpen())
+ _heSpoolingMusicFile.close();
}
void SoundHE::startSound(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
@@ -88,24 +96,13 @@ void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlag
}
void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) {
- int chan = findSoundChannel(sound);
- if (chan >= 0 && _heChannel[chan].sound) {
- // Modify the current playing sound
- if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)
- _mixer->setChannelVolume(_heSoundChannels[chan], volume);
-
- // Convert the pan range from (0, 127) to (-127, 127)
- int scaledPan = (pan != 64) ? 2 * pan - 127 : 0;
- if (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)
- _mixer->setChannelBalance(_heSoundChannels[chan], scaledPan);
-
- if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
- int newFrequency = (_heChannel[chan].frequency * frequencyShift) / HSND_SOUND_FREQ_BASE;
- if (newFrequency)
- _mixer->setChannelRate(_heSoundChannels[chan], newFrequency);
+ int channel = hsFindSoundChannel(sound);
+ if (channel >= 0 && _heChannel[channel].sound) {
+ // The implementation for this is only available for the Miles mixer
+ if (_useMilesSoundSystem) {
+ _heMixer->milesModifySound(channel, offset, HESoundModifiers(frequencyShift, pan, volume), flags);
}
}
- // TODO: Implement spooled sound case
}
void SoundHE::processSoundQueues() {
@@ -121,8 +118,13 @@ void SoundHE::processSoundQueues() {
hePan = _soundQueue[i].pan;
heVol = _soundQueue[i].vol;
- if (snd)
- triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
+ if (snd) {
+ if (_vm->_game.heversion < 99) {
+ triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers());
+ } else {
+ triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
+ }
+ }
}
_soundQueuePos = 0;
} else {
@@ -132,12 +134,10 @@ void SoundHE::processSoundQueues() {
heOffset = _soundQueue[_soundQueuePos].offset;
heChannel = _soundQueue[_soundQueuePos].channel;
heFlags = _soundQueue[_soundQueuePos].flags;
- heFreq = _soundQueue[_soundQueuePos].freq;
- hePan = _soundQueue[_soundQueuePos].pan;
- heVol = _soundQueue[_soundQueuePos].vol;
- if (snd)
- triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
+ if (snd) {
+ triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers());
+ }
}
}
@@ -189,9 +189,9 @@ void SoundHE::stopSound(int sound) {
_heChannel[i].sound = 0;
_heChannel[i].priority = 0;
_heChannel[i].frequency = 0;
- _heChannel[i].timer = 0;
+ _heChannel[i].timeout = 0;
_heChannel[i].hasSoundTokens = false;
- _heChannel[i].codeOffs = 0;
+ _heChannel[i].codeOffset = 0;
memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
}
}
@@ -209,12 +209,12 @@ void SoundHE::stopAllSounds() {
Sound::stopAllSounds();
}
-int SoundHE::findSoundChannel(int sound) {
+int SoundHE::hsFindSoundChannel(int sound) {
if (sound >= HSND_CHANNEL_0) {
int channel = sound - HSND_CHANNEL_0;
if (channel < 0 || channel > HSND_MAX_CHANNELS - 1) {
- error("SoundHE::findSoundChannel(): ERROR: Channel %d out of range (%d-%d)",
+ error("SoundHE::hsFindSoundChannel(): ERROR: Channel %d out of range (%d-%d)",
channel, 0, HSND_MAX_CHANNELS - 1);
}
@@ -249,9 +249,9 @@ void SoundHE::stopSoundChannel(int chan) {
_heChannel[chan].sound = 0;
_heChannel[chan].priority = 0;
_heChannel[chan].frequency = 0;
- _heChannel[chan].timer = 0;
+ _heChannel[chan].timeout = 0;
_heChannel[chan].hasSoundTokens = false;
- _heChannel[chan].codeOffs = 0;
+ _heChannel[chan].codeOffset = 0;
memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
@@ -375,56 +375,56 @@ void SoundHE::setupHEMusicFile() {
uint32 id, len;
Common::String musicFilename(_vm->generateFilename(-4));
- if (_spoolingMusicFile.open(musicFilename)) {
+ if (_heSpoolingMusicFile.open(musicFilename)) {
- id = _spoolingMusicFile.readUint32BE();
- len = _spoolingMusicFile.readUint32BE();
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
if (id == MKTAG('S', 'O', 'N', 'G')) {
// Older versions had a much simpler file structure
if (_vm->_game.heversion < 80) {
// Skip header wrapping file
- _spoolingMusicFile.seek(16, SEEK_SET);
- _heMusicTracks = _spoolingMusicFile.readUint32LE();
+ _heSpoolingMusicFile.seek(16, SEEK_SET);
+ _heSpoolingMusicCount = _heSpoolingMusicFile.readUint32LE();
} else {
// HE 80 and above
- id = _spoolingMusicFile.readUint32BE();
- len = _spoolingMusicFile.readUint32BE();
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
if (id == MKTAG('S', 'G', 'H', 'D')) {
- _heMusicTracks = _spoolingMusicFile.readUint32LE();
- _spoolingMusicFile.seek(len - 8 - 4, SEEK_CUR);
+ _heSpoolingMusicCount = _heSpoolingMusicFile.readUint32LE();
+ _heSpoolingMusicFile.seek(len - 8 - 4, SEEK_CUR);
} else {
- _spoolingMusicFile.close();
+ _heSpoolingMusicFile.close();
debug(5, "setupHEMusicFile(): Invalid spooling file '%s', couldn't find SGHD tag, found %s", musicFilename.c_str(), tag2str(id));
return;
}
}
- debug(5, "setupHEMusicFile(): music files count = %d", _heMusicTracks);
- _heSpoolingMusicTable = (HESpoolingMusicItem *)malloc(_heMusicTracks * sizeof(HESpoolingMusicItem));
+ debug(5, "setupHEMusicFile(): music files count = %d", _heSpoolingMusicCount);
+ _heSpoolingMusicTable = (HESpoolingMusicItem *)malloc(_heSpoolingMusicCount * sizeof(HESpoolingMusicItem));
if (_heSpoolingMusicTable != nullptr) {
- for (int i = 0; i < _heMusicTracks; i++) {
+ for (int i = 0; i < _heSpoolingMusicCount; i++) {
// For later versions we check that we are actually reading a SGEN section...
if (_vm->_game.heversion >= 80) {
- id = _spoolingMusicFile.readUint32BE();
- len = _spoolingMusicFile.readUint32BE();
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
if (id != MKTAG('S', 'G', 'E', 'N')) {
- _spoolingMusicFile.close();
+ _heSpoolingMusicFile.close();
debug(5, "setupHEMusicFile(): Invalid spooling file '%s', couldn't find SGEN tag, found %s", musicFilename.c_str(), tag2str(id));
return;
}
}
- _heSpoolingMusicTable[i].song = _spoolingMusicFile.readSint32LE();
- _heSpoolingMusicTable[i].offset = _spoolingMusicFile.readSint32LE();
- _heSpoolingMusicTable[i].size = _spoolingMusicFile.readSint32LE();
+ _heSpoolingMusicTable[i].song = _heSpoolingMusicFile.readSint32LE();
+ _heSpoolingMusicTable[i].offset = _heSpoolingMusicFile.readSint32LE();
+ _heSpoolingMusicTable[i].size = _heSpoolingMusicFile.readSint32LE();
int amountToRead = _vm->_game.heversion >= 80 ? 9 : 13;
int readAmount = 0;
for (readAmount = 0; readAmount < amountToRead; readAmount++) {
- _heSpoolingMusicTable[i].filename[readAmount] = _spoolingMusicFile.readByte();
+ _heSpoolingMusicTable[i].filename[readAmount] = _heSpoolingMusicFile.readByte();
// Early string termination
if (_heSpoolingMusicTable[i].filename[readAmount] == '\0')
@@ -453,17 +453,15 @@ void SoundHE::setupHEMusicFile() {
}
bool SoundHE::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
- int i;
-
- for (i = 0; i < _heMusicTracks; i++) {
+ for (int i = 0; i < _heSpoolingMusicCount; i++) {
if (_heSpoolingMusicTable[i].song == id) {
musicOffs = _heSpoolingMusicTable[i].offset;
musicSize = _heSpoolingMusicTable[i].size;
- return 1;
+ return true;
}
}
- return 0;
+ return false;
}
void SoundHE::handleSoundFrame() {
@@ -507,10 +505,10 @@ void SoundHE::unqueueSoundCallbackScripts() {
void SoundHE::checkSoundTimeouts() {
for (int chan = 0; chan < ARRAYSIZE(_heChannel); chan++) {
- if (_heChannel[chan].sound == 0 || _heChannel[chan].timer == 0)
+ if (_heChannel[chan].sound == 0 || _heChannel[chan].timeout == 0)
continue;
- if (_vm->getHETimer(chan + HSND_TIMER_SLOT) > _heChannel[chan].timer) {
+ if (_vm->getHETimer(chan + HSND_TIMER_SLOT) > _heChannel[chan].timeout) {
digitalSoundCallback(HSND_SOUND_TIMEOUT, chan);
}
}
@@ -606,7 +604,7 @@ void SoundHE::runSoundCode() {
continue;
}
- if (_heChannel[chan].codeOffs == -1) {
+ if (_heChannel[chan].codeOffset == -1) {
continue;
}
@@ -624,7 +622,7 @@ void SoundHE::runSoundCode() {
assert(soundPtr);
- soundPtr += _heChannel[chan].codeOffs;
+ soundPtr += _heChannel[chan].codeOffset;
len = READ_LE_UINT16(soundPtr);
freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
@@ -634,7 +632,7 @@ void SoundHE::runSoundCode() {
processSoundOpcodes(_heChannel[chan].sound, soundPtr + sizeof(uint16) + sizeof(uint32), _heChannel[chan].soundVars);
- _heChannel[chan].codeOffs += len;
+ _heChannel[chan].codeOffset += len;
soundPtr += len;
@@ -642,7 +640,7 @@ void SoundHE::runSoundCode() {
freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
if (len == 0) {
- _heChannel[chan].codeOffs = -1;
+ _heChannel[chan].codeOffset = -1;
break;
}
}
@@ -781,8 +779,18 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
if ((READ_BE_UINT32(soundAddr) == MKTAG('D', 'I', 'G', 'I')) || (READ_BE_UINT32(soundAddr) == MKTAG('T', 'A', 'L', 'K'))) {
triggerDigitalSound(soundId, heOffset, heChannel, heFlags);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('M', 'I', 'D', 'I')) {
- // Nothing happens in the original as well
- return;
+ if (_vm->_imuse) {
+ // This is used in the DOS version of Fatty Bear's
+ // Birthday Surprise to change the note on the piano
+ // when not using a digitized instrument.
+ _vm->_imuse->stopSound(_currentMusic);
+ _currentMusic = soundId;
+ _vm->_imuse->startSoundWithNoteOffset(soundId, heOffset);
+ } else if (_vm->_musicEngine) {
+ _vm->_musicEngine->stopSound(_currentMusic);
+ _currentMusic = soundId;
+ _vm->_musicEngine->startSoundWithTrackID(soundId, heOffset);
+ }
} else if (READ_BE_UINT32(soundAddr) == MKTAG('W', 'S', 'O', 'U')) {
triggerRIFFSound(soundId, heOffset, heChannel, heFlags, modifiers);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('X', 'S', 'O', 'U')) {
@@ -792,8 +800,209 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
}
}
-void SoundHE::triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
+void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags, HESoundModifiers modifiers) {
+ if (_heSpoolingMusicCount != 0) {
+ for (int i = 0; i < _heSpoolingMusicCount; i++) {
+ if (_heSpoolingMusicTable[i].song == song) {
+ Common::String filename(_vm->generateFilename(-4));
+ int fileOffset = 0;
+ int songsize = 0;
+ int id, len;
+
+ hsStopDigitalSound(_heChannel[channel].sound);
+
+ if (_heSpoolingMusicTable[i].offset != 0) {
+ fileOffset = _heSpoolingMusicTable[i].offset;
+
+ if (_vm->_game.heversion < 80)
+ fileOffset += HSND_RES_OFFSET_SOUND_DATA;
+
+ _heSpoolingMusicFile.seek(fileOffset, SEEK_SET);
+ songsize = _heSpoolingMusicTable[i].size;
+
+ if (_vm->_game.heversion < 80)
+ songsize -= HSND_RES_OFFSET_SOUND_DATA;
+ } else {
+ _heSpoolingMusicFile.close();
+
+ filename = _heSpoolingMusicTable[i].filename;
+
+ if (!_heSpoolingMusicFile.open(filename)) {
+ debug("SoundHE::triggerSpoolingSound(): Can't open music file '%s'", filename.c_str());
+ if (_vm->_game.heversion < 95) {
+ _vm->VAR(_vm->VAR_ERROR_FLAG) = -1;
+ } else {
+ _vm->VAR(_vm->VAR_OPERATION_FAILURE) = -1;
+ }
+
+ return;
+ } else if (_vm->_game.heversion < 80) {
+ _heSpoolingMusicFile.seek(HSND_RES_OFFSET_SOUND_DATA, SEEK_SET);
+ songsize = _heSpoolingMusicFile.size() - HSND_RES_OFFSET_SOUND_DATA;
+ }
+ }
+
+ // Old spooled music format (raw audio)
+ if (_vm->_game.heversion < 80) {
+ _heMixer->startSpoolingChannel(
+ channel, _heSpoolingMusicFile, songsize,
+ HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel,
+ CHANNEL_ACTIVE);
+
+ _heChannel[channel].sound = song;
+ _heChannel[channel].priority = 255;
+
+ _vm->VAR(_vm->VAR_ERROR_FLAG) = 0;
+ return;
+ }
+
+ _heChannel[channel].codeOffset = -1;
+ _heChannel[channel].codeBuffer = nullptr;
+ _heChannel[channel].hasSoundTokens = false;
+
+ id = _heSpoolingMusicFile.readUint32BE();
+
+ if (id == MKTAG('R', 'I', 'F', 'F')) {
+ len = _heSpoolingMusicFile.readUint32LE();
+ id = _heSpoolingMusicFile.readUint32BE();
+
+ if (id != MKTAG('W', 'A', 'V', 'E'))
+ error("SoundHE::triggerSpoolingSound(): Illegal music .wav file %d", song);
+
+ while (true) {
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32LE();
+
+ if (id == MKTAG('d', 'a', 't', 'a')) {
+ id = MKTAG('S', 'D', 'A', 'T'); // Convert it to a SDAT tag
+ len += 8; // Compensate the length of the header
+
+ break;
+ } else if (id == MKTAG('S', 'B', 'N', 'G')) {
+ if (len > ARRAYSIZE(_heSpoolingCodeBuffer)) {
+ error("SoundHE::triggerSpoolingSound(): Spooling sound %d code too large", song);
+ }
+
+ _heSpoolingMusicFile.read(_heSpoolingCodeBuffer, len);
+
+ _heChannel[channel].codeOffset = 0;
+ _heChannel[channel].codeBuffer = _heSpoolingCodeBuffer;
+ _heChannel[channel].hasSoundTokens = true;
+ } else if (id == MKTAG('f', 'm', 't', ' ')) {
+ PCMWaveFormat pcm;
+
+ if (len < sizeof(pcm))
+ error("SoundHE::triggerSpoolingSound(): Illegal .wav format length in song %d", song);
+
+ pcm.wFormatTag = _heSpoolingMusicFile.readUint16LE();
+ pcm.wChannels = _heSpoolingMusicFile.readUint16LE();
+ pcm.dwSamplesPerSec = _heSpoolingMusicFile.readUint32LE();
+ pcm.dwAvgBytesPerSec = _heSpoolingMusicFile.readUint32LE();
+ pcm.wBlockAlign = _heSpoolingMusicFile.readUint16LE();
+ pcm.wBitsPerSample = _heSpoolingMusicFile.readUint16LE();
+
+ if (_useMilesSoundSystem) {
+ if (pcm.wFormatTag != WAVE_FORMAT_PCM && pcm.wFormatTag != WAVE_FORMAT_IMA_ADPCM) {
+ error("SoundHE::triggerSpoolingSound(): Illegal .wav format for Miles mixer, song %d - %d",
+ song, pcm.wFormatTag);
+ }
+ } else {
+ if (pcm.wFormatTag != WAVE_FORMAT_PCM ||
+ pcm.wChannels != 1 ||
+ pcm.dwSamplesPerSec != HSND_DEFAULT_FREQUENCY ||
+ pcm.wBitsPerSample != 8) {
+ error("SoundHE::triggerSpoolingSound(): Illegal .wav format for software mixer, song %d - %d, %d, %d, %d",
+ song, pcm.wFormatTag, pcm.wChannels, pcm.dwSamplesPerSec, pcm.wBitsPerSample);
+ }
+ }
+
+ // Skip over the rest of this block.
+ _heSpoolingMusicFile.seek(len - sizeof(pcm), SEEK_CUR);
+ } else {
+ _heSpoolingMusicFile.seek(len, SEEK_CUR);
+ }
+ }
+ } else if (id == MKTAG('D', 'I', 'G', 'I')) {
+ len = _heSpoolingMusicFile.readUint32BE();
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
+
+ if (id == MKTAG('H', 'S', 'H', 'D')) {
+ _heSpoolingMusicFile.seek(len - 8, SEEK_CUR);
+ } else {
+ error("SoundHE::triggerSpoolingSound(): Illegal spooling sound %d, id %s", song, tag2str(id));
+ }
+
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
+
+ if (id == MKTAG('S', 'B', 'N', 'G')) {
+ if (len > sizeof(_heSpoolingCodeBuffer)) {
+ error("SoundHE::triggerSpoolingSound(): Spooling sound %d code too large", song);
+ }
+
+ _heSpoolingMusicFile.read(_heSpoolingCodeBuffer, len - 8);
+ _heChannel[channel].codeOffset = 0;
+ _heChannel[channel].codeBuffer = _heSpoolingCodeBuffer;
+ _heChannel[channel].hasSoundTokens = true;
+
+ id = _heSpoolingMusicFile.readUint32BE();
+ len = _heSpoolingMusicFile.readUint32BE();
+ }
+ } else {
+ error("SoundHE::triggerSpoolingSound(): Illegal spooling sound %d, id %s", song, tag2str(id));
+ }
+
+ if (id == MKTAG('S', 'D', 'A', 'T')) {
+ songsize = len - 8;
+ } else {
+ error("SoundHE::triggerSpoolingSound(): Illegal spooling sound %d, id %s", song, tag2str(id));
+ }
+
+ if (_useMilesSoundSystem) {
+ if (offset)
+ debug("SoundHE::triggerSpoolingSound(): Starting offsets into music files not supported with Miles currently");
+
+ _heMixer->milesStartSpoolingChannel(channel, filename.c_str(), fileOffset, flags, modifiers);
+ } else {
+ // Start the music track at a specified offset
+ if (offset) {
+ _heSpoolingMusicFile.seek(offset, SEEK_CUR);
+
+ if (offset >= songsize || offset < 0) {
+ error("SoundHE::triggerSpoolingSound(): Invalid offset %d for sound %d (song size %d)", offset, song, songsize);
+ }
+
+ songsize -= offset;
+ }
+
+ _heMixer->startSpoolingChannel(channel, _heSpoolingMusicFile, songsize,
+ HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel, CHANNEL_ACTIVE);
+ }
+ _vm->setHETimer(channel + HSND_TIMER_SLOT);
+
+ _heChannel[channel].sound = song;
+ _heChannel[channel].priority = 255;
+ _heChannel[channel].frequency = HSND_DEFAULT_FREQUENCY;
+
+ if (_vm->_game.heversion < 95) {
+ _vm->VAR(_vm->VAR_ERROR_FLAG) = 0;
+ } else {
+ _vm->VAR(_vm->VAR_OPERATION_FAILURE) = 0;
+ }
+
+ return;
+ }
+ }
+ }
+
+ // Error situation
+ if (_vm->_game.heversion < 95) {
+ _vm->VAR(_vm->VAR_ERROR_FLAG) = -1;
+ } else {
+ _vm->VAR(_vm->VAR_OPERATION_FAILURE) = -1;
+ }
}
void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
@@ -804,6 +1013,15 @@ void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heF
int soundCodeOffset = -1;
bool parsedFmt = false;
+ // For uninit var warning...
+ pFmt.wFormatTag = 0;
+ pFmt.wChannels = 0;
+ pFmt.dwSamplesPerSec = 0;
+ pFmt.dwAvgBytesPerSec = 0;
+ pFmt.wBlockAlign = 0;
+ pFmt.wBitsPerSample = 0;
+
+
// Let's begin by fetching the sound address...
uint8 *wsouPtr = (byte *)_vm->getResourceAddress(rtSound, soundId);
@@ -874,14 +1092,15 @@ void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heF
}
parsedFmt = true;
+
break;
}
case MKTAG('d', 'a', 't', 'a'):
assert(parsedFmt);
soundDataPtr = wavePtr;
sampleCount = (chunkLength * 8) / (pFmt.wChannels * pFmt.wBitsPerSample);
- break;
+ break;
case MKTAG('X', 'S', 'H', '2'):
{
// Check for the optional sound flag block
@@ -891,11 +1110,9 @@ void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heF
break;
}
-
case MKTAG('S', 'B', 'N', 'G'):
soundCodeOffset = wavePtr - wsouPtr;
break;
-
default:
break;
}
@@ -986,278 +1203,162 @@ void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heF
heFlags, bitsPerSample, sampleChannelCount, HESoundModifiers());
}
-void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int sound_data,
+void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundData,
int globType, int globNum, int sampleCount, int frequency, int channel, int priority,
int soundCode, int flags, int bitsPerSample, int soundChannelCount, HESoundModifiers modifiers) {
+ int earlyCallbackByteCount = 0;
+ int index;
+ uint32 hflags;
- return;
-}
-
-void SoundHE::hsStopDigitalSound(int sound) {
- return;
-}
-
-void SoundHE::triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags) {
- Audio::RewindableAudioStream *stream = nullptr;
- byte *ptr, *spoolPtr;
- int size = -1;
- int priority, rate;
- byte flags = Audio::FLAG_UNSIGNED;
-
- Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType;
- if (soundId > _vm->_numSounds)
- type = Audio::Mixer::kMusicSoundType;
- else if (soundId == 1)
- type = Audio::Mixer::kSpeechSoundType;
-
-
- if (heChannel == -1)
- heChannel = (_vm->VAR_START_DYN_SOUND_CHANNELS != 0xFF) ? getNextDynamicChannel() : 1;
+ hflags = (flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP) ? CHANNEL_LOOPING : 0;
+ hflags |= (flags & ScummEngine_v70he::HESndFlags::HE_SND_SOFT_SOUND) ? CHANNEL_SOFT_REMIX : 0;
- debug(5,"triggerDigitalSound: soundId %d heOffset %d heChannel %d heFlags %d", soundId, heOffset, heChannel, heFlags);
-
- if (soundId >= 10000) {
- // Special codes, used in pjgames
- return;
- }
-
- if (soundId > _vm->_numSounds) {
- int music_offs;
- Common::File musicFile;
- Common::String buf(_vm->generateFilename(-4));
+ // If there's an early channel callback value, fetch it...
+ if (_vm->_game.heversion >= 95) {
+ index = _vm->VAR_EARLY_CHAN_1_CALLBACK + channel;
- if (musicFile.open(buf) == false) {
- warning("triggerDigitalSound: Can't open music file %s", buf.c_str());
- return;
- }
- if (!getHEMusicDetails(soundId, music_offs, size)) {
- debug(0, "triggerDigitalSound: musicID %d not found", soundId);
- return;
+ if ((_vm->VAR_EARLY_CHAN_1_CALLBACK <= index) && (_vm->VAR_EARLY_CHAN_3_CALLBACK >= index)) {
+ earlyCallbackByteCount = _vm->VAR(index);
+ } else {
+ earlyCallbackByteCount = 0;
}
+ }
- musicFile.seek(music_offs, SEEK_SET);
-
- _mixer->stopHandle(_heSoundChannels[heChannel]);
- spoolPtr = _vm->_res->createResource(rtSpoolBuffer, heChannel, size);
- assert(spoolPtr);
- musicFile.read(spoolPtr, size);
- musicFile.close();
-
- if (_vm->_game.heversion == 70) {
- // Try to load high quality audio file if found
- stream = tryLoadAudioOverride(soundId);
-
- if (!stream) {
- stream = Audio::makeRawStream(spoolPtr, size, 11025, flags, DisposeAfterUse::NO);
+ if (_vm->_game.heversion >= 95) {
+ _heMixer->startChannelNew(
+ channel, globType, globNum, soundData, offset,
+ (sampleCount - offset), frequency, bitsPerSample, soundChannelCount,
+ modifiers, channel, CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
+ earlyCallbackByteCount);
+ } else if (_vm->_game.heversion >= 80) {
+ _heMixer->startChannel(
+ channel, globType, globNum, soundData + offset,
+ (sampleCount - offset), HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel,
+ CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
+ _vm->VAR(_vm->VAR_EARLY_CHAN_1_CALLBACK + channel));
+ } else {
+ if (channel != _vm->VAR(_vm->VAR_TALK_CHANNEL)) {
+ _heMixer->startChannel(
+ channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
+ sampleCount - offset, frequency, HSND_MAX_VOLUME, channel, CHANNEL_ACTIVE | hflags);
+ } else {
+ if (_vm->_game.heversion >= 72) {
+ earlyCallbackByteCount = ((_vm->VAR(_vm->VAR_TIMER_NEXT) * frequency) / 60);
+ earlyCallbackByteCount *= _vm->VAR(_vm->VAR_EARLY_TALKIE_CALLBACK);
+
+ _heMixer->startChannel(
+ channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
+ sampleCount - offset, frequency, HSND_MAX_VOLUME, channel,
+ CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
+ earlyCallbackByteCount);
+ } else {
+ _heMixer->startChannel(
+ channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
+ sampleCount - offset, frequency, HSND_MAX_VOLUME, channel,
+ CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags);
}
-
- _mixer->playStream(type, &_heSoundChannels[heChannel], stream, soundId);
- return;
}
- }
- if (soundId > _vm->_numSounds) {
- ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel);
- } else {
- ptr = _vm->getResourceAddress(rtSound, soundId);
- }
+ _heChannel[channel].sound = sound;
+ _heChannel[channel].priority = priority;
- if (!ptr) {
return;
}
- // Support for sound in later HE games
- if (READ_BE_UINT32(ptr) == MKTAG('R','I','F','F') || READ_BE_UINT32(ptr) == MKTAG('W','S','O','U')) {
- uint16 compType;
- int blockAlign;
- int samplesPerBlock;
- int codeOffs = -1;
+ _vm->setHETimer(channel + HSND_TIMER_SLOT);
- priority = (soundId > _vm->_numSounds) ? 255 : *(ptr + 18);
-
- byte *sbngPtr = findSoundTag(MKTAG('S','B','N','G'), ptr);
- if (sbngPtr != nullptr) {
- codeOffs = sbngPtr - ptr + 8;
- }
+ _heChannel[channel].sound = sound;
+ _heChannel[channel].priority = priority;
+ _heChannel[channel].codeOffset = soundCode;
+ _heChannel[channel].hasSoundTokens = (soundCode != -1);
+ _heChannel[channel].frequency = frequency;
- if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
- int curSnd = _heChannel[heChannel].sound;
- if (curSnd == 1 && soundId != 1)
- return;
- if (curSnd != 0 && curSnd != 1 && soundId != 1 && _heChannel[heChannel].priority > priority)
- return;
- }
-
- if (READ_BE_UINT32(ptr) == MKTAG('W','S','O','U'))
- ptr += 8;
-
- size = READ_LE_UINT32(ptr + 4);
- Common::MemoryReadStream memStream(ptr, size);
-
- if (!Audio::loadWAVFromStream(memStream, size, rate, flags, &compType, &blockAlign, &samplesPerBlock)) {
- error("triggerDigitalSound: Not a valid WAV file (%d)", soundId);
- }
-
- assert(heOffset >= 0 && heOffset < size);
-
- // FIXME: Disabled sound offsets, due to asserts been triggered
- heOffset = 0;
-
- _vm->setHETimer(heChannel + 4);
- _heChannel[heChannel].sound = soundId;
- _heChannel[heChannel].priority = priority;
- _heChannel[heChannel].frequency = rate;
- _heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
- _heChannel[heChannel].codeOffs = codeOffs;
- memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
-
- // TODO: Extra sound flags
- if (heFlags & 1) {
- _heChannel[heChannel].timer = 0;
- } else {
- _heChannel[heChannel].timer = size * 1000 / (rate * blockAlign);
- }
-
- _mixer->stopHandle(_heSoundChannels[heChannel]);
- if (compType == 17) {
- int nChan = (flags & Audio::FLAG_STEREO) ? 2 : 1;
- Audio::AudioStream *voxStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, size, Audio::kADPCMMSIma, rate, nChan, blockAlign);
-
- // FIXME: Get rid of this crude hack to turn a ADPCM stream into a raw stream.
- // It seems it is only there to allow looping -- if that is true, we certainly
- // can do without it, using a LoopingAudioStream.
-
- if (_heChannel[heChannel].timer)
- _heChannel[heChannel].timer = (int)(((int64)size * samplesPerBlock * 1000) / ((int64)rate * blockAlign * nChan));
-
- byte *sound = (byte *)malloc(size * 4);
- /* On systems where it matters, malloc will return
- * even addresses, so the use of (void *) in the
- * following cast shuts the compiler from warning
- * unnecessarily. */
- size = voxStream->readBuffer((int16 *)(void *)sound, size * 2);
- size *= 2; // 16bits.
- delete voxStream;
-
- // makeADPCMStream returns a stream in native endianness, but RawMemoryStream
- // defaults to big endian. If we're on a little endian system, set the LE flag.
-#ifdef SCUMM_LITTLE_ENDIAN
- flags |= Audio::FLAG_LITTLE_ENDIAN;
-#endif
- stream = Audio::makeRawStream(sound + heOffset, size - heOffset, rate, flags);
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP) {
+ _heChannel[channel].timeout = 0;
+ } else {
+ if (_vm->_game.heversion >= 95) {
+ uint64 timeOut;
+ timeOut = (uint64)(sampleCount - offset) * (uint64)1000;
+ _heChannel[channel].timeout = (int)(timeOut / frequency) + 2000;
} else {
- stream = Audio::makeRawStream(ptr + memStream.pos() + heOffset, size - heOffset, rate, flags, DisposeAfterUse::NO);
+ uint32 timeOut;
+ timeOut = (uint32)(sampleCount - offset) * (uint32)1000;
+ _heChannel[channel].timeout = (int)(timeOut / frequency) + 2000;
}
- _mixer->playStream(type, &_heSoundChannels[heChannel],
- Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundId);
}
- // Support for sound in Humongous Entertainment games
- else if (READ_BE_UINT32(ptr) == MKTAG('D','I','G','I') || READ_BE_UINT32(ptr) == MKTAG('T','A','L','K')) {
- byte *sndPtr = ptr;
- int codeOffs = -1;
- priority = (soundId > _vm->_numSounds) ? 255 : *(ptr + 18);
- rate = READ_LE_UINT16(ptr + 22);
+ for (int i = 0; i < HSND_MAX_SOUND_VARS; i++) {
+ _heChannel[channel].soundVars[i] = 0;
+ }
+}
- // Skip DIGI/TALK (8) and HSHD (24) blocks
- ptr += 32;
+void SoundHE::hsStopDigitalSound(int sound) {
+ int channel;
+ if ((channel = hsFindSoundChannel(sound)) != -1) {
+ _heMixer->stopChannel(channel);
+ _heChannel[channel].sound = 0;
+ memset(&_heChannel[channel], 0, sizeof(_heChannel[channel]));
+ }
+}
- if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) {
- int curSnd = _heChannel[heChannel].sound;
- if (curSnd == 1 && soundId != 1)
- return;
- if (curSnd != 0 && curSnd != 1 && soundId != 1 && _heChannel[heChannel].priority > priority)
- return;
- }
+void SoundHE::triggerDigitalSound(int sound, int offset, int channel, int flags) {
+ byte *soundAddr;
+ int soundCode, soundData, bitsPerSample, sampleChannels;
+ byte *soundResPtr;
+ uint32 soundLength, soundFrequency;
+ int soundPriority;
- if (READ_BE_UINT32(ptr) == MKTAG('S','B','N','G')) {
- codeOffs = ptr - sndPtr + 8;
- ptr += READ_BE_UINT32(ptr + 4);
- }
+ debug(5, "SoundHE::triggerDigitalSound(sound=%d, offset=%d, channel=%d, flags=%08x)", sound, offset, channel, flags);
- assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T'));
- size = READ_BE_UINT32(ptr + 4) - 8;
- if (heOffset < 0 || heOffset > size) {
- // Occurs when making fireworks in puttmoon
- heOffset = 0;
- }
- size -= heOffset;
+ // Don't let digital sounds interrupt speech...
+ if (_heChannel[channel].sound == HSND_TALKIE_SLOT && sound != HSND_TALKIE_SLOT) {
+ return;
+ }
- if (_overrideFreq) {
- // Used by the piano in Fatty Bear's Birthday Surprise
- rate = _overrideFreq;
- _overrideFreq = 0;
- }
+ soundAddr = (byte *)_vm->getResourceAddress(rtSound, sound);
+ soundPriority = soundAddr[HSND_RES_OFFSET_KILL_PRIO];
- // Try to load high quality audio file if found
- int newDuration;
- stream = tryLoadAudioOverride(soundId, &newDuration);
- if (stream != nullptr && soundId == 1) {
- // Disable lip sync if the speech audio was overriden
- codeOffs = -1;
- }
-
- _vm->setHETimer(heChannel + 4);
- _heChannel[heChannel].sound = soundId;
- _heChannel[heChannel].priority = priority;
- _heChannel[heChannel].frequency = rate;
- _heChannel[heChannel].hasSoundTokens = (codeOffs != -1);
- _heChannel[heChannel].codeOffs = codeOffs;
- memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars));
-
- // TODO: Extra sound flags
- if (heFlags & 1) {
- _heChannel[heChannel].timer = 0;
- } else {
- if (stream != nullptr) {
- _heChannel[heChannel].timer = newDuration;
- } else {
- _heChannel[heChannel].timer = size * 1000 / rate;
- }
- }
+ if (_vm->_game.heversion < 95 && _overrideFreq) {
+ soundFrequency = _overrideFreq;
+ _overrideFreq = 0;
+ } else {
+ soundFrequency = READ_LE_UINT16(&soundAddr[HSND_RES_OFFSET_FREQUENCY]);
+ }
- _mixer->stopHandle(_heSoundChannels[heChannel]);
+ bitsPerSample = 8;
+ sampleChannels = 1;
- if (!stream) {
- stream = Audio::makeRawStream(ptr + heOffset + 8, size, rate, flags, DisposeAfterUse::NO);
+ // Check sound priority
+ if (_heChannel[channel].sound && (_heChannel[channel].sound != HSND_TALKIE_SLOT) && (sound != HSND_TALKIE_SLOT)) {
+ if (soundPriority < _heChannel[channel].priority) {
+ return; // Don't start new sound, prio too low
}
- _mixer->playStream(type, &_heSoundChannels[heChannel],
- Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundId);
}
- // Support for PCM music in 3DO versions of Humongous Entertainment games
- else if (READ_BE_UINT32(ptr) == MKTAG('M','R','A','W')) {
- priority = *(ptr + 18);
- rate = READ_LE_UINT16(ptr + 22);
- // Skip DIGI (8) and HSHD (24) blocks
- ptr += 32;
-
- assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T'));
- size = READ_BE_UINT32(ptr + 4) - 8;
+ soundResPtr = (byte *)_vm->findResource(MKTAG('S', 'B', 'N', 'G'), soundAddr);
+ if (soundResPtr == nullptr) {
+ soundCode = -1;
+ } else {
+ soundCode = soundResPtr - soundAddr + 8;
+ }
- byte *sound = (byte *)malloc(size);
- memcpy(sound, ptr + 8, size);
+ soundResPtr = (byte *)_vm->findResource(MKTAG('S', 'D', 'A', 'T'), soundAddr);
+ if (soundResPtr == nullptr)
+ error("SoundHE::triggerDigitalSound(): Can't find SDAT section in sound %d", sound);
- _mixer->stopID(_currentMusic);
- _currentMusic = soundId;
+ soundData = soundResPtr - soundAddr + 8;
+ soundLength = READ_BE_UINT32(soundResPtr + 4) - 8;
- stream = Audio::makeRawStream(sound, size, rate, 0);
- _mixer->playStream(Audio::Mixer::kMusicSoundType, nullptr, stream, soundId);
- }
- else if (READ_BE_UINT32(ptr) == MKTAG('M','I','D','I')) {
- if (_vm->_imuse) {
- // This is used in the DOS version of Fatty Bear's
- // Birthday Surprise to change the note on the piano
- // when not using a digitized instrument.
- _vm->_imuse->stopSound(_currentMusic);
- _currentMusic = soundId;
- _vm->_imuse->startSoundWithNoteOffset(soundId, heOffset);
- } else if (_vm->_musicEngine) {
- _vm->_musicEngine->stopSound(_currentMusic);
- _currentMusic = soundId;
- _vm->_musicEngine->startSoundWithTrackID(soundId, heOffset);
- }
+ // Check for a valid offset...
+ if ((uint32)offset >= soundLength) {
+ debug(5, "SoundHE::triggerDigitalSound(): WARNING: Sound %d started past end offset %d size %d", sound, offset, soundLength);
+ offset = 0;
}
+
+ hsStartDigitalSound(
+ sound, offset, soundAddr, soundData, rtSound, sound,
+ soundLength, soundFrequency, channel, soundPriority, soundCode, flags,
+ bitsPerSample, sampleChannels, HESoundModifiers());
}
Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *duration) {
@@ -1379,151 +1480,185 @@ void SoundHE::startHETalkSound(uint32 offset) {
file.read(ptr, size);
int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
- addSoundToQueue(1, 0, channel, 0);
+ addSoundToQueue(HSND_TALKIE_SLOT, 0, channel, 0, HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
}
#ifdef ENABLE_HE
-void ScummEngine_v80he::createSound(int baseSound, int sound) {
- byte *baseSoundPtr, *soundPtr;
- byte *sbng1Ptr, *sbng2Ptr;
- byte *sdat1Ptr, *sdat2Ptr;
- byte *src, *dst;
- int len, offs, size;
- int sdat1size, sdat2size;
-
- sbng1Ptr = NULL;
- sbng2Ptr = NULL;
+void SoundHE::createSound(int baseSound, int sound) {
+ byte *baseSndPtr, *sndPtr, *baseSndSbngPtr, *sndSbngPtr, *dep, *sep;
+ int baseSndLeft, sndSize, channel;
if (sound == -1) {
- _sndPtrOffs = 0;
- _sndTmrOffs = 0;
- _sndDataSize = 0;
+ _createSndLastAppend = 0;
+ _createSndLastPos = 0;
+ _baseSndSize = 0;
return;
}
- if (baseSound != _curSndId) {
- _curSndId = baseSound;
- _sndPtrOffs = 0;
- _sndTmrOffs = 0;
- _sndDataSize = 0;
+ if (baseSound != _createSndId) {
+ _createSndId = baseSound;
+ _createSndLastAppend = 0;
+ _createSndLastPos = 0;
+ _baseSndSize = 0;
}
- baseSoundPtr = getResourceAddress(rtSound, baseSound);
- assert(baseSoundPtr);
- soundPtr = getResourceAddress(rtSound, sound);
- assert(soundPtr);
+ _vm->ensureResourceLoaded(rtSound, baseSound);
+ _vm->ensureResourceLoaded(rtSound, sound);
+
+ baseSndPtr = (byte *)_vm->getResourceAddress(rtSound, baseSound);
+ sndPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
- int chan = ((SoundHE *)_sound)->findSoundChannel(baseSound);
+ channel = hsFindSoundChannel(baseSound);
- if (!findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr)) {
- sbng1Ptr = heFindResource(MKTAG('S','B','N','G'), baseSoundPtr);
- sbng2Ptr = heFindResource(MKTAG('S','B','N','G'), soundPtr);
+ //Check for a RIFF block here and set a flag.
+
+ bool fIsWav = false;
+
+ if (findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr)) {
+ fIsWav = true;
}
- if (sbng1Ptr != NULL && sbng2Ptr != NULL) {
- if (chan != -1 && ((SoundHE *)_sound)->_heChannel[chan].codeOffs > 0) {
- // Copy any code left over to the beginning of the code block
- int curOffs = ((SoundHE *)_sound)->_heChannel[chan].codeOffs;
+ if (!fIsWav) {
+ // For non-WAV files we have to deals with sound variables (i.e. skip them :-) )
+ baseSndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
- src = baseSoundPtr + curOffs;
- dst = sbng1Ptr + 8;
- size = READ_BE_UINT32(sbng1Ptr + 4);
- len = sbng1Ptr - baseSoundPtr + size - curOffs;
+ if (baseSndSbngPtr != nullptr) {
+ sndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
- memmove(dst, src, len);
+ if (sndSbngPtr != nullptr) {
+ if (channel != -1 && (_heChannel[channel].codeOffset > 0)) {
+ memcpy(baseSndSbngPtr + 8,
+ baseSndPtr + _heChannel[channel].codeOffset,
+ (READ_BE_UINT32(baseSndSbngPtr + 4) - 8) - (_heChannel[channel].codeOffset - (baseSndSbngPtr - baseSndPtr + 8)));
- // Now seek to the end of this code block
- dst = sbng1Ptr + 8;
- while ((size = READ_LE_UINT16(dst)) != 0)
- dst += size;
- } else {
- // We're going to overwrite the code block completely
- dst = sbng1Ptr + 8;
- }
+ _heChannel[channel].codeOffset = baseSndSbngPtr + 8 - baseSndPtr;
+ dep = baseSndSbngPtr + 8;
- // Reset the current code offset to the beginning of the code block
- ((SoundHE *)_sound)->_heChannel[chan].codeOffs = sbng1Ptr - baseSoundPtr + 8;
+ while (READ_LE_UINT16(dep) != 0)
+ dep += READ_LE_UINT16(dep);
+ } else {
+ _heChannel[channel].codeOffset = baseSndSbngPtr + 8 - baseSndPtr;
+ dep = baseSndSbngPtr + 8;
+ }
- // Seek to the end of the code block for sound 2
- byte *tmp = sbng2Ptr + 8;
- while ((offs = READ_LE_UINT16(tmp)) != 0) {
- tmp += offs;
- }
+ sep = sndSbngPtr + 8;
+ while (READ_LE_UINT16(sep) != 0)
+ sep += READ_LE_UINT16(sep);
- // Copy the code block for sound 2 to the code block for sound 1
- src = sbng2Ptr + 8;
- len = tmp - sbng2Ptr - 6;
- memcpy(dst, src, len);
-
- // Rewrite the time for this new code block to be after the sound 1 code block
- int32 time;
- while ((size = READ_LE_UINT16(dst)) != 0) {
- time = READ_LE_UINT32(dst + 2);
- time += _sndTmrOffs;
- WRITE_LE_UINT32(dst + 2, time);
- dst += size;
+ memcpy(dep, sndSbngPtr + 8, sep - sndSbngPtr - 8 + 2);
+
+ while (READ_LE_UINT16(dep)) {
+ WRITE_LE_UINT32(dep + 2, READ_LE_UINT32(dep + 2) + _createSndLastPos);
+ dep += READ_LE_UINT16(dep);
+ }
+ }
}
}
- // Find the data pointers and sizes
- if (findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr)) {
- sdat1Ptr = findSoundTag(MKTAG('d','a','t','a'), baseSoundPtr);
- assert(sdat1Ptr);
- sdat2Ptr = findSoundTag(MKTAG('d','a','t','a'), soundPtr);
- assert(sdat2Ptr);
- if (!_sndDataSize)
- _sndDataSize = READ_LE_UINT32(sdat1Ptr + 4) - 8;
+ byte *pRiff = nullptr;
+ byte *pData = nullptr;
+
+ // Find where the actual sound data is located...
+ if (fIsWav) {
+ baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
+ if (baseSndPtr == nullptr)
+ error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", baseSound);
+
+ sndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtr);
+ if (sndPtr == nullptr)
+ error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", sound);
+
+ pData = baseSndPtr;
+ pRiff = baseSndPtr + 8;
- sdat2size = READ_LE_UINT32(sdat2Ptr + 4) - 8;
+ if (_baseSndSize == 0) {
+ _baseSndSize = READ_LE_UINT32(baseSndPtr + sizeof(uint32)) - 8;
+ }
+
+ sndSize = READ_LE_UINT32(sndPtr + sizeof(uint32)) - 8;
} else {
- sdat1Ptr = heFindResource(MKTAG('S','D','A','T'), baseSoundPtr);
- assert(sdat1Ptr);
- sdat2Ptr = heFindResource(MKTAG('S','D','A','T'), soundPtr);
- assert(sdat2Ptr);
+ baseSndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
+ if (baseSndPtr == nullptr)
+ error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", baseSound);
- _sndDataSize = READ_BE_UINT32(sdat1Ptr + 4) - 8;
+ sndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
+ if (sndPtr == nullptr)
+ error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", sound);
- sdat2size = READ_BE_UINT32(sdat2Ptr + 4) - 8;
+ _baseSndSize = READ_BE_UINT32(baseSndPtr + sizeof(uint32)) - 8;
+ sndSize = READ_BE_UINT32(sndPtr + sizeof(uint32)) - 8;
}
- sdat1size = _sndDataSize - _sndPtrOffs;
- if (sdat2size < sdat1size) {
- // We have space leftover at the end of sound 1
- // -> Just append sound 2
- src = sdat2Ptr + 8;
- dst = sdat1Ptr + 8 + _sndPtrOffs;
- len = sdat2size;
+ // Finally perform the merging of the sound data
+ baseSndLeft = _baseSndSize - _createSndLastAppend;
- memcpy(dst, src, len);
+ baseSndPtr += 8;
+ sndPtr += 8;
- _sndPtrOffs += sdat2size;
- _sndTmrOffs += sdat2size;
+ if (sndSize < baseSndLeft) {
+ memcpy(baseSndPtr + _createSndLastAppend, sndPtr, sndSize);
+ _createSndLastAppend += sndSize;
} else {
- // We might not have enough space leftover at the end of sound 1
- // -> Append as much of possible of sound 2 to sound 1
- src = sdat2Ptr + 8;
- dst = sdat1Ptr + 8 + _sndPtrOffs;
- len = sdat1size;
-
- memcpy(dst, src, len);
-
- if (sdat2size != sdat1size) {
- // We don't have enough space
- // -> Start overwriting the beginning of the sound again
- src = sdat2Ptr + 8 + sdat1size;
- dst = sdat1Ptr + 8;
- len = sdat2size - sdat1size;
-
- memcpy(dst, src, len);
+ memcpy(baseSndPtr + _createSndLastAppend, sndPtr, baseSndLeft);
+ if (sndSize - baseSndLeft) {
+ memcpy(baseSndPtr, sndPtr + baseSndLeft, sndSize - baseSndLeft);
}
+ _createSndLastAppend = sndSize - baseSndLeft;
+ }
+
+ _createSndLastPos += sndSize;
+
+ _heMixer->softRemixAllChannels();
+}
+
+byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
+ byte *wsouPtr = (byte *)block;
+
+ // For compatibility reason with old sound formats this
+ // doesn't error out, and instead gracefully returns a nullptr.
+ if (READ_BE_UINT32(wsouPtr) != MKTAG('W', 'S', 'O', 'U'))
+ return nullptr;
+
+ // Skip over the WSOU header...
+ byte *soundPtr = wsouPtr + 8;
+ if (READ_BE_UINT32(soundPtr) != MKTAG('R', 'I', 'F', 'F'))
+ error("SoundHE::findWavBlock(): Expected RIFF block");
+
+ int riffLength = READ_LE_UINT32(soundPtr + 4);
+ assert((riffLength & 1) == 0); // It must be even, since all sub-blocks must be padded to even.
+
+ // Skip over RIFF and length and go to the actual sound data...
+ byte *wavePtr = soundPtr + 8;
+ assert(READ_BE_UINT32(wavePtr) == MKTAG('W', 'A', 'V', 'E'));
+ wavePtr += 4; // Skip over the WAVE tag
+ riffLength -= 4;
- _sndPtrOffs = sdat2size - sdat1size;
- _sndTmrOffs += sdat2size;
+ // Walk the nested blocks of the .wav file...
+ while (riffLength > 0) {
+ int chunkID = READ_BE_UINT32(wavePtr);
+ int chunkLength = READ_LE_UINT32(wavePtr + 4);
+ if (chunkLength < 0)
+ error("SoundHE::findWavBlock(): Illegal chunk length - %d bytes", chunkLength);
+ if (chunkLength > riffLength)
+ error("SoundHE::findWavBlock(): Chunk extends beyond file end - %d versus %d", chunkLength, riffLength);
+
+ riffLength -= 8;
+
+ if ((uint32)chunkID == tag)
+ return wavePtr;
+
+ wavePtr += 8;
+
+ // Round up to the next multiple of two.
+ chunkLength = (chunkLength + 1) & ~1;
+ wavePtr += chunkLength;
+ riffLength -= chunkLength;
}
- // TODO: PX_SoftRemixAllChannels()
+ return nullptr;
}
+
#endif
} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 36bdd5c143b..1a4ae30087e 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -25,6 +25,7 @@
#include "common/scummsys.h"
#include "common/mutex.h"
#include "scumm/sound.h"
+#include "scumm/he/mixer_he.h"
#include "audio/audiostream.h"
namespace Scumm {
@@ -56,6 +57,7 @@ namespace Scumm {
#define HSND_FIRST_SPOOLING_SOUND 4000
#define HSND_MAX_FREQ_RATIO 4
+#define HSND_MAX_SPOOLING_CODE_SIZE 16384
#define HSND_SBNG_TYPE_ALL 0xF8
#define HSND_SBNG_DATA_ALL 0x07
@@ -80,7 +82,51 @@ namespace Scumm {
#define WAVE_FORMAT_PCM 1
#define WAVE_FORMAT_IMA_ADPCM 17
+#define HSND_RES_OFFSET_ID1 0 // uint32, DIGI or MIDI header
+#define HSND_RES_OFFSET_LEN1 4 // uint32
+#define HSND_RES_OFFSET_ID 8 // uint32, HSHD header
+#define HSND_RES_OFFSET_LEN2 12 // uint32
+#define HSND_RES_OFFSET_COMPRESSED 16 // uint8, header data
+#define HSND_RES_OFFSET_FORMATTED 17 // uint8
+#define HSND_RES_OFFSET_KILL_PRIO 18 // uint8
+#define HSND_RES_OFFSET_DEAD_PRIO 19 // uint8
+#define HSND_RES_OFFSET_LEFT_VOLUME 20 // uint8
+#define HSND_RES_OFFSET_RIGHT_VOLUME 21 // uint8
+#define HSND_RES_OFFSET_FREQUENCY 22 // uint16
+#define HSND_RES_OFFSET_MONDO_OFFSET 24 // uint32
+#define HSND_RES_OFFSET_BITS_PER_SAMPLE 28 // uint8
+#define HSND_RES_OFFSET_SAMPLE_CHANNELS 29 // uint8
+#define HSND_RES_OFFSET_UNUSED3 30 // uint8
+#define HSND_RES_OFFSET_UNUSED4 31 // uint8
+#define HSND_RES_OFFSET_ID3 32 // uint32, SDAT header
+#define HSND_RES_OFFSET_LEN3 36 // uint32
+#define HSND_RES_OFFSET_SOUND_DATA 40
+
+// Used both in SoundHE and HEMixer
+struct HESoundModifiers {
+ HESoundModifiers(int mFrequencyShift, int mPan, int mVolume) {
+ assert(mFrequencyShift >= HSND_SOUND_FREQ_BASE / HSND_MAX_FREQ_RATIO);
+ assert(mFrequencyShift <= HSND_SOUND_FREQ_BASE * HSND_MAX_FREQ_RATIO);
+ assert(mPan >= HSND_SOUND_PAN_LEFT && mPan <= HSND_SOUND_PAN_RIGHT);
+ assert(mVolume >= 0 && mVolume <= HSND_MAX_VOLUME);
+ frequencyShift = mFrequencyShift;
+ pan = mPan;
+ volume = mVolume;
+ }
+
+ HESoundModifiers() {
+ frequencyShift = HSND_SOUND_FREQ_BASE;
+ pan = HSND_SOUND_PAN_CENTER;
+ volume = HSND_MAX_VOLUME;
+ }
+
+ int frequencyShift;
+ int pan;
+ int volume;
+};
+
class ScummEngine_v60he;
+class HEMixer;
class SoundHE : public Sound {
protected:
@@ -88,6 +134,7 @@ protected:
int _overrideFreq;
Common::Mutex *_mutex;
+ HEMixer *_heMixer;
struct HESoundCallbackItem {
int32 sound;
@@ -114,52 +161,31 @@ protected:
uint16 wBitsPerSample;
};
- struct HESoundModifiers {
- HESoundModifiers(int mFrequencyShift, int mPan, int mVolume) {
- assert(mFrequencyShift >= HSND_SOUND_FREQ_BASE / HSND_MAX_FREQ_RATIO);
- assert(mFrequencyShift <= HSND_SOUND_FREQ_BASE * HSND_MAX_FREQ_RATIO);
- assert(mPan >= HSND_SOUND_PAN_LEFT && mPan <= HSND_SOUND_PAN_RIGHT);
- assert(mVolume >= 0 && mVolume <= HSND_MAX_VOLUME);
- frequencyShift = mFrequencyShift;
- pan = mPan;
- volume = mVolume;
- }
-
- HESoundModifiers() {
- frequencyShift = HSND_SOUND_FREQ_BASE;
- pan = HSND_SOUND_PAN_CENTER;
- volume = HSND_MAX_VOLUME;
- }
-
- int frequencyShift;
- int pan;
- int volume;
- };
-
- int32 _heMusicTracks;
+ int32 _heSpoolingMusicCount;
Audio::SoundHandle *_heSoundChannels;
- Common::File _spoolingMusicFile;
+ Common::File _heSpoolingMusicFile;
+ byte _heSpoolingCodeBuffer[HSND_MAX_SPOOLING_CODE_SIZE];
public: // Used by createSound()
struct {
int sound;
- int codeOffs;
+ int codeOffset;
byte *codeBuffer;
int priority;
int frequency;
- int timer;
+ int timeout;
bool hasSoundTokens;
int soundVars[HSND_MAX_SOUND_VARS];
int age;
void clearChannel() {
sound = 0;
- codeOffs = 0;
+ codeOffset = 0;
codeBuffer = nullptr;
priority = 0;
frequency = 0;
- timer = 0;
+ timeout = 0;
hasSoundTokens = false;
memset(soundVars, 0, sizeof(soundVars));
age = 0;
@@ -167,6 +193,14 @@ public: // Used by createSound()
} _heChannel[HSND_MAX_CHANNELS];
+ bool _useMilesSoundSystem = false;
+
+ // Used throughout script functions
+ int32 _createSndId;
+ int32 _createSndLastAppend;
+ int32 _createSndLastPos;
+ int32 _baseSndSize;
+
public:
SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex);
~SoundHE() override;
@@ -178,7 +212,7 @@ public:
int isSoundRunning(int sound) const override;
void stopSound(int sound) override;
void stopAllSounds() override;
- int findSoundChannel(int sound);
+ int hsFindSoundChannel(int sound);
void setupSound() override;
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
@@ -209,6 +243,9 @@ public:
void setupHEMusicFile();
void startHETalkSound(uint32 offset);
void stopSoundChannel(int chan);
+ void createSound(int snd1id, int snd2id);
+
+ byte *findWavBlock(uint32 tag, const byte *block);
protected:
void processSoundQueues() override;
@@ -220,6 +257,8 @@ private:
int _soundCallbacksQueueSize = 0;
int _soundAlreadyInQueueCount = 0;
int _soundsDebugFrameCounter = 0;
+ int _scummOverrideFrequency = 0;
+
Audio::RewindableAudioStream *tryLoadAudioOverride(int soundID, int *duration = nullptr);
};
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index d5968f71065..8f7dd8bfe40 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -712,11 +712,6 @@ ScummEngine_v72he::ScummEngine_v72he(OSystem *syst, const DetectorResult &dr)
ScummEngine_v80he::ScummEngine_v80he(OSystem *syst, const DetectorResult &dr)
: ScummEngine_v72he(syst, dr) {
- _heSndResId = 0;
- _curSndId = 0;
- _sndPtrOffs = 0;
- _sndTmrOffs = 0;
- _sndDataSize = 0;
VAR_PLATFORM_VERSION = 0xFF;
VAR_CURRENT_CHARSET = 0xFF;
@@ -3548,7 +3543,7 @@ int ScummEngine_v60he::getHETimer(int timer) {
}
void ScummEngine_v60he::setHETimer(int timer) {
- assertRange(1, timer, 15, "setHETimer: Timer");
+ assertRange(1, timer, ARRAYSIZE(_heTimers) - 1, "setHETimer: Timer");
_heTimers[timer] = _system->getMillis();
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 92955f69a3d..9a0abcdbbb6 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1799,6 +1799,7 @@ public:
byte VAR_START_DYN_SOUND_CHANNELS = 0xFF; // Used in getNextDynamicChannel()
byte VAR_SOUND_CALLBACK_SCRIPT = 0xFF;
+ byte VAR_EARLY_TALKIE_CALLBACK = 0xFF;
byte VAR_EARLY_CHAN_0_CALLBACK = 0xFF;
byte VAR_EARLY_CHAN_1_CALLBACK = 0xFF;
byte VAR_EARLY_CHAN_2_CALLBACK = 0xFF;
@@ -1810,6 +1811,8 @@ public:
byte VAR_LAST_SCRIPT_PRIORITY = 0xFF; // Used in runAllScripts()
byte VAR_QUIT_SCRIPT = 0xFF; // Used in confirmExitDialog()
+ byte VAR_ERROR_FLAG = 0xFF; // HE70-90
+ byte VAR_OPERATION_FAILURE = 0xFF; // HE99+
// Exists both in V7 and in V72HE:
byte VAR_NUM_GLOBAL_OBJS = 0xFF;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 39f6ecec7cd..2c649b091ac 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -202,7 +202,7 @@ void Sound::updateMusicTimer() {
_musicTimer = 277;
}
-void Sound::startSound(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void Sound::startSound(int sound, int offset, int channel, int flags, int freq, int pan, int volume) {
if (_vm->VAR_LAST_SOUND != 0xFF)
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
_lastSound = sound;
@@ -211,18 +211,18 @@ void Sound::startSound(int sound, int heOffset, int heChannel, int heFlags, int
if (sound <= _vm->_numSounds)
_vm->ensureResourceLoaded(rtSound, sound);
- addSoundToQueue(sound, heOffset, heChannel, heFlags, heFreq, hePan, heVol);
+ addSoundToQueue(sound, offset, channel, flags, freq, pan, volume);
}
-void Sound::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
+void Sound::addSoundToQueue(int sound, int offset, int channel, int flags, int freq, int pan, int volume) {
assert(_soundQueuePos < ARRAYSIZE(_soundQueue));
_soundQueue[_soundQueuePos].sound = sound;
- _soundQueue[_soundQueuePos].offset = heOffset;
- _soundQueue[_soundQueuePos].channel = heChannel;
- _soundQueue[_soundQueuePos].flags = heFlags;
- _soundQueue[_soundQueuePos].freq = heFreq;
- _soundQueue[_soundQueuePos].pan = hePan;
- _soundQueue[_soundQueuePos].vol = heVol;
+ _soundQueue[_soundQueuePos].offset = offset;
+ _soundQueue[_soundQueuePos].channel = channel;
+ _soundQueue[_soundQueuePos].flags = flags;
+ _soundQueue[_soundQueuePos].freq = freq;
+ _soundQueue[_soundQueuePos].pan = pan;
+ _soundQueue[_soundQueuePos].vol = volume;
_soundQueuePos++;
}
@@ -1765,7 +1765,7 @@ int ScummEngine::readSoundResource(ResId idx) {
ptr = _res->createResource(rtSound, idx, total_size + 8);
((uint32 *)ptr)[0] = TO_BE_32(MKTAG('W', 'S', 'O', 'U'));
- ((uint32 *)ptr)[1] = total_size + 8;
+ ((uint32 *)ptr)[1] = TO_BE_32(total_size + 8);
// Move the ptr forward for the actual data allocation,
// so that our new header doesn't get rewritten
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 0db7b60a28b..6c04c1df9bf 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -214,6 +214,7 @@ void ScummEngine_v70he::setupScummVars() {
VAR_CURRENTDRIVE = 0xFF;
VAR_MUSIC_TIMER = 0xFF;
+ VAR_ERROR_FLAG = 8; // Alias of VAR_GAME_LOADED for now, which will get removed later
VAR_NUM_SOUND_CHANNELS = 9;
VAR_TALK_CHANNEL = 10;
VAR_SOUND_CHANNEL = 14;
@@ -272,6 +273,7 @@ void ScummEngine_v72he::setupScummVars() {
VAR_SOUND_CHANNEL = 52;
VAR_EARLY_CHAN_0_CALLBACK = 53;
VAR_EARLY_CHAN_1_CALLBACK = 54;
+ VAR_EARLY_TALKIE_CALLBACK = 55;
VAR_EARLY_CHAN_2_CALLBACK = 55;
VAR_EARLY_CHAN_3_CALLBACK = 56;
VAR_MEMORY_PERFORMANCE = 57;
@@ -333,6 +335,7 @@ void ScummEngine_v90he::setupScummVars() {
VAR_U32_VERSION = 107;
VAR_U32_ARRAY_UNK = 116;
VAR_WIZ_TCOLOR = 117;
+ VAR_OPERATION_FAILURE = 119;
VAR_START_DYN_SOUND_CHANNELS = 120;
}
if (_game.heversion >= 98) {
Commit: e457f7590821bba30f5a2e9a29fa9459c019ccc0
https://github.com/scummvm/scummvm/commit/e457f7590821bba30f5a2e9a29fa9459c019ccc0
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix crashes in HE versions without sound modifiers
Also, fix early callback stuff and clean some other stuff up
Changed paths:
engines/scumm/akos.cpp
engines/scumm/he/intern_he.h
engines/scumm/he/script_v70he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.cpp
engines/scumm/vars.cpp
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index 3009dee4ca4..0748bb1afca 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -25,6 +25,7 @@
#include "scumm/bomp.h"
#include "scumm/imuse_digi/dimuse_engine.h"
#include "scumm/he/intern_he.h"
+#include "scumm/he/sound_he.h"
#include "scumm/resource.h"
#include "scumm/scumm_v7.h"
#include "scumm/sound.h"
@@ -1647,7 +1648,15 @@ void ScummEngine_v6::akos_processQueue() {
a->putActor(0, 0, 0);
break;
case AKQC_StartSound:
- _sound->startSound(param1, 0, -1, 0);
+ if (_game.heversion < 95) {
+ _sound->addSoundToQueue(param1, 0, -1, 0,
+ HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+ } else {
+ // Later games also set VAR_LAST_SOUND variable
+ _sound->startSound(param1, 0, -1, 0,
+ HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+ }
+
break;
case AKQC_StartAnimation:
a->startAnimActor(param1);
@@ -1678,7 +1687,15 @@ void ScummEngine_v6::akos_processQueue() {
break;
case AKQC_SoftStartSound:
- _sound->startSound(param1, 0, -1, 4);
+ if (_game.heversion < 95) {
+ _sound->addSoundToQueue(param1, 0, -1, ScummEngine_v70he::HESndFlags::HE_SND_SOFT_SOUND,
+ HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+ } else {
+ // Later games also set VAR_LAST_SOUND variable
+ _sound->startSound(param1, 0, -1, ScummEngine_v70he::HESndFlags::HE_SND_SOFT_SOUND,
+ HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+ }
+
break;
default:
error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param1, param2);
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index 40d2258bd45..ec25fd0a97a 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -137,7 +137,8 @@ class ScummEngine_v70he : public ScummEngine_v60he {
friend class ResExtractor;
friend class SoundHE;
-protected:
+public:
+ // Used in akos.cpp
enum HESndFlags {
HE_SND_LOOP = 1,
HE_SND_APPEND = 2,
@@ -149,6 +150,8 @@ protected:
HE_SND_PAN = 128
};
+
+protected:
enum SubOpType {
SO_SOFT = 9,
SO_IMAGE_LOADED = 18,
@@ -193,7 +196,8 @@ protected:
byte *_heV7RoomOffsets;
uint32 *_heV7RoomIntOffsets;
- int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol;
+ int32 _heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags,
+ _heSndFrequency, _heSndFrequencyShift, _heSndPan, _heSndVol;
bool _heSndStartNewSoundFlag;
int _numStoredFlObjects;
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index 2eca4a41d32..125e3dd0809 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -69,7 +69,7 @@ void ScummEngine_v70he::o70_soundOps() {
case SO_SOUND_VOLUME:
value = pop();
_heSndSoundId = pop();
- _sound->startSound(_heSndSoundId, 0, 0, HE_SND_VOL, 0, 0, value);
+ _sound->startSound(_heSndSoundId, 0, 0, HE_SND_VOL, HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, value);
break;
case SO_NOW:
_heSndFlags |= HE_SND_QUICK_START;
@@ -81,7 +81,7 @@ void ScummEngine_v70he::o70_soundOps() {
// WORKAROUND: For errors in room script 240 (room 4) of maze
break;
case SO_SOUND_FREQUENCY:
- _heSndFrequencyShift = pop();
+ _heSndFrequency = pop();
break;
case SO_SOUND_CHANNEL:
_heSndChannel = pop();
@@ -92,14 +92,15 @@ void ScummEngine_v70he::o70_soundOps() {
case SO_SOUND_START:
_heSndSoundId = pop();
_heSndOffset = 0;
- _heSndFrequencyShift = 11025;
+ _heSndFrequency = 11025; // This is set but apparently is not used?
_heSndChannel = VAR(VAR_SOUND_CHANNEL);
break;
case SO_SOUND_LOOPING:
_heSndFlags |= HE_SND_LOOP;
break;
case SO_END:
- _sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift);
+ _sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags,
+ HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
_heSndFlags = 0;
break;
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index f2da8e91c17..43f814677b0 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -465,6 +465,9 @@ bool SoundHE::getHEMusicDetails(int id, int &musicOffs, int &musicSize) {
}
void SoundHE::handleSoundFrame() {
+ if (_vm->_game.heversion < 80)
+ return;
+
_soundsDebugFrameCounter++;
if (_stopActorTalkingFlag) {
@@ -472,7 +475,8 @@ void SoundHE::handleSoundFrame() {
_stopActorTalkingFlag = false;
}
- unqueueSoundCallbackScripts();
+ if (_vm->_game.heversion >= 95)
+ unqueueSoundCallbackScripts();
runSoundCode();
checkSoundTimeouts();
@@ -628,13 +632,22 @@ void SoundHE::runSoundCode() {
freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
while (soundPos > freq) {
- debug(5, "Channel %d Timer %d Time %d", chan, soundPos, freq);
+ debug(5, "SoundHE::runSoundCode(): Channel %d Timer %d Time %d", chan, soundPos, freq);
processSoundOpcodes(_heChannel[chan].sound, soundPtr + sizeof(uint16) + sizeof(uint32), _heChannel[chan].soundVars);
_heChannel[chan].codeOffset += len;
- soundPtr += len;
+ // The original runs the following section again on purpose
+ if (_heChannel[chan].codeBuffer == nullptr) {
+ soundPtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
+ } else {
+ soundPtr = _heChannel[chan].codeBuffer;
+ }
+
+ assert(soundPtr);
+
+ soundPtr += _heChannel[chan].codeOffset;
len = READ_LE_UINT16(soundPtr);
freq = READ_LE_UINT32(soundPtr + sizeof(uint16));
@@ -657,7 +670,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
arg = opcode & HSND_SBNG_VARORVAL;
opcode &= ~HSND_SBNG_VARORVAL;
- debug(5, "processSoundOpcodes: sound %d opcode %d", sound, opcode);
+ debug(5, "SoundHE::processSoundOpcodes(): sound %d opcode %d", sound, opcode);
switch (opcode) {
case HSND_SBNG_END: // Continue
@@ -709,7 +722,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
}
if (!val) {
val = 1; // Safeguard for division by zero
- warning("Incorrect value 0 for processSoundOpcodes() kludge DIV");
+ warning("SoundHE::processSoundOpcodes(): Incorrect value 0 for processSoundOpcodes() kludge DIV");
}
val = getSoundVar(sound, var) / val;
setSoundVar(sound, var, val);
@@ -725,43 +738,11 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
setSoundVar(sound, var, val);
break;
default:
- error("Illegal sound %d opcode %d", sound, opcode);
+ error("SoundHE::processSoundOpcodes(): Illegal sound %d opcode %d", sound, opcode);
}
}
}
-byte *findSoundTag(uint32 tag, byte *ptr) {
- byte *endPtr;
- uint32 offset, size;
-
- if (READ_BE_UINT32(ptr) == MKTAG('W','S','O','U')) {
- ptr += 8;
- }
-
- if (READ_BE_UINT32(ptr) != MKTAG('R','I','F','F'))
- return nullptr;
-
- endPtr = (ptr + 12);
- size = READ_LE_UINT32(ptr + 4);
-
- while (endPtr < ptr + size) {
- offset = READ_LE_UINT32(endPtr + 4);
-
- if (offset <= 0)
- error("Illegal chunk length - %d bytes.", offset);
-
- if (offset > size)
- error("Chunk extends beyond file end - %d versus %d.", offset, size);
-
- if (READ_BE_UINT32(endPtr) == tag)
- return endPtr;
-
- endPtr = endPtr + offset + 8;
- }
-
- return nullptr;
-}
-
void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
if (_vm->_game.heversion >= 95) {
if (heChannel == HSND_DYN_SOUND_CHAN) {
@@ -779,18 +760,7 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
if ((READ_BE_UINT32(soundAddr) == MKTAG('D', 'I', 'G', 'I')) || (READ_BE_UINT32(soundAddr) == MKTAG('T', 'A', 'L', 'K'))) {
triggerDigitalSound(soundId, heOffset, heChannel, heFlags);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('M', 'I', 'D', 'I')) {
- if (_vm->_imuse) {
- // This is used in the DOS version of Fatty Bear's
- // Birthday Surprise to change the note on the piano
- // when not using a digitized instrument.
- _vm->_imuse->stopSound(_currentMusic);
- _currentMusic = soundId;
- _vm->_imuse->startSoundWithNoteOffset(soundId, heOffset);
- } else if (_vm->_musicEngine) {
- _vm->_musicEngine->stopSound(_currentMusic);
- _currentMusic = soundId;
- _vm->_musicEngine->startSoundWithTrackID(soundId, heOffset);
- }
+ triggerMidiSound(soundId, heOffset);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('W', 'S', 'O', 'U')) {
triggerRIFFSound(soundId, heOffset, heChannel, heFlags, modifiers);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('X', 'S', 'O', 'U')) {
@@ -1217,7 +1187,7 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
if (_vm->_game.heversion >= 95) {
index = _vm->VAR_EARLY_CHAN_1_CALLBACK + channel;
- if ((_vm->VAR_EARLY_CHAN_1_CALLBACK <= index) && (_vm->VAR_EARLY_CHAN_3_CALLBACK >= index)) {
+ if ((index >= _vm->VAR_EARLY_CHAN_1_CALLBACK) && (index <= _vm->VAR_EARLY_CHAN_3_CALLBACK)) {
earlyCallbackByteCount = _vm->VAR(index);
} else {
earlyCallbackByteCount = 0;
@@ -1231,32 +1201,29 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
modifiers, channel, CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
earlyCallbackByteCount);
} else if (_vm->_game.heversion >= 80) {
+ // HE 80 doesn't make checks on the range for the early callback channel
_heMixer->startChannel(
channel, globType, globNum, soundData + offset,
(sampleCount - offset), HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel,
CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
_vm->VAR(_vm->VAR_EARLY_CHAN_1_CALLBACK + channel));
} else {
- if (channel != _vm->VAR(_vm->VAR_TALK_CHANNEL)) {
+ // Sub HE 80 codepath, simpler than the newer versions
+
+ // If we're trying to start a speech sound, we have to signal an early callback
+ if (_vm->_game.heversion >= 70 && channel == _vm->VAR(_vm->VAR_TALK_CHANNEL)) {
+ earlyCallbackByteCount = ((_vm->VAR(_vm->VAR_TIMER_NEXT) * frequency) / 60);
+ earlyCallbackByteCount *= _vm->VAR(_vm->VAR_EARLY_TALKIE_CALLBACK);
+
_heMixer->startChannel(
channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
- sampleCount - offset, frequency, HSND_MAX_VOLUME, channel, CHANNEL_ACTIVE | hflags);
+ sampleCount - offset, frequency, HSND_MAX_VOLUME, channel,
+ CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
+ earlyCallbackByteCount);
} else {
- if (_vm->_game.heversion >= 72) {
- earlyCallbackByteCount = ((_vm->VAR(_vm->VAR_TIMER_NEXT) * frequency) / 60);
- earlyCallbackByteCount *= _vm->VAR(_vm->VAR_EARLY_TALKIE_CALLBACK);
-
- _heMixer->startChannel(
- channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
- sampleCount - offset, frequency, HSND_MAX_VOLUME, channel,
- CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags,
- earlyCallbackByteCount);
- } else {
- _heMixer->startChannel(
- channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
- sampleCount - offset, frequency, HSND_MAX_VOLUME, channel,
- CHANNEL_ACTIVE | CHANNEL_CALLBACK_EARLY | hflags);
- }
+ _heMixer->startChannel(
+ channel, globType, globNum, HSND_RES_OFFSET_SOUND_DATA + offset,
+ sampleCount - offset, frequency, HSND_MAX_VOLUME, channel, CHANNEL_ACTIVE | hflags);
}
_heChannel[channel].sound = sound;
@@ -1361,6 +1328,21 @@ void SoundHE::triggerDigitalSound(int sound, int offset, int channel, int flags)
bitsPerSample, sampleChannels, HESoundModifiers());
}
+void SoundHE::triggerMidiSound(int soundId, int heOffset) {
+ if (_vm->_imuse) {
+ // This is used in the DOS version of Fatty Bear's
+ // Birthday Surprise to change the note on the piano
+ // when not using a digitized instrument.
+ _vm->_imuse->stopSound(_currentMusic);
+ _currentMusic = soundId;
+ _vm->_imuse->startSoundWithNoteOffset(soundId, heOffset);
+ } else if (_vm->_musicEngine) {
+ _vm->_musicEngine->stopSound(_currentMusic);
+ _currentMusic = soundId;
+ _vm->_musicEngine->startSoundWithTrackID(soundId, heOffset);
+ }
+}
+
Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *duration) {
if (!_vm->_enableAudioOverride) {
return nullptr;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 1a4ae30087e..85606b95616 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -224,6 +224,7 @@ public:
void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags);
+ void triggerMidiSound(int soundId, int heOffset);
void triggerRIFFSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerXSOUSound(int soundId, int heOffset, int heChannel, int heFlags);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 8f7dd8bfe40..809a4a904b6 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -656,6 +656,7 @@ ScummEngine_v70he::ScummEngine_v70he(OSystem *syst, const DetectorResult &dr)
_heSndOffset = 0;
_heSndChannel = 0;
_heSndFlags = 0;
+ _heSndFrequency = 0;
_heSndFrequencyShift = 0;
_heSndPan = 0;
_heSndVol = 0;
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 6c04c1df9bf..0a9652c5f70 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -218,6 +218,7 @@ void ScummEngine_v70he::setupScummVars() {
VAR_NUM_SOUND_CHANNELS = 9;
VAR_TALK_CHANNEL = 10;
VAR_SOUND_CHANNEL = 14;
+ VAR_EARLY_TALKIE_CALLBACK = 66;
}
#ifdef ENABLE_HE
Commit: 9f89e3e8237ca0dd6acbdf42342ed05375cdd65c
https://github.com/scummvm/scummvm/commit/9f89e3e8237ca0dd6acbdf42342ed05375cdd65c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Finish wiring up high level functions
Everything is wired up... except the mixer, hence no sound.
Changed paths:
engines/scumm/actor.cpp
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/script_v72he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.h
engines/scumm/string.cpp
engines/scumm/vars.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 1f74d7627ba..3d00bfb2cda 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -3122,7 +3122,7 @@ void ScummEngine::stopTalk() {
_sound->stopTalkSound();
- _haveMsg = 0;
+ _haveMsg = _game.heversion >= 70 ? 3 : 0;
_talkDelay = 0;
_sound->_digiSndMode = DIGI_SND_MODE_EMPTY;
@@ -3137,12 +3137,7 @@ void ScummEngine::stopTalk() {
if (_game.version <= 7 && _game.heversion == 0)
setTalkingActor(0xFF);
if (_game.heversion != 0) {
- if (_game.heversion == 98 && _game.id == GID_FREDDI4) {
- // Delay unsetting _heTalking to next sound frame. fixes bug #3533.
- _actorShouldStopTalking = true;
- } else {
- ((ActorHE *)a)->_heTalking = false;
- }
+ ((ActorHE *)a)->_heTalking = false;
}
}
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 727ff4b3546..83c7a0d6202 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -80,6 +80,10 @@ bool HEMixer::pauseMixerSubSystem(bool paused) {
void HEMixer::feedMixer() {
}
+int HEMixer::getChannelCurrentPosition(int channel) {
+ return 0;
+}
+
bool HEMixer::startChannelNew(
int channel, int globType, int globNum, uint32 soundData, uint32 offset,
int sampleLen, int frequency, int bitsPerSample, int sampleChannels,
@@ -89,7 +93,7 @@ bool HEMixer::startChannelNew(
if (!_useMilesSoundSystem) {
if (bitsPerSample != 8) {
- debug(5, "HEMixer::PX_StartChannel(): Glob(%d, %d) is %d bits per channel, must be 8 for software mixer", globType, globNum, bitsPerSample);
+ debug(5, "HEMixer::startChannelNew(): Glob(%d, %d) is %d bits per channel, must be 8 for software mixer", globType, globNum, bitsPerSample);
return false;
}
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 13b1883be72..0fdf610e495 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -71,6 +71,7 @@ public:
void premixUntilCritical();
bool pauseMixerSubSystem(bool paused);
void feedMixer();
+ int getChannelCurrentPosition(int channel);
bool startChannelNew(
int channel, int globType, int globNum, uint32 soundData, uint32 offset,
diff --git a/engines/scumm/he/script_v72he.cpp b/engines/scumm/he/script_v72he.cpp
index 80c99d58369..a2638585e5f 100644
--- a/engines/scumm/he/script_v72he.cpp
+++ b/engines/scumm/he/script_v72he.cpp
@@ -554,7 +554,7 @@ void ScummEngine_v72he::o72_setTimer() {
void ScummEngine_v72he::o72_getSoundPosition() {
int snd = pop();
- push(((SoundHE *)_sound)->getSoundPos(snd));
+ push(((SoundHE *)_sound)->getSoundPosition(snd));
}
void ScummEngine_v72he::o72_startScript() {
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 43f814677b0..d55e539b2e6 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -146,70 +146,111 @@ void SoundHE::processSoundQueues() {
int SoundHE::isSoundRunning(int sound) const {
if (_vm->_game.heversion >= 70) {
- if (sound >= 10000) {
- return _mixer->getSoundID(_heSoundChannels[sound - 10000]);
+ if (sound >= HSND_CHANNEL_0) {
+ sound = _heChannel[sound - HSND_CHANNEL_0].sound;
}
- } else if (_vm->_game.heversion >= 60) {
+
+ if (hsFindSoundChannel(sound) != -1) {
+ return sound;
+ }
+
+ return 0;
+ } else {
if (sound == -2) {
sound = _heChannel[0].sound;
} else if (sound == -1) {
sound = _currentMusic;
}
- }
- if (_mixer->isSoundIDActive(sound))
- return sound;
+ if (_mixer->isSoundIDActive(sound))
+ return sound;
- if (isSoundInQueue(sound))
- return sound;
+ if (isSoundInQueue(sound))
+ return sound;
- if (_vm->_musicEngine && _vm->_musicEngine->getSoundStatus(sound))
- return sound;
+ if (_vm->_musicEngine && _vm->_musicEngine->getSoundStatus(sound))
+ return sound;
- return 0;
+ return 0;
+ }
}
void SoundHE::stopSound(int sound) {
if (_vm->_game.heversion >= 70) {
- if ( sound >= 10000) {
- stopSoundChannel(sound - 10000);
+ int channel = -1;
+
+ if (sound >= HSND_CHANNEL_0 && sound <= HSND_CHANNEL_7) {
+ channel = sound - HSND_CHANNEL_0;
+
+ if (_heChannel[channel].sound) {
+ sound = _heChannel[channel].sound;
+ stopDigitalSound(sound);
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].channel == channel)
+ _soundQueue[i].sound = 0;
+ }
+ } else {
+ if (_vm->_game.heversion >= 95 && sound == HSND_DYN_SOUND_CHAN) {
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].channel == HSND_DYN_SOUND_CHAN) {
+ _soundQueue[i].sound = 0;
+ }
+ }
+ } else {
+ if (hsFindSoundChannel(sound) != -1) {
+ stopDigitalSound(sound);
+ }
+
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].sound == sound)
+ _soundQueue[i].sound = 0;
+ }
+ }
+ }
+
+ if ((sound == HSND_TALKIE_SLOT) || (channel == _vm->VAR(_vm->VAR_TALK_CHANNEL))) {
+ _vm->_talkDelay = 0;
}
+
} else if (_vm->_game.heversion >= 60) {
if (sound == -2) {
sound = _heChannel[0].sound;
} else if (sound == -1) {
sound = _currentMusic;
}
- }
-
- Sound::stopSound(sound);
- for (int i = 0; i < ARRAYSIZE(_heChannel); i++) {
- if (_heChannel[i].sound == sound) {
- _heChannel[i].sound = 0;
- _heChannel[i].priority = 0;
- _heChannel[i].frequency = 0;
- _heChannel[i].timeout = 0;
- _heChannel[i].hasSoundTokens = false;
- _heChannel[i].codeOffset = 0;
- memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
+ Sound::stopSound(sound);
+
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i++) {
+ if (_heChannel[i].sound == sound) {
+ _heChannel[i].sound = 0;
+ _heChannel[i].priority = 0;
+ _heChannel[i].frequency = 0;
+ _heChannel[i].timeout = 0;
+ _heChannel[i].hasSoundTokens = false;
+ _heChannel[i].codeOffset = 0;
+ memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
+ }
}
}
-
- if (_vm->_game.heversion >= 70 && sound == 1) {
- _vm->_haveMsg = 3;
- _vm->_talkDelay = 0;
- }
}
void SoundHE::stopAllSounds() {
- // Clear sound channels for HE games
- memset(_heChannel, 0, sizeof(_heChannel));
+ if (_vm->_game.heversion >= 95)
+ stopSound(HSND_DYN_SOUND_CHAN);
+
+ for (int i = HSND_CHANNEL_0; i <= HSND_CHANNEL_7; i++) {
+ stopSound(i);
+ }
- Sound::stopAllSounds();
+ // Empty the sound queue
+ _soundQueuePos = 0;
+ memset(_soundQueue, 0, sizeof(_soundQueue));
}
-int SoundHE::hsFindSoundChannel(int sound) {
+int SoundHE::hsFindSoundChannel(int sound) const {
if (sound >= HSND_CHANNEL_0) {
int channel = sound - HSND_CHANNEL_0;
@@ -238,30 +279,13 @@ void SoundHE::setupSound() {
}
}
-void SoundHE::stopSoundChannel(int chan) {
- if (_heChannel[chan].sound == HSND_TALKIE_SLOT || chan == _vm->VAR(_vm->VAR_TALK_CHANNEL)) {
+void SoundHE::stopDigitalSound(int sound) {
+ if (sound == HSND_TALKIE_SLOT) {
_vm->_haveMsg = 3;
_vm->_talkDelay = 0;
}
- _mixer->stopHandle(_heSoundChannels[chan]);
-
- _heChannel[chan].sound = 0;
- _heChannel[chan].priority = 0;
- _heChannel[chan].frequency = 0;
- _heChannel[chan].timeout = 0;
- _heChannel[chan].hasSoundTokens = false;
- _heChannel[chan].codeOffset = 0;
- memset(_heChannel[chan].soundVars, 0, sizeof(_heChannel[chan].soundVars));
-
- for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
- if (_soundQueue[i].channel == chan) {
- _soundQueue[i].sound = 0;
- _soundQueue[i].offset = 0;
- _soundQueue[i].channel = 0;
- _soundQueue[i].flags = 0;
- }
- }
+ hsStopDigitalSound(sound);
}
int SoundHE::getNextDynamicChannel() {
@@ -316,16 +340,20 @@ bool SoundHE::isSoundCodeUsed(int sound) {
}
}
-int SoundHE::getSoundPos(int sound) {
- int chan = -1;
- for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
- if (_heChannel[i].sound == sound)
- chan = i;
+int SoundHE::getChannelPosition(int channel) {
+ if (_vm->_game.heversion >= 80) {
+ int frequency = _vm->_game.heversion >= 95 ? _heChannel[channel].frequency : HSND_DEFAULT_FREQUENCY;
+ return (_vm->getHETimer(HSND_TIMER_SLOT + channel) * frequency) / 1000;
+ } else {
+ return _heMixer->getChannelCurrentPosition(channel);
}
+}
- if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
- int time = _vm->getHETimer(chan + 4) * _heChannel[chan].frequency / 1000;
- return time;
+int SoundHE::getSoundPosition(int sound) {
+ int channel = hsFindSoundChannel(sound);
+
+ if (channel != -1) {
+ return getChannelPosition(channel);
} else {
return 0;
}
@@ -353,7 +381,7 @@ int SoundHE::getSoundVar(int sound, int var) {
}
void SoundHE::setSoundVar(int sound, int var, int val) {
- assertRange(0, var, 25, "sound variable");
+ assertRange(0, var, HSND_MAX_SOUND_VARS - 1, "sound variable");
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
@@ -362,7 +390,7 @@ void SoundHE::setSoundVar(int sound, int var, int val) {
}
if (chan != -1) {
- debug(5, "setSoundVar: sound %d var %d val %d", sound, var, val);
+ debug(5, " SoundHE::setSoundVar(): sound %d var %d val %d", sound, var, val);
_heChannel[chan].soundVars[var] = val;
}
}
@@ -483,7 +511,10 @@ void SoundHE::handleSoundFrame() {
}
void SoundHE::unqueueSoundCallbackScripts() {
- Common::StackLock lock(*_mutex);
+ if (_inUnqueueCallbackScripts)
+ return;
+
+ _inUnqueueCallbackScripts++;
for (int i = 0; i < _soundCallbacksQueueSize; i++) {
@@ -505,6 +536,7 @@ void SoundHE::unqueueSoundCallbackScripts() {
}
_soundCallbacksQueueSize = 0;
+ _inUnqueueCallbackScripts--;
}
void SoundHE::checkSoundTimeouts() {
@@ -1074,9 +1106,9 @@ void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heF
case MKTAG('X', 'S', 'H', '2'):
{
// Check for the optional sound flag block
- int optionalBlockFlags = READ_BE_UINT32(wavePtr);
+ int optionalBlockFlags = READ_LE_UINT32(wavePtr);
if (optionalBlockFlags & XSH2_FLAG_HAS_PRIORITY)
- soundPriority = READ_BE_UINT32(wavePtr + 4);
+ soundPriority = READ_LE_UINT32(wavePtr + 4);
break;
}
@@ -1127,10 +1159,10 @@ void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heF
soundHeaderBlock = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('X', 'S', 'H', 'D'), soundPtr, 0, true);
soundHeaderBlock += 8;
- sampleCount = READ_BE_UINT32(soundHeaderBlock + 0);
- sampleFrequency = READ_BE_UINT32(soundHeaderBlock + 4);
- bitsPerSample = READ_BE_UINT32(soundHeaderBlock + 8);
- sampleChannelCount = READ_BE_UINT32(soundHeaderBlock + 12);
+ sampleCount = READ_LE_UINT32(soundHeaderBlock + 0);
+ sampleFrequency = READ_LE_UINT32(soundHeaderBlock + 4);
+ bitsPerSample = READ_LE_UINT32(soundHeaderBlock + 8);
+ sampleChannelCount = READ_LE_UINT32(soundHeaderBlock + 12);
soundDataPtr = (byte *)((ScummEngine_v71he *)_vm)->findWrappedBlock(MKTAG('X', 'D', 'A', 'T'), soundPtr, 0, true);
soundDataOffset = ((soundDataPtr - soundPtr) + 8);
@@ -1142,11 +1174,11 @@ void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heF
if (optionalHeaderBlock) {
optionalHeaderBlock += 8;
- optionalBlockFlags = READ_BE_UINT32(optionalHeaderBlock);
+ optionalBlockFlags = READ_LE_UINT32(optionalHeaderBlock);
optionalHeaderBlock += 4;
if (optionalBlockFlags & XSH2_FLAG_HAS_PRIORITY) {
- soundPriority = READ_BE_UINT32(optionalHeaderBlock);
+ soundPriority = READ_LE_UINT32(optionalHeaderBlock);
optionalHeaderBlock += 4;
}
}
@@ -1383,7 +1415,7 @@ Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *du
if (soundId == 1) {
// Speech audio doesn't have a unique ID,
// so we use the file offset instead.
- // _heTalkOffset is set at startHETalkSound.
+ // _heTalkOffset is set at playVoice.
type = "speech";
soundId = _heTalkOffset;
} else {
@@ -1424,9 +1456,9 @@ Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *du
return nullptr;
}
-void SoundHE::startHETalkSound(uint32 offset) {
+void SoundHE::playVoice(uint32 offset, uint32 length) {
byte *ptr;
- int32 size;
+ int talkieChannel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
if (ConfMan.getBool("speech_mute"))
return;
@@ -1434,15 +1466,16 @@ void SoundHE::startHETalkSound(uint32 offset) {
if (_sfxFilename.empty()) {
// This happens in the Pajama Sam's Lost & Found demo, on the
// main menu screen, so don't make it a fatal error.
- warning("startHETalkSound: Speech file is not found");
+ warning("SoundHE::playVoice(): Speech file is not found");
return;
}
ScummFile file(_vm);
if (!_vm->openFile(file, _sfxFilename)) {
- warning("startHETalkSound: Could not open speech file %s", _sfxFilename.c_str());
+ warning("SoundHE::playVoice(): Could not open speech file %s", _sfxFilename.c_str());
return;
}
+
file.setEnc(_sfxFileEncByte);
// Speech audio doesn't have a unique ID,
@@ -1451,18 +1484,29 @@ void SoundHE::startHETalkSound(uint32 offset) {
_heTalkOffset = offset;
_digiSndMode |= DIGI_SND_MODE_TALKIE;
- _vm->_res->nukeResource(rtSound, 1);
+ _heMixer->stopChannel(talkieChannel);
+ _vm->_res->nukeResource(rtSound, HSND_TALKIE_SLOT);
- file.seek(offset + 4, SEEK_SET);
- size = file.readUint32BE();
file.seek(offset, SEEK_SET);
- _vm->_res->createResource(rtSound, 1, size);
+ _vm->_res->createResource(rtSound, 1, length);
ptr = _vm->getResourceAddress(rtSound, 1);
- file.read(ptr, size);
+ file.read(ptr, length);
+
+ addSoundToQueue(HSND_TALKIE_SLOT, 0, talkieChannel, 0, HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+}
- int channel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
- addSoundToQueue(HSND_TALKIE_SLOT, 0, channel, 0, HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
+void SoundHE::playVoiceFile(char *filename) {
+ // This is unimplemented on purpose! I haven't found a game which uses this;
+ // after all, this is a development path and it doesn't seem to affect any game
+ // version I had the chance to test. Emphasis on "had the chance to test":
+ // I don't know if there's actually a game which uses this!
+ //
+ // Originally this served the purpose of fetching voice lines from separate files
+ // instead of using a single .HE2 bundle.
+ //
+ // Again, we should never end up in here, but IF WE DO we issue an error.
+ error("SoundHE::playVoiceFile(): Unimplemented development codepath, please file a ticket!");
}
#ifdef ENABLE_HE
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 85606b95616..3e09a81052c 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -212,13 +212,13 @@ public:
int isSoundRunning(int sound) const override;
void stopSound(int sound) override;
void stopAllSounds() override;
- int hsFindSoundChannel(int sound);
+ int hsFindSoundChannel(int sound) const;
void setupSound() override;
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
int getNextDynamicChannel();
bool isSoundCodeUsed(int sound);
- int getSoundPos(int sound);
+ int getSoundPosition(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
@@ -242,9 +242,11 @@ public:
void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
void setOverrideFreq(int freq);
void setupHEMusicFile();
- void startHETalkSound(uint32 offset);
- void stopSoundChannel(int chan);
+ void playVoice(uint32 offset, uint32 length);
+ void playVoiceFile(char *filename);
+ void stopDigitalSound(int sound);
void createSound(int snd1id, int snd2id);
+ int getChannelPosition(int channel);
byte *findWavBlock(uint32 tag, const byte *block);
@@ -257,6 +259,7 @@ private:
bool _inSoundCallbackFlag = false;
int _soundCallbacksQueueSize = 0;
int _soundAlreadyInQueueCount = 0;
+ int _inUnqueueCallbackScripts = 0;
int _soundsDebugFrameCounter = 0;
int _scummOverrideFrequency = 0;
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 9a0abcdbbb6..561dc154434 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1794,7 +1794,7 @@ public:
byte VAR_SKIP_RESET_TALK_ACTOR = 0xFF; // Used in setActorCostume()
byte VAR_SOUND_CHANNEL = 0xFF; // Used in o_startSound()
- byte VAR_TALK_CHANNEL = 0xFF; // Used in startHETalkSound()
+ byte VAR_TALK_CHANNEL = 0xFF; // Used in playVoice()
byte VAR_SOUND_TOKEN_OFFSET = 0xFF; // Used in handleSoundFrame()
byte VAR_START_DYN_SOUND_CHANNELS = 0xFF; // Used in getNextDynamicChannel()
byte VAR_SOUND_CALLBACK_SCRIPT = 0xFF;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index c3bb9bb7dd1..f26b45da7a4 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -424,9 +424,10 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
buffer += 14;
if (_game.heversion >= 60) {
#ifdef ENABLE_HE
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
+ // TODO: Properly wire up Localizer to new sound system
+ ((SoundHE *)_sound)->playVoice(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset, digiTalkieLength);
#else
- ((SoundHE *)_sound)->startHETalkSound(digiTalkieOffset);
+ ((SoundHE *)_sound)->playVoice(digiTalkieOffset, digiTalkieLength);
#endif
} else {
_sound->talkSound(digiTalkieOffset, digiTalkieLength, DIGI_SND_MODE_TALKIE);
@@ -465,9 +466,9 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
const int charsetCode = (_game.heversion >= 80) ? 127 : 64;
uint32 digiTalkieOffset = 0;
- //uint32 digiTalkieLength = 0;
+ uint32 digiTalkieLength = 0;
int i, c = 0;
- char value[32];
+ char value[4096];
bool endLoop = false;
bool endText = false;
byte *buffer = _charsetBuffer + _charsetBufPos;
@@ -496,8 +497,9 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
i++;
}
value[i] = 0;
- //digiTalkieLength = atoi(value);
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
+ digiTalkieLength = atoi(value);
+ // TODO: Properly wire up Localizer to new sound system
+ ((SoundHE *)_sound)->playVoice(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset, digiTalkieLength);
break;
case 104:
_haveMsg = 0;
@@ -520,7 +522,8 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
value[i] = 0;
digiTalkieOffset = atoi(value);
//digiTalkieLength = 0;
- ((SoundHE *)_sound)->startHETalkSound(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset);
+ // TODO: Properly wire up Localizer to new sound system
+ ((SoundHE *)_sound)->playVoiceFile(value);
break;
case 119:
_haveMsg = 0xFF;
@@ -926,7 +929,7 @@ void ScummEngine::CHARSET_1() {
(_game.version == 7 && _haveMsg != 1)) {
if (_game.heversion >= 60) {
- if (_sound->isSoundRunning(1) == 0)
+ if (_sound->isSoundRunning(HSND_TALKIE_SLOT) == 0)
stopTalk();
} else {
if ((_sound->_digiSndMode & DIGI_SND_MODE_TALKIE) == 0)
diff --git a/engines/scumm/vars.cpp b/engines/scumm/vars.cpp
index 0a9652c5f70..c0dcce667ea 100644
--- a/engines/scumm/vars.cpp
+++ b/engines/scumm/vars.cpp
@@ -214,7 +214,7 @@ void ScummEngine_v70he::setupScummVars() {
VAR_CURRENTDRIVE = 0xFF;
VAR_MUSIC_TIMER = 0xFF;
- VAR_ERROR_FLAG = 8; // Alias of VAR_GAME_LOADED for now, which will get removed later
+ VAR_ERROR_FLAG = 8;
VAR_NUM_SOUND_CHANNELS = 9;
VAR_TALK_CHANNEL = 10;
VAR_SOUND_CHANNEL = 14;
@@ -230,6 +230,7 @@ void ScummEngine_v72he::setupScummVars() {
VAR_WALKTO_OBJ = 4;
VAR_RANDOM_NR = 5;
+ VAR_ERROR_FLAG = 8; // Alias of VAR_GAME_LOADED for now, which will get removed later
VAR_GAME_LOADED = 8;
VAR_EGO = 9;
VAR_NUM_ACTOR = 10;
Commit: 806ff5147a5426af10cc20ea5a6d57a86358bd81
https://github.com/scummvm/scummvm/commit/806ff5147a5426af10cc20ea5a6d57a86358bd81
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Suddenly... sound!
Moonbase Commander has finally started playing
some sounds. There are still some bugs here and there,
and the Miles mixer implementation is certainly not complete,
but hey, it works!
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/resource.cpp
engines/scumm/resource.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 83c7a0d6202..82cc6f11980 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -23,8 +23,9 @@
namespace Scumm {
-HEMixer::HEMixer(Audio::Mixer *mixer, bool useMilesSoundSystem) {
+HEMixer::HEMixer(Audio::Mixer *mixer, ScummEngine_v60he *vm, bool useMilesSoundSystem) {
_mixer = mixer;
+ _vm = vm;
_useMilesSoundSystem = useMilesSoundSystem;
}
@@ -57,10 +58,19 @@ bool HEMixer::isMixerDisabled() {
}
bool HEMixer::stopChannel(int channel) {
- return false;
+ if (_useMilesSoundSystem) {
+ return milesStopChannel(channel);
+ } else {
+ //return mixerStopChannel(channel);
+ }
+
+ return true;
}
void HEMixer::stopAllChannels() {
+ for (int i = 0; i < HSND_MAX_CHANNELS; i++) {
+ stopChannel(i);
+ }
}
bool HEMixer::changeChannelVolume(int channel, int volume, bool soft) {
@@ -140,6 +150,10 @@ bool HEMixer::changeChannelVolume(int channel, int newVolume, int soft_flag) {
}
void HEMixer::milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers) {
+ assert(channel >= 0 && channel < ARRAYSIZE(_milesChannels));
+
+ if (channel >= 0 && channel < ARRAYSIZE(_milesChannels))
+ _milesChannels[channel].startSpoolingChannel(filename, offset, flags, modifiers);
}
int HEMixer::hsFindSoundQueue(int sound) {
@@ -150,11 +164,212 @@ bool HEMixer::mixerStartChannel(int channel, int globType, int globNum, uint32 s
return false;
}
-bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 sound_data, uint32 offset, int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
- return false;
+bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 soundData, uint32 offset, int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
+ // Stop any running sound on the target channel,
+ // which is also going to trigger the appropriate callbacks
+ milesStopChannel(channel);
+
+ int audioDataLen = 0;
+ int compType = WAVE_FORMAT_PCM;
+ byte *audioData = milesGetAudioDataFromResource(globType, globNum, audioDataLen, compType);
+
+ byte streamFlags = 0;
+ if (bitsPerSample == 8) // 8 bit data is unsigned
+ streamFlags |= Audio::FLAG_UNSIGNED;
+ else if (bitsPerSample == 16) // 16 bit data is signed little endian
+ streamFlags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+ else if (bitsPerSample == 24) // 24 bit data is signed little endian
+ streamFlags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
+ else if (bitsPerSample == 4 && (compType == WAVE_FORMAT_IMA_ADPCM))
+ streamFlags |= Audio::FLAG_16BITS;
+
+ if (sampleChannels == 2)
+ streamFlags |= Audio::FLAG_STEREO;
+
+
+ if (audioData) {
+ _vm->_res->lock((ResType)globType, globNum);
+
+ _milesChannels[channel].dataOffset = soundData + offset;
+ _milesChannels[channel].lastPlayPosition = 0;
+ _milesChannels[channel].globType = globType;
+ _milesChannels[channel].globNum = globNum;
+
+ // Actually play the sound
+ if (flags & CHANNEL_LOOPING) {
+ _milesChannels[channel].playFlags = CHANNEL_LOOPING;
+
+ if (compType == WAVE_FORMAT_PCM) {
+ Audio::RewindableAudioStream *stream = Audio::makeRawStream(audioData, audioDataLen, frequency, streamFlags);
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel].audioHandle,
+ Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
+ } else {
+ error("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
+ }
+ } else {
+ _milesChannels[channel].playFlags = CHANNEL_EMPTY_FLAGS;
+
+ int newFrequency = (frequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+
+ _milesChannels[channel].m_modifiers.frequencyShift = modifiers.frequencyShift;
+ _milesChannels[channel].m_modifiers.volume = modifiers.volume;
+ _milesChannels[channel].m_modifiers.pan = modifiers.pan;
+
+ _milesChannels[channel].m_baseFrequency = frequency;
+
+ int scaledPan = (modifiers.pan != 64) ? 2 * modifiers.pan - 127 : 0;
+ if (compType == WAVE_FORMAT_PCM) {
+ Audio::SeekableAudioStream *stream = Audio::makeRawStream(audioData + offset, audioDataLen - offset, newFrequency, streamFlags);
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel].audioHandle,
+ stream, channel, modifiers.volume, scaledPan, DisposeAfterUse::NO);
+ } else {
+ error("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ }
+ }
+ }
+
+ return true;
+}
+
+bool HEMixer::milesStopChannel(int channel) {
+ milesStopAndCallback(channel, HSND_SOUND_STOPPED);
+
+ return true;
+}
+
+void HEMixer::milesStopAllSounds() {
+ for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
+ milesStopChannel(i);
+ }
}
void HEMixer::milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags) {
+ Audio::SoundHandle audioHandle = _milesChannels[channel].audioHandle;
+
+ debug(5, "HEMixer::milesModifySound(): modifying sound in channel %d, flags %d, vol %d, pan %d, freq %d",
+ channel, flags, modifiers.volume, modifiers.pan, modifiers.frequencyShift);
+
+ if (_mixer->isSoundHandleActive(audioHandle)) {
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)
+ _milesChannels[channel].m_modifiers.volume = modifiers.volume;
+
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)
+ _milesChannels[channel].m_modifiers.pan = modifiers.pan;
+
+ if ((flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL) || (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)) {
+ int scaledPan = (modifiers.pan != 64) ? 2 * modifiers.pan - 127 : 0;
+
+ _mixer->setChannelVolume(_milesChannels[channel].audioHandle, _milesChannels[channel].m_modifiers.volume);
+ _mixer->setChannelBalance(_milesChannels[channel].audioHandle, scaledPan);
+ }
+
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
+ _milesChannels[channel].m_modifiers.frequencyShift = modifiers.frequencyShift;
+ int newFrequency = (_milesChannels[channel].m_baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+ if (newFrequency)
+ _mixer->setChannelRate(_milesChannels[channel].audioHandle, newFrequency);
+ }
+ }
+}
+
+void HEMixer::milesStopAndCallback(int channel, int messageId) {
+ if (!_mixer->isSoundHandleActive(_milesChannels[channel].audioHandle) &&
+ !_milesChannels[channel].m_stream)
+ return;
+
+ if (_mixer->isSoundHandleActive(_milesChannels[channel].audioHandle)) {
+ // Stop the sound, and then unload it...
+ _mixer->stopHandle(_milesChannels[channel].audioHandle);
+ int globType = _milesChannels[channel].globType;
+ int globNum = _milesChannels[channel].globNum;
+
+ if (!_vm->_res->isOffHeap((ResType)globType, globNum)) {
+ _vm->_res->unlock((ResType)globType, globNum);
+
+ if (globType == rtSound && globNum == HSND_TALKIE_SLOT) {
+ // Voice files have to be manually purged
+ _vm->_res->nukeResource((ResType)globType, globNum);
+ }
+ }
+ } else {
+ // TODO: Stop the spooling file stream...
+ }
+
+ _milesChannels[channel].clearChannelData();
+
+ // Signal the sound engine that we've stopped a sound
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(messageId, channel);
+
+}
+
+void HEMixer::milesRestoreChannel(int channel) {
+ milesStopAndCallback(channel, HSND_SOUND_TIMEOUT);
+}
+
+void HEMixer::milesFeedMixer() {
+}
+
+bool HEMixer::milesPauseMixerSubSystem(bool paused) {
+ _mixerPaused = paused;
+
+ _mixer->pauseAll(_mixerPaused);
+
+ return true;
+}
+
+byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, int &dataLength, int &compType) {
+ byte *globPtr = _vm->getResourceAddress((ResType)globType, globNum);
+
+ if (globPtr == nullptr) {
+ error("HEMixer::milesGetAudioDataFromResource(): Glob(%d,%d) missing from heap", globType, globNum);
+ }
+
+ uint32 globId = READ_BE_UINT32(globPtr);
+
+ if (globId != MKTAG('W', 'S', 'O', 'U')) {
+ debug(5, "HEMixer::milesGetAudioDataFromResource(): Glob(%d,%d) - type '%s' - is not a WSOU (wrapped .wav) file", globType, globNum, tag2str(globId));
+ return nullptr;
+ }
+
+ // WSOU tag found, skip to the RIFF header...
+ globPtr += 8;
+ globId = READ_BE_UINT32(globPtr);
+
+ if (globId != MKTAG('R', 'I', 'F', 'F')) {
+ debug(5, "HEMixer::milesGetAudioDataFromResource(): Glob(%d,%d) - '%s' - is not a .wav file", globType, globNum, tag2str(globId));
+ return nullptr;
+ }
+
+ compType = READ_LE_UINT16(globPtr + 20); // Format type from the 'fmt ' block
+ dataLength = READ_LE_UINT32(globPtr + 40); // Length field of the 'data' block
+
+ if (compType != WAVE_FORMAT_PCM && compType != WAVE_FORMAT_IMA_ADPCM) {
+ debug("HEMixer::milesGetAudioDataFromResource(): .wav files must be PCM or IMA ADPCM. Unsupported .wav sound type %d.", compType);
+ return nullptr;
+ }
+
+ return globPtr + WAVE_RIFF_HEADER_LEN;
+}
+
+void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers) {
+ // TODO
+}
+
+void HEMilesChannel::clearChannelData() {
+ lastPlayPosition = 0;
+ playFlags = 0;
+ dataOffset = 0;
+ globType = 0;
+ globNum = 0;
+ m_fileHandle = nullptr;
+ m_stream = nullptr;
+ m_baseFrequency = 0;
+ m_modifiers.volume = HSND_MAX_VOLUME;
+ m_modifiers.pan = HSND_SOUND_PAN_CENTER;
+ m_modifiers.frequencyShift = HSND_BASE_FREQ_FACTOR;
+}
+
+void HEMilesChannel::closeFileHandle() {
}
} // End of namespace Scumm
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 0fdf610e495..bda195911e2 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -23,9 +23,13 @@
#define SCUMM_HE_MIXER_HE_H
#include "scumm/he/sound_he.h"
+#include "scumm/he/intern_he.h"
+#include "scumm/resource.h"
#include "common/util.h"
#include "common/file.h"
#include "common/debug.h"
+#include "audio/audiostream.h"
+#include "audio/decoders/raw.h"
namespace Audio {
class AudioStream;
@@ -46,15 +50,57 @@ namespace Scumm {
#define CHANNEL_CALLBACK_EARLY 0x00000080
#define CHANNEL_SOFT_REMIX 0x00000100
+#define MILES_MAX_CHANNELS 8
+
struct HESoundModifiers;
+class ScummEngine_v60he;
+
+struct MyModifiers {
+ int frequencyShift;
+ int pan;
+ int volume;
+};
+
+class HEMilesChannel {
+protected:
+ Common::File *m_fileHandle;
+ byte *m_globPtr;
+
+public:
+ MyModifiers m_modifiers;
+ Audio::AudioStream *m_stream;
+ int m_baseFrequency;
+ Audio::SoundHandle audioHandle;
+ uint32 lastPlayPosition;
+ uint32 playFlags;
+ int dataOffset;
+ int globType;
+ int globNum;
+
+ HEMilesChannel() {
+ clearChannelData();
+ }
+
+ ~HEMilesChannel() {
+ clearChannelData();
+ }
+
+ void startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers);
+ void clearChannelData();
+ void closeFileHandle();
+};
class HEMixer {
protected:
+ ScummEngine *_vm;
Audio::Mixer *_mixer;
- bool _useMilesSoundSystem;
+ bool _useMilesSoundSystem = false;
+ bool _mixerPaused = false;
+
+ HEMilesChannel _milesChannels[MILES_MAX_CHANNELS];
public:
- HEMixer(Audio::Mixer *mixer, bool useMiles);
+ HEMixer(Audio::Mixer *mixer, ScummEngine_v60he *vm, bool useMiles);
~HEMixer();
void *getMilesSoundSystemObject();
@@ -99,7 +145,14 @@ public:
int sampleLen, int bitsPerSample, int sampleChannels,
int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...);
+ bool milesStopChannel(int channel);
+ void milesStopAllSounds();
void milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags);
+ void milesStopAndCallback(int channel, int messageId);
+ void milesRestoreChannel(int channel);
+ void milesFeedMixer();
+ bool milesPauseMixerSubSystem(bool paused);
+ byte *milesGetAudioDataFromResource(int globType, int globNum, int &riffLength, int &compType);
};
} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index d55e539b2e6..fa797d67c02 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -62,7 +62,7 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
memset(_heChannel, 0, sizeof(_heChannel));
_heSoundChannels = new Audio::SoundHandle[8]();
_useMilesSoundSystem = parent->_game.id == GID_MOONBASE;
- _heMixer = new HEMixer(_mixer, _useMilesSoundSystem);
+ _heMixer = new HEMixer(_mixer, _vm, _useMilesSoundSystem);
}
SoundHE::~SoundHE() {
@@ -570,13 +570,14 @@ void SoundHE::digitalSoundCallback(int message, int channel) {
}
_inSoundCallbackFlag = true;
+ int sound = _heChannel[channel].sound;
switch (message) {
case HSND_SOUND_TIMEOUT:
case HSND_SOUND_ENDED:
case HSND_SOUND_STOPPED:
- if (_heChannel[channel].sound == HSND_TALKIE_SLOT) {
+ if (sound == HSND_TALKIE_SLOT) {
// In the original this was used to prevent an edge case bug
// which caused recursive resource accesses/allocations.
// I think there's no harm in doing that ourselves too...
@@ -589,7 +590,7 @@ void SoundHE::digitalSoundCallback(int message, int channel) {
_heChannel[channel].clearChannel();
- queueSoundCallbackScript(_heChannel[channel].sound, channel, message);
+ queueSoundCallbackScript(sound, channel, message);
break;
}
@@ -1212,6 +1213,7 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
int index;
uint32 hflags;
+ debug(5, "SoundHE::hsStartDigitalSound(): Starting sound %d with offset %d, on channel %d with flags %d", sound, offset, channel, flags);
hflags = (flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP) ? CHANNEL_LOOPING : 0;
hflags |= (flags & ScummEngine_v70he::HESndFlags::HE_SND_SOFT_SOUND) ? CHANNEL_SOFT_REMIX : 0;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 3e09a81052c..d6451f2f412 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -81,6 +81,7 @@ namespace Scumm {
#define WAVE_FORMAT_PCM 1
#define WAVE_FORMAT_IMA_ADPCM 17
+#define WAVE_RIFF_HEADER_LEN 44
#define HSND_RES_OFFSET_ID1 0 // uint32, DIGI or MIDI header
#define HSND_RES_OFFSET_LEN1 4 // uint32
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index aee73264769..e69dff29d0f 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -1049,6 +1049,12 @@ bool ResourceManager::isModified(ResType type, ResId idx) const {
return _types[type][idx].isModified();
}
+bool ResourceManager::isOffHeap(ResType type, ResId idx) const {
+ if (!validateResource("isOffHeap", type, idx))
+ return false;
+ return _types[type][idx].isOffHeap();
+}
+
bool ResourceManager::Resource::isModified() const {
return (_status & RS_MODIFIED) != 0;
}
diff --git a/engines/scumm/resource.h b/engines/scumm/resource.h
index 68fc01ca42d..5df84e36820 100644
--- a/engines/scumm/resource.h
+++ b/engines/scumm/resource.h
@@ -201,6 +201,7 @@ public:
void setModified(ResType type, ResId idx);
bool isModified(ResType type, ResId idx) const;
void setOffHeap(ResType type, ResId idx);
+ bool isOffHeap(ResType type, ResId idx) const;
void setOnHeap(ResType type, ResId idx);
/**
Commit: f73388136037c256a30a2f49e957ec34e557f583
https://github.com/scummvm/scummvm/commit/f73388136037c256a30a2f49e957ec34e557f583
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement Miles feedMixer() for Moonbase
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 82cc6f11980..070619972d0 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -88,6 +88,11 @@ bool HEMixer::pauseMixerSubSystem(bool paused) {
}
void HEMixer::feedMixer() {
+ if (_useMilesSoundSystem) {
+ milesFeedMixer();
+ } else {
+ //mixerFeedMixer();
+ }
}
int HEMixer::getChannelCurrentPosition(int channel) {
@@ -194,6 +199,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
_milesChannels[channel].lastPlayPosition = 0;
_milesChannels[channel].globType = globType;
_milesChannels[channel].globNum = globNum;
+ _milesChannels[channel].audioHandleActive = true;
// Actually play the sound
if (flags & CHANNEL_LOOPING) {
@@ -244,12 +250,10 @@ void HEMixer::milesStopAllSounds() {
}
void HEMixer::milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags) {
- Audio::SoundHandle audioHandle = _milesChannels[channel].audioHandle;
-
debug(5, "HEMixer::milesModifySound(): modifying sound in channel %d, flags %d, vol %d, pan %d, freq %d",
channel, flags, modifiers.volume, modifiers.pan, modifiers.frequencyShift);
- if (_mixer->isSoundHandleActive(audioHandle)) {
+ if (_milesChannels[channel].audioHandleActive) {
if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)
_milesChannels[channel].m_modifiers.volume = modifiers.volume;
@@ -270,16 +274,21 @@ void HEMixer::milesModifySound(int channel, int offset, HESoundModifiers modifie
_mixer->setChannelRate(_milesChannels[channel].audioHandle, newFrequency);
}
}
+
+ if (_milesChannels[channel].m_stream) {
+ // TODO
+ }
}
void HEMixer::milesStopAndCallback(int channel, int messageId) {
- if (!_mixer->isSoundHandleActive(_milesChannels[channel].audioHandle) &&
+ if (!_milesChannels[channel].audioHandleActive &&
!_milesChannels[channel].m_stream)
return;
- if (_mixer->isSoundHandleActive(_milesChannels[channel].audioHandle)) {
+ if (_milesChannels[channel].audioHandleActive) {
// Stop the sound, and then unload it...
_mixer->stopHandle(_milesChannels[channel].audioHandle);
+
int globType = _milesChannels[channel].globType;
int globNum = _milesChannels[channel].globNum;
@@ -307,6 +316,32 @@ void HEMixer::milesRestoreChannel(int channel) {
}
void HEMixer::milesFeedMixer() {
+ if (_mixerPaused) {
+ return;
+ }
+
+ // TODO: service spooling streams?
+
+ for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
+ bool soundDone = false;
+
+
+ if (_milesChannels[i].audioHandleActive) {
+ soundDone = !_mixer->isSoundHandleActive(_milesChannels[i].audioHandle);
+ }
+
+ // TODO: spooled case: if this is a streamed sound check if the stream has ended
+ if (_milesChannels[i].m_stream) {
+
+ }
+
+ if (soundDone)
+ milesStopAndCallback(i, HSND_SOUND_ENDED);
+ }
+
+ if (!_vm->_insideCreateResource) {
+ ((SoundHE *)_vm->_sound)->unqueueSoundCallbackScripts();
+ }
}
bool HEMixer::milesPauseMixerSubSystem(bool paused) {
@@ -356,6 +391,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
}
void HEMilesChannel::clearChannelData() {
+ audioHandleActive = false;
lastPlayPosition = 0;
playFlags = 0;
dataOffset = 0;
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index bda195911e2..5e7a73e7df5 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -76,6 +76,7 @@ public:
int dataOffset;
int globType;
int globNum;
+ bool audioHandleActive = false;
HEMilesChannel() {
clearChannelData();
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index fa797d67c02..65c4ee2a5f8 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -510,6 +510,10 @@ void SoundHE::handleSoundFrame() {
checkSoundTimeouts();
}
+void SoundHE::feedMixer() {
+ _heMixer->feedMixer();
+}
+
void SoundHE::unqueueSoundCallbackScripts() {
if (_inUnqueueCallbackScripts)
return;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index d6451f2f412..9f11882139d 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -235,6 +235,7 @@ public:
void hsStopDigitalSound(int sound);
void handleSoundFrame();
+ void feedMixer();
void unqueueSoundCallbackScripts();
void checkSoundTimeouts();
void digitalSoundCallback(int message, int channel);
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 809a4a904b6..a4e3e19edc6 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2410,6 +2410,10 @@ Common::Error ScummEngine::go() {
// Run the main loop
scummLoop(delta);
+ if (_game.heversion >= 70) {
+ ((SoundHE *)_sound)->feedMixer();
+ }
+
if (shouldQuit()) {
// TODO: Maybe perform an autosave on exit?
runQuitScript();
Commit: a18e86d077b3d38c8d6b0c9deb3143c4a6deab4a
https://github.com/scummvm/scummvm/commit/a18e86d077b3d38c8d6b0c9deb3143c4a6deab4a
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix clicking sound at the end of wav files
Changed paths:
engines/scumm/sound.cpp
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 2c649b091ac..3916d14c892 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -1754,12 +1754,12 @@ int ScummEngine::readSoundResource(ResId idx) {
case MKTAG('D','I','G','I'):
case MKTAG('C','r','e','a'):
case 0x460e200d: // WORKAROUND bug #2221
- // Some HE games take WAV files and puts a WSOU header around it
+ // Some HE games take WAV files and put a WSOU header around them
if (_game.heversion > 0 && basetag == MKTAG('R','I','F','F')) {
_fileHandle->seek(-4, SEEK_CUR);
// The chunksize field in a RIFF header is a LE field
- total_size = _fileHandle->readUint32LE();
+ total_size = _fileHandle->readUint32LE() + 8;
// Make space for the WSOU tag and for the size field
ptr = _res->createResource(rtSound, idx, total_size + 8);
Commit: 0ef7127dd87770cb27be5e4407c256d9b4f33070
https://github.com/scummvm/scummvm/commit/0ef7127dd87770cb27be5e4407c256d9b4f33070
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix in-battle music for Moonbase Commander
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 65c4ee2a5f8..0b83f762ac3 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -341,12 +341,15 @@ bool SoundHE::isSoundCodeUsed(int sound) {
}
int SoundHE::getChannelPosition(int channel) {
+ int soundPos;
if (_vm->_game.heversion >= 80) {
int frequency = _vm->_game.heversion >= 95 ? _heChannel[channel].frequency : HSND_DEFAULT_FREQUENCY;
- return (_vm->getHETimer(HSND_TIMER_SLOT + channel) * frequency) / 1000;
+ soundPos = (int)(((uint64)_vm->getHETimer(HSND_TIMER_SLOT + channel) * (uint64)frequency) / 1000);
} else {
- return _heMixer->getChannelCurrentPosition(channel);
+ soundPos = _heMixer->getChannelCurrentPosition(channel);
}
+
+ return soundPos;
}
int SoundHE::getSoundPosition(int sound) {
@@ -1521,6 +1524,7 @@ void SoundHE::createSound(int baseSound, int sound) {
int baseSndLeft, sndSize, channel;
if (sound == -1) {
+ debug(5, "SoundHE::createSound(): Resetting append position...");
_createSndLastAppend = 0;
_createSndLastPos = 0;
_baseSndSize = 0;
@@ -1534,24 +1538,22 @@ void SoundHE::createSound(int baseSound, int sound) {
_baseSndSize = 0;
}
+ debug(5, "SoundHE::createSound(): Appending sound %d to base sound %d", sound, baseSound);
+
_vm->ensureResourceLoaded(rtSound, baseSound);
_vm->ensureResourceLoaded(rtSound, sound);
+ _vm->_res->lock(rtSound, baseSound);
+ _vm->_res->lock(rtSound, sound);
baseSndPtr = (byte *)_vm->getResourceAddress(rtSound, baseSound);
sndPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
channel = hsFindSoundChannel(baseSound);
- //Check for a RIFF block here and set a flag.
+ bool sndIsWav = findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr) != nullptr;
- bool fIsWav = false;
-
- if (findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr)) {
- fIsWav = true;
- }
-
- if (!fIsWav) {
- // For non-WAV files we have to deals with sound variables (i.e. skip them :-) )
+ if (!sndIsWav) {
+ // For non-WAV files we have to deal with sound variables (i.e. skip them :-) )
baseSndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
if (baseSndSbngPtr != nullptr) {
@@ -1587,12 +1589,11 @@ void SoundHE::createSound(int baseSound, int sound) {
}
}
-
byte *pRiff = nullptr;
byte *pData = nullptr;
// Find where the actual sound data is located...
- if (fIsWav) {
+ if (sndIsWav) {
baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", baseSound);
@@ -1641,6 +1642,9 @@ void SoundHE::createSound(int baseSound, int sound) {
_createSndLastPos += sndSize;
+ _vm->_res->unlock(rtSound, baseSound);
+ _vm->_res->unlock(rtSound, sound);
+
_heMixer->softRemixAllChannels();
}
Commit: 4babb8ffd36dfa21e5873f807ea852b144a5c3bd
https://github.com/scummvm/scummvm/commit/4babb8ffd36dfa21e5873f807ea852b144a5c3bd
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix HE70 return value for getSoundPosition()
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 0b83f762ac3..05ca0c30ef0 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -358,7 +358,7 @@ int SoundHE::getSoundPosition(int sound) {
if (channel != -1) {
return getChannelPosition(channel);
} else {
- return 0;
+ return (_vm->_game.heversion > 72) ? 0 : channel;
}
}
Commit: 14bc03c4e178a7f18e729b3e7b7649c357180d01
https://github.com/scummvm/scummvm/commit/14bc03c4e178a7f18e729b3e7b7649c357180d01
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Full audio support for Moonbase Commander
...at least I hope so.
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 070619972d0..d06498f0220 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -138,6 +138,13 @@ bool HEMixer::startChannelNew(
return true;
}
+void HEMixer::serviceAllStreams() {
+ for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
+ if (_milesChannels[i]._stream.streamObj != nullptr)
+ _milesChannels[i].serviceStream();
+ }
+}
+
bool HEMixer::startChannel(int channel, int globType, int globNum, uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...) {
return false;
}
@@ -158,7 +165,7 @@ void HEMixer::milesStartSpoolingChannel(int channel, const char *filename, long
assert(channel >= 0 && channel < ARRAYSIZE(_milesChannels));
if (channel >= 0 && channel < ARRAYSIZE(_milesChannels))
- _milesChannels[channel].startSpoolingChannel(filename, offset, flags, modifiers);
+ _milesChannels[channel].startSpoolingChannel(filename, offset, flags, modifiers, _mixer);
}
int HEMixer::hsFindSoundQueue(int sound) {
@@ -170,64 +177,73 @@ bool HEMixer::mixerStartChannel(int channel, int globType, int globNum, uint32 s
}
bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 soundData, uint32 offset, int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
- // Stop any running sound on the target channel,
- // which is also going to trigger the appropriate callbacks
+ // This function executes either a one-shot or a looping sfx/voice file
+ // which will entirely fit in RAM (as opposed to spooling sounds)
+ debug(5, "HEMixer::milesStartChannel(): Starting sound with resource %d in channel %d, modifiers v %d p %d f %d",
+ globNum, channel, modifiers.volume, modifiers.pan, modifiers.frequencyShift);
+
+ // Stop any running sound on the target channel; this
+ // is also going to trigger the appropriate callbacks
milesStopChannel(channel);
+ // Fetch the audio format for the target sound, and fill out
+ // the format fields on our milesChannel
int audioDataLen = 0;
int compType = WAVE_FORMAT_PCM;
byte *audioData = milesGetAudioDataFromResource(globType, globNum, audioDataLen, compType);
- byte streamFlags = 0;
- if (bitsPerSample == 8) // 8 bit data is unsigned
- streamFlags |= Audio::FLAG_UNSIGNED;
- else if (bitsPerSample == 16) // 16 bit data is signed little endian
- streamFlags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
- else if (bitsPerSample == 24) // 24 bit data is signed little endian
- streamFlags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
- else if (bitsPerSample == 4 && (compType == WAVE_FORMAT_IMA_ADPCM))
- streamFlags |= Audio::FLAG_16BITS;
-
- if (sampleChannels == 2)
- streamFlags |= Audio::FLAG_STEREO;
-
+ _milesChannels[channel]._bitsPerSample = bitsPerSample;
+ _milesChannels[channel]._numChannels = sampleChannels;
+ _milesChannels[channel]._dataFormat = compType;
if (audioData) {
_vm->_res->lock((ResType)globType, globNum);
- _milesChannels[channel].dataOffset = soundData + offset;
- _milesChannels[channel].lastPlayPosition = 0;
- _milesChannels[channel].globType = globType;
- _milesChannels[channel].globNum = globNum;
- _milesChannels[channel].audioHandleActive = true;
+ // Fill out some other information about the sound
+ _milesChannels[channel]._dataOffset = soundData + offset;
+ _milesChannels[channel]._lastPlayPosition = 0;
+ _milesChannels[channel]._globType = globType;
+ _milesChannels[channel]._globNum = globNum;
+
+ // This flag signals that there's an active sound in our channel!
+ _milesChannels[channel]._audioHandleActive = true;
- // Actually play the sound
+ // Play the sound, whether in one-shot fashion or as a loop
if (flags & CHANNEL_LOOPING) {
- _milesChannels[channel].playFlags = CHANNEL_LOOPING;
+ _milesChannels[channel]._playFlags = CHANNEL_LOOPING;
+ // Looping sounds don't care for modifiers!
if (compType == WAVE_FORMAT_PCM) {
- Audio::RewindableAudioStream *stream = Audio::makeRawStream(audioData, audioDataLen, frequency, streamFlags);
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel].audioHandle,
+ Audio::RewindableAudioStream *stream =
+ Audio::makeRawStream(audioData, audioDataLen, frequency, _milesChannels[channel].getOutputFlags());
+
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
} else {
error("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
}
} else {
- _milesChannels[channel].playFlags = CHANNEL_EMPTY_FLAGS;
+ _milesChannels[channel]._playFlags = CHANNEL_EMPTY_FLAGS;
+ // Fill out the modifiers
int newFrequency = (frequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
- _milesChannels[channel].m_modifiers.frequencyShift = modifiers.frequencyShift;
- _milesChannels[channel].m_modifiers.volume = modifiers.volume;
- _milesChannels[channel].m_modifiers.pan = modifiers.pan;
+ _milesChannels[channel]._modifiers.frequencyShift = modifiers.frequencyShift;
+ _milesChannels[channel]._modifiers.volume = modifiers.volume;
+ _milesChannels[channel]._modifiers.pan = modifiers.pan;
+
+ // Set the target sample rate for the playback
+ _milesChannels[channel]._baseFrequency = frequency;
- _milesChannels[channel].m_baseFrequency = frequency;
+ // Transform the range of the pan value from [0, 127] to [-127, 127]
+ int scaledPan = (_milesChannels[channel]._modifiers.pan != 64) ? 2 * _milesChannels[channel]._modifiers.pan - 127 : 0;
- int scaledPan = (modifiers.pan != 64) ? 2 * modifiers.pan - 127 : 0;
+ // Play the one-shot sound!
if (compType == WAVE_FORMAT_PCM) {
- Audio::SeekableAudioStream *stream = Audio::makeRawStream(audioData + offset, audioDataLen - offset, newFrequency, streamFlags);
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel].audioHandle,
- stream, channel, modifiers.volume, scaledPan, DisposeAfterUse::NO);
+ Audio::SeekableAudioStream *stream =
+ Audio::makeRawStream(audioData + offset, audioDataLen - offset, newFrequency, _milesChannels[channel].getOutputFlags());
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
+ stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
} else {
error("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
}
@@ -250,47 +266,70 @@ void HEMixer::milesStopAllSounds() {
}
void HEMixer::milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags) {
+ // This function usually serves as a parameter fade handler (e.g. fading out volume in Moonbase)
debug(5, "HEMixer::milesModifySound(): modifying sound in channel %d, flags %d, vol %d, pan %d, freq %d",
channel, flags, modifiers.volume, modifiers.pan, modifiers.frequencyShift);
- if (_milesChannels[channel].audioHandleActive) {
+ // Handling for non-streamed sound effects
+ if (_milesChannels[channel]._audioHandleActive) {
if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)
- _milesChannels[channel].m_modifiers.volume = modifiers.volume;
+ _milesChannels[channel]._modifiers.volume = modifiers.volume;
if (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)
- _milesChannels[channel].m_modifiers.pan = modifiers.pan;
+ _milesChannels[channel]._modifiers.pan = modifiers.pan;
if ((flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL) || (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN)) {
- int scaledPan = (modifiers.pan != 64) ? 2 * modifiers.pan - 127 : 0;
+ // Transform the range of the pan value from [0, 127] to [-127, 127]
+ int scaledPan = (_milesChannels[channel]._modifiers.pan != 64) ? 2 * _milesChannels[channel]._modifiers.pan - 127 : 0;
- _mixer->setChannelVolume(_milesChannels[channel].audioHandle, _milesChannels[channel].m_modifiers.volume);
- _mixer->setChannelBalance(_milesChannels[channel].audioHandle, scaledPan);
+ _mixer->setChannelVolume(_milesChannels[channel]._audioHandle, _milesChannels[channel]._modifiers.volume);
+ _mixer->setChannelBalance(_milesChannels[channel]._audioHandle, scaledPan);
}
if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
- _milesChannels[channel].m_modifiers.frequencyShift = modifiers.frequencyShift;
- int newFrequency = (_milesChannels[channel].m_baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+ _milesChannels[channel]._modifiers.frequencyShift = modifiers.frequencyShift;
+ int newFrequency = (_milesChannels[channel]._baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
if (newFrequency)
- _mixer->setChannelRate(_milesChannels[channel].audioHandle, newFrequency);
+ _mixer->setChannelRate(_milesChannels[channel]._audioHandle, newFrequency);
}
}
- if (_milesChannels[channel].m_stream) {
- // TODO
+ // Handling for streamed music
+ if (_milesChannels[channel]._stream.streamObj) {
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL) {
+ _milesChannels[channel]._modifiers.volume = modifiers.volume;
+ _mixer->setChannelVolume(_milesChannels[channel]._stream.streamHandle, _milesChannels[channel]._modifiers.volume);
+ }
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_PAN) {
+ _milesChannels[channel]._modifiers.pan = modifiers.pan;
+ // Transform the range of the pan value from [0, 127] to [-127, 127]
+ int scaledPan = (_milesChannels[channel]._modifiers.pan != 64) ? 2 * _milesChannels[channel]._modifiers.pan - 127 : 0;
+ _mixer->setChannelBalance(_milesChannels[channel]._stream.streamHandle, scaledPan);
+ }
+
+ if (flags & ScummEngine_v70he::HESndFlags::HE_SND_FREQUENCY) {
+ _milesChannels[channel]._modifiers.frequencyShift = modifiers.frequencyShift;
+ int newFrequency = (_milesChannels[channel]._baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+ if (newFrequency)
+ _mixer->setChannelRate(_milesChannels[channel]._stream.streamHandle, newFrequency);
+ }
}
}
void HEMixer::milesStopAndCallback(int channel, int messageId) {
- if (!_milesChannels[channel].audioHandleActive &&
- !_milesChannels[channel].m_stream)
+ // In here we check if we can actually stop the target channel,
+ // and if so, we call the script callback.
+
+ if (!_milesChannels[channel]._audioHandleActive && !_milesChannels[channel]._stream.streamObj) {
return;
+ }
- if (_milesChannels[channel].audioHandleActive) {
+ if (_milesChannels[channel]._audioHandleActive) { // Non streamed sounds
// Stop the sound, and then unload it...
- _mixer->stopHandle(_milesChannels[channel].audioHandle);
+ _mixer->stopHandle(_milesChannels[channel]._audioHandle);
- int globType = _milesChannels[channel].globType;
- int globNum = _milesChannels[channel].globNum;
+ int globType = _milesChannels[channel]._globType;
+ int globNum = _milesChannels[channel]._globNum;
if (!_vm->_res->isOffHeap((ResType)globType, globNum)) {
_vm->_res->unlock((ResType)globType, globNum);
@@ -300,15 +339,19 @@ void HEMixer::milesStopAndCallback(int channel, int messageId) {
_vm->_res->nukeResource((ResType)globType, globNum);
}
}
- } else {
- // TODO: Stop the spooling file stream...
+ } else { // Streamed music
+ _mixer->stopHandle(_milesChannels[channel]._stream.streamHandle);
+ _milesChannels[channel]._stream.streamObj->finish();
+
+ if (_milesChannels[channel]._stream.fileHandle)
+ _milesChannels[channel]._stream.fileHandle->close();
}
+ // Clean up the channel
_milesChannels[channel].clearChannelData();
// Signal the sound engine that we've stopped a sound
((SoundHE *)_vm->_sound)->digitalSoundCallback(messageId, channel);
-
}
void HEMixer::milesRestoreChannel(int channel) {
@@ -320,25 +363,28 @@ void HEMixer::milesFeedMixer() {
return;
}
- // TODO: service spooling streams?
+ // Feed the audio streams
+ serviceAllStreams();
+ // Check for any sound which has finished playing and call the milesStopAndCallback function
for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
bool soundDone = false;
-
- if (_milesChannels[i].audioHandleActive) {
- soundDone = !_mixer->isSoundHandleActive(_milesChannels[i].audioHandle);
+ if (_milesChannels[i]._audioHandleActive) {
+ soundDone = !_mixer->isSoundHandleActive(_milesChannels[i]._audioHandle);
}
- // TODO: spooled case: if this is a streamed sound check if the stream has ended
- if (_milesChannels[i].m_stream) {
-
+ if (_milesChannels[i]._stream.streamObj) {
+ soundDone |= _milesChannels[i]._stream.streamObj->endOfStream();
+ soundDone |= !_mixer->isSoundHandleActive(_milesChannels[i]._stream.streamHandle);
}
- if (soundDone)
+ if (soundDone) {
milesStopAndCallback(i, HSND_SOUND_ENDED);
+ }
}
+ // Finally, check the callback queue and execute any pending callback script
if (!_vm->_insideCreateResource) {
((SoundHE *)_vm->_sound)->unqueueSoundCallbackScripts();
}
@@ -353,6 +399,9 @@ bool HEMixer::milesPauseMixerSubSystem(bool paused) {
}
byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, int &dataLength, int &compType) {
+ // This function is used for non streamed sound effects and voice files,
+ // and fetches metadata for the target sound resource
+
byte *globPtr = _vm->getResourceAddress((ResType)globType, globNum);
if (globPtr == nullptr) {
@@ -386,26 +435,205 @@ byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, int &dat
return globPtr + WAVE_RIFF_HEADER_LEN;
}
-void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers) {
- // TODO
+void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers, Audio::Mixer *mixer) {
+ // This function sets up a stream for the target spooling music file.
+ // applies modifiers, and begins playing said stream
+
+ // We use this auxiliary struct representing a RIFF header for two things:
+ // - Tidier code;
+ // - File read consistency checks
+ //
+ // I certainly could have done without it, but I feel the code is
+ // more readable and comprehensible this way...
+ struct WaveHeader {
+ uint32 riffTag;
+ uint32 riffSize;
+ uint32 waveTag;
+ uint32 fmtTag;
+ uint32 fmtSize;
+
+ // Format subchunk
+ uint16 wFormatTag;
+ uint16 wChannels;
+ uint32 dwSamplesPerSec;
+ uint32 dwAvgBytesPerSec;
+ uint16 wBlockAlign;
+ uint16 wBitsPerSample;
+ };
+
+ WaveHeader waveHeader;
+
+ // Open the music bundle file and then seek to the target sound
+ _stream.fileHandle = new Common::File();
+
+ if (!_stream.fileHandle->open(filename)) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Couldn't open spooling file '%s'", filename);
+ return;
+ }
+
+ _stream.fileHandle->seek(offset, SEEK_CUR);
+ if (_stream.fileHandle->pos() != offset) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Couldn't seek file %s to offset %d", filename, offset);
+ _stream.fileHandle->close();
+ return;
+ }
+
+ // Extract the usual metadata information from the header, for later usage
+ int beginningPos = _stream.fileHandle->pos();
+
+ waveHeader.riffTag = _stream.fileHandle->readUint32BE();
+ waveHeader.riffSize = _stream.fileHandle->readUint32LE();
+ waveHeader.waveTag = _stream.fileHandle->readUint32BE();
+ waveHeader.fmtTag = _stream.fileHandle->readUint32BE();
+ waveHeader.fmtSize = _stream.fileHandle->readUint32LE();
+
+ waveHeader.wFormatTag = _stream.fileHandle->readUint16LE();
+ waveHeader.wChannels = _stream.fileHandle->readUint16LE();
+ waveHeader.dwSamplesPerSec = _stream.fileHandle->readUint32LE();
+ waveHeader.dwAvgBytesPerSec = _stream.fileHandle->readUint32LE();
+ waveHeader.wBlockAlign = _stream.fileHandle->readUint16LE();
+ waveHeader.wBitsPerSample = _stream.fileHandle->readUint16LE();
+
+ // Some safety checks...
+ if (_stream.fileHandle->pos() - beginningPos != sizeof(waveHeader)) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Couldn't read RIFF header correctly");
+ _stream.fileHandle->close();
+ return;
+ }
+
+ if (waveHeader.riffTag != MKTAG('R', 'I', 'F', 'F')) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Expected RIFF tag, found %s instead", tag2str(waveHeader.riffTag));
+ return;
+ }
+
+ if (waveHeader.waveTag != MKTAG('W', 'A', 'V', 'E')) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Expected WAVE tag, found %s instead", tag2str(waveHeader.waveTag));
+ return;
+ }
+
+ if (waveHeader.fmtTag != MKTAG('f', 'm', 't', ' ')) {
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Expected fmt tag, found %s instead", tag2str(waveHeader.fmtTag));
+ return;
+ }
+
+ // Fill out the relevant metadata for our channel
+ _modifiers.volume = modifiers.volume;
+ _modifiers.pan = modifiers.pan;
+ _modifiers.frequencyShift = modifiers.frequencyShift;
+
+ _dataFormat = waveHeader.wFormatTag;
+ _blockAlign = waveHeader.wBlockAlign;
+ _numChannels = waveHeader.wChannels;
+ _bitsPerSample = waveHeader.wBitsPerSample;
+ _baseFrequency = waveHeader.dwSamplesPerSec;
+
+ // Transform the range of the pan value from [0, 127] to [-127, 127]
+ int scaledPan = (_modifiers.pan != 64) ? 2 * _modifiers.pan - 127 : 0;
+
+ // Finally create our stream and play it!
+ _stream.streamObj = Audio::makeQueuingAudioStream(_baseFrequency, (waveHeader.wChannels > 1));
+ mixer->playStream(Audio::Mixer::kPlainSoundType, &_stream.streamHandle, _stream.streamObj, -1, 255, 0, DisposeAfterUse::NO);
+
+ // Apply the modifiers and the loop flag,if available
+ mixer->setChannelVolume(_stream.streamHandle, _modifiers.volume);
+ mixer->setChannelBalance(_stream.streamHandle, scaledPan);
+ int newFrequency = (_baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+ if (newFrequency)
+ mixer->setChannelRate(_stream.streamHandle, newFrequency);
+
+ _stream.loopFlag = flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP;
+
+ // And now we help out the stream by feeding the first bytes of data to it
+ _stream.fileHandle->readUint32BE(); // Skip 'data' tag
+
+ _stream.dataLength = _stream.fileHandle->readUint32LE();
+ _stream.curDataPos = 0;
+ _stream.dataOffset = _stream.fileHandle->pos();
+
+ if (_dataFormat == WAVE_FORMAT_PCM) {
+ // Saturate the stream queue with the beginning audio data
+ for (int i = 0; i < MILES_MAX_QUEUED_STREAMS; i++)
+ serviceStream();
+ } else if (_dataFormat == WAVE_FORMAT_IMA_ADPCM) {
+ error("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ } else {
+ debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %d",
+ _dataFormat, filename, offset);
+ }
}
void HEMilesChannel::clearChannelData() {
- audioHandleActive = false;
- lastPlayPosition = 0;
- playFlags = 0;
- dataOffset = 0;
- globType = 0;
- globNum = 0;
- m_fileHandle = nullptr;
- m_stream = nullptr;
- m_baseFrequency = 0;
- m_modifiers.volume = HSND_MAX_VOLUME;
- m_modifiers.pan = HSND_SOUND_PAN_CENTER;
- m_modifiers.frequencyShift = HSND_BASE_FREQ_FACTOR;
+ _audioHandleActive = false;
+ _lastPlayPosition = 0;
+ _playFlags = 0;
+ _dataOffset = 0;
+ _globType = 0;
+ _globNum = 0;
+
+ _baseFrequency = 0;
+ _modifiers.volume = HSND_MAX_VOLUME;
+ _modifiers.pan = HSND_SOUND_PAN_CENTER;
+ _modifiers.frequencyShift = HSND_BASE_FREQ_FACTOR;
+
+ closeFileHandle();
+ _stream.fileHandle = nullptr;
+ _stream.streamObj = nullptr;
+ _stream.loopFlag = false;
+ _stream.dataLength = 0;
+ _stream.curDataPos = 0;
+ _stream.dataOffset = 0;
+
+ _bitsPerSample = 8;
+ _numChannels = 1;
+ _dataFormat = WAVE_FORMAT_PCM;
}
void HEMilesChannel::closeFileHandle() {
+ if (_stream.fileHandle && _stream.fileHandle->isOpen())
+ _stream.fileHandle->close();
+}
+
+byte HEMilesChannel::getOutputFlags() {
+ byte streamFlags = 0;
+ if (_bitsPerSample == 8) // 8 bit data is unsigned
+ streamFlags |= Audio::FLAG_UNSIGNED;
+ else if (_bitsPerSample == 16) // 16 bit data is signed little endian
+ streamFlags |= (Audio::FLAG_16BITS | Audio::FLAG_LITTLE_ENDIAN);
+ else if (_bitsPerSample == 24) // 24 bit data is signed little endian
+ streamFlags |= (Audio::FLAG_24BITS | Audio::FLAG_LITTLE_ENDIAN);
+ else if (_bitsPerSample == 4 && (_dataFormat == WAVE_FORMAT_IMA_ADPCM))
+ streamFlags |= Audio::FLAG_16BITS;
+
+ if (_numChannels == 2)
+ streamFlags |= Audio::FLAG_STEREO;
+
+ return streamFlags;
+}
+
+void HEMilesChannel::serviceStream() {
+ // This is called at each frame, to ensure that the target stream doesn't starve
+ if (_stream.streamObj->numQueuedStreams() < MILES_MAX_QUEUED_STREAMS) {
+ int sizeToRead = MIN<int>(MILES_CHUNK_SIZE * _blockAlign, _stream.dataLength - _stream.curDataPos);
+ bool reachedTheEnd = sizeToRead < MILES_CHUNK_SIZE * _blockAlign;
+
+ byte *buffer = (byte *)malloc(sizeToRead);
+ if (sizeToRead > 0 && buffer != nullptr) {
+ int readBytes = _stream.fileHandle->read(buffer, sizeToRead);
+ _stream.curDataPos += readBytes;
+ _stream.streamObj->queueBuffer(buffer, readBytes, DisposeAfterUse::YES, getOutputFlags());
+ }
+
+ if (reachedTheEnd) {
+ if (_stream.loopFlag) {
+ // Rewind the stream...
+ _stream.curDataPos = 0;
+ _stream.fileHandle->seek(_stream.dataOffset, SEEK_SET);
+ } else {
+ // Mark the stream as finished...
+ _stream.streamObj->finish();
+ }
+ }
+ }
}
} // End of namespace Scumm
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 5e7a73e7df5..9f97c191361 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -51,11 +51,13 @@ namespace Scumm {
#define CHANNEL_SOFT_REMIX 0x00000100
#define MILES_MAX_CHANNELS 8
+#define MILES_CHUNK_SIZE 4096
+#define MILES_MAX_QUEUED_STREAMS 16
struct HESoundModifiers;
class ScummEngine_v60he;
-struct MyModifiers {
+struct MilesModifiers {
int frequencyShift;
int pan;
int volume;
@@ -63,20 +65,32 @@ struct MyModifiers {
class HEMilesChannel {
protected:
- Common::File *m_fileHandle;
- byte *m_globPtr;
+ struct MilesStream {
+ Audio::QueuingAudioStream *streamObj = nullptr;
+ Audio::SoundHandle streamHandle;
+ bool loopFlag = false;
+ Common::File *fileHandle = nullptr;
+ uint32 dataLength = 0;
+ uint32 curDataPos = 0;
+ uint32 dataOffset = 0;
+ };
public:
- MyModifiers m_modifiers;
- Audio::AudioStream *m_stream;
- int m_baseFrequency;
- Audio::SoundHandle audioHandle;
- uint32 lastPlayPosition;
- uint32 playFlags;
- int dataOffset;
- int globType;
- int globNum;
- bool audioHandleActive = false;
+ MilesModifiers _modifiers;
+ MilesStream _stream;
+ int _baseFrequency;
+ Audio::SoundHandle _audioHandle;
+ bool _audioHandleActive = false;
+ uint32 _lastPlayPosition;
+ uint32 _playFlags;
+ int _dataOffset;
+ int _globType;
+ int _globNum;
+
+ uint16 _blockAlign = 0;
+ uint16 _numChannels = 1;
+ uint16 _bitsPerSample = 8;
+ uint16 _dataFormat = 1;
HEMilesChannel() {
clearChannelData();
@@ -86,9 +100,11 @@ public:
clearChannelData();
}
- void startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers);
+ void startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers, Audio::Mixer *mixer);
void clearChannelData();
void closeFileHandle();
+ void serviceStream();
+ byte getOutputFlags();
};
class HEMixer {
@@ -118,6 +134,7 @@ public:
void premixUntilCritical();
bool pauseMixerSubSystem(bool paused);
void feedMixer();
+ void serviceAllStreams();
int getChannelCurrentPosition(int channel);
bool startChannelNew(
Commit: 4d7d4a870697e2d164169322ad3f5cc4e012814e
https://github.com/scummvm/scummvm/commit/4d7d4a870697e2d164169322ad3f5cc4e012814e
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Add preliminary Miles support for other games
Also, fix the o_isSoundRunning() opcode for HE games
Changed paths:
engines/scumm/he/intern_he.h
engines/scumm/he/mixer_he.cpp
engines/scumm/he/script_v100he.cpp
engines/scumm/he/script_v60he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/sound.h
diff --git a/engines/scumm/he/intern_he.h b/engines/scumm/he/intern_he.h
index ec25fd0a97a..b4f57e09350 100644
--- a/engines/scumm/he/intern_he.h
+++ b/engines/scumm/he/intern_he.h
@@ -127,6 +127,7 @@ protected:
void o60_rename();
void o60_writeFile();
void o60_soundOps();
+ void o60_isSoundRunning();
void o60_seekFilePos();
void o60_localizeArrayToScript();
void o60_redimArray();
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index d06498f0220..f1b619b3971 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -220,7 +220,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
} else {
- error("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
+ warning("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
}
} else {
_milesChannels[channel]._playFlags = CHANNEL_EMPTY_FLAGS;
@@ -245,7 +245,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
} else {
- error("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ warning("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
}
}
}
@@ -555,7 +555,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
for (int i = 0; i < MILES_MAX_QUEUED_STREAMS; i++)
serviceStream();
} else if (_dataFormat == WAVE_FORMAT_IMA_ADPCM) {
- error("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ warning("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
} else {
debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %d",
_dataFormat, filename, offset);
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 74d6901ccfc..82a089dae64 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -302,7 +302,7 @@ void ScummEngine_v100he::setupOpcodes() {
OPCODE(0xd7, o90_sin);
/* D8 */
OPCODE(0xd8, o72_getSoundPosition);
- OPCODE(0xd9, o6_isSoundRunning);
+ OPCODE(0xd9, o60_isSoundRunning);
OPCODE(0xda, o80_getSoundVar);
OPCODE(0xdb, o100_getSpriteInfo);
/* DC */
diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp
index db63bdaac0e..e74921e3610 100644
--- a/engines/scumm/he/script_v60he.cpp
+++ b/engines/scumm/he/script_v60he.cpp
@@ -67,6 +67,7 @@ void ScummEngine_v60he::setupOpcodes() {
_opcodes[0x63].setProc(nullptr, nullptr);
_opcodes[0x64].setProc(nullptr, nullptr);
OPCODE(0x70, o60_setState);
+ OPCODE(0x98, o60_isSoundRunning);
_opcodes[0x9a].setProc(nullptr, nullptr);
OPCODE(0x9c, o60_roomOps);
OPCODE(0x9d, o60_actorOps);
@@ -1191,4 +1192,13 @@ void ScummEngine_v60he::decodeParseString(int m, int n) {
}
}
+void ScummEngine_v60he::o60_isSoundRunning() {
+ int snd = pop();
+
+ if (snd)
+ snd = _sound->isSoundInUse(snd);
+
+ push(snd);
+}
+
} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 05ca0c30ef0..6b830c963a3 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -61,7 +61,13 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
memset(_heChannel, 0, sizeof(_heChannel));
_heSoundChannels = new Audio::SoundHandle[8]();
- _useMilesSoundSystem = parent->_game.id == GID_MOONBASE;
+
+ _useMilesSoundSystem =
+ parent->_game.id == GID_MOONBASE ||
+ parent->_game.id == GID_BASEBALL2003 ||
+ parent->_game.id == GID_BASKETBALL ||
+ parent->_game.id == GID_FOOTBALL2002;
+
_heMixer = new HEMixer(_mixer, _vm, _useMilesSoundSystem);
}
@@ -175,6 +181,38 @@ int SoundHE::isSoundRunning(int sound) const {
}
}
+bool SoundHE::isSoundInUse(int sound) const {
+ // If our sound is a channel number, search for it
+ // between the currently playing sounds first, then
+ // search the sound queue...
+ if (sound >= HSND_CHANNEL_0) {
+ int channel = sound - HSND_CHANNEL_0;
+ sound = _heChannel[channel].sound;
+
+ if (sound)
+ return sound;
+
+ for (int i = 0; i < _soundQueuePos; i++) {
+ if (_soundQueue[i].channel == channel) {
+ return _soundQueue[i].sound;
+ }
+ }
+
+ return 0;
+ }
+
+ // ...otherwise the sound parameter is a proper
+ // sound number, so search the queue
+ int i = _soundQueuePos;
+ while (i) {
+ if (sound == _soundQueue[--i].sound)
+ return sound;
+ }
+
+ // If it's not in the queue, look to see if it is actually playing
+ return (isSoundRunning(sound));
+}
+
void SoundHE::stopSound(int sound) {
if (_vm->_game.heversion >= 70) {
int channel = -1;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 9f11882139d..555ea9ceb3d 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -211,6 +211,7 @@ public:
void modifySound(int sound, int offset, int frequencyShift, int pan, int volume, int flags) override;
int isSoundRunning(int sound) const override;
+ bool isSoundInUse(int sound) const override;
void stopSound(int sound) override;
void stopAllSounds() override;
int hsFindSoundChannel(int sound) const;
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 836fb51da5b..0889af18ec3 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -131,7 +131,7 @@ public:
void stopTalkSound();
bool isMouthSyncOff(uint pos);
virtual int isSoundRunning(int sound) const;
- bool isSoundInUse(int sound) const;
+ virtual bool isSoundInUse(int sound) const;
virtual void stopSound(int sound);
virtual void stopAllSounds();
void soundKludge(int *list, int num);
Commit: f3c625437de33ab42acf5e070023509edd015f79
https://github.com/scummvm/scummvm/commit/f3c625437de33ab42acf5e070023509edd015f79
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement some ADPCM stuff
Also, fix Backyard Baseball 2003 lipsync, and
a start-up crash
Changed paths:
engines/scumm/actor.cpp
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/scumm.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 3d00bfb2cda..43701d40aca 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2491,9 +2491,13 @@ void ActorHE::prepareDrawActorCostume(BaseCostumeRenderer *bcr) {
if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
int talkState = 0;
- if (((SoundHE *)_vm->_sound)->isSoundCodeUsed(1))
- talkState = ((SoundHE *)_vm->_sound)->getSoundVar(1, 19);
- if (talkState == 0)
+ if (!((SoundHE *)_vm->_sound)->isSoundCodeUsed(HSND_TALKIE_SLOT))
+ talkState = _vm->_rnd.getRandomNumberRng(1, 10);
+ else
+ talkState = ((SoundHE *)_vm->_sound)->getSoundVar(HSND_TALKIE_SLOT, 19);
+
+ // Allow a talkie with tokens to kick into random mouth mode
+ if (talkState == -1)
talkState = _vm->_rnd.getRandomNumberRng(1, 10);
assertRange(1, talkState, 13, "Talk state");
@@ -3122,7 +3126,7 @@ void ScummEngine::stopTalk() {
_sound->stopTalkSound();
- _haveMsg = _game.heversion >= 70 ? 3 : 0;
+ _haveMsg = 0;
_talkDelay = 0;
_sound->_digiSndMode = DIGI_SND_MODE_EMPTY;
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index f1b619b3971..c53d11dfaf5 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -188,13 +188,17 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
// Fetch the audio format for the target sound, and fill out
// the format fields on our milesChannel
- int audioDataLen = 0;
- int compType = WAVE_FORMAT_PCM;
- byte *audioData = milesGetAudioDataFromResource(globType, globNum, audioDataLen, compType);
+ byte *audioData = milesGetAudioDataFromResource(globType, globNum, soundData,
+ _milesChannels[channel]._dataFormat, _milesChannels[channel]._blockAlign);
+
+ uint32 audioDataLen = 0;
+ if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM)
+ audioDataLen = sampleLen;
+ else
+ audioDataLen = sampleLen * _milesChannels[channel]._blockAlign;
_milesChannels[channel]._bitsPerSample = bitsPerSample;
_milesChannels[channel]._numChannels = sampleChannels;
- _milesChannels[channel]._dataFormat = compType;
if (audioData) {
_vm->_res->lock((ResType)globType, globNum);
@@ -213,13 +217,13 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
_milesChannels[channel]._playFlags = CHANNEL_LOOPING;
// Looping sounds don't care for modifiers!
- if (compType == WAVE_FORMAT_PCM) {
+ if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_PCM) {
Audio::RewindableAudioStream *stream =
Audio::makeRawStream(audioData, audioDataLen, frequency, _milesChannels[channel].getOutputFlags());
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
- } else {
+ } else if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM) {
warning("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
}
} else {
@@ -227,6 +231,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
// Fill out the modifiers
int newFrequency = (frequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
+ int msOffset = (offset * 1000) / newFrequency;
_milesChannels[channel]._modifiers.frequencyShift = modifiers.frequencyShift;
_milesChannels[channel]._modifiers.volume = modifiers.volume;
@@ -239,13 +244,34 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
int scaledPan = (_milesChannels[channel]._modifiers.pan != 64) ? 2 * _milesChannels[channel]._modifiers.pan - 127 : 0;
// Play the one-shot sound!
- if (compType == WAVE_FORMAT_PCM) {
+ if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_PCM) {
Audio::SeekableAudioStream *stream =
- Audio::makeRawStream(audioData + offset, audioDataLen - offset, newFrequency, _milesChannels[channel].getOutputFlags());
+ Audio::makeRawStream(audioData, audioDataLen, newFrequency, _milesChannels[channel].getOutputFlags());
+
+ stream->seek(msOffset);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
- } else {
- warning("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ } else if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM) {
+ Common::MemoryReadStream memStream(audioData, audioDataLen);
+ Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
+ _milesChannels[channel]._baseFrequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
+
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4);
+ uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
+
+ byte outFlags = _milesChannels[channel].getOutputFlags();
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ outFlags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+
+ Audio::SeekableAudioStream *stream =
+ Audio::makeRawStream(adpcmData, adpcmSize, newFrequency, outFlags);
+
+ stream->seek(msOffset);
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
+ stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
+
}
}
}
@@ -398,7 +424,7 @@ bool HEMixer::milesPauseMixerSubSystem(bool paused) {
return true;
}
-byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, int &dataLength, int &compType) {
+byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign) {
// This function is used for non streamed sound effects and voice files,
// and fetches metadata for the target sound resource
@@ -425,14 +451,14 @@ byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, int &dat
}
compType = READ_LE_UINT16(globPtr + 20); // Format type from the 'fmt ' block
- dataLength = READ_LE_UINT32(globPtr + 40); // Length field of the 'data' block
+ blockAlign = READ_LE_UINT16(globPtr + 32); // Block align field
if (compType != WAVE_FORMAT_PCM && compType != WAVE_FORMAT_IMA_ADPCM) {
debug("HEMixer::milesGetAudioDataFromResource(): .wav files must be PCM or IMA ADPCM. Unsupported .wav sound type %d.", compType);
return nullptr;
}
- return globPtr + WAVE_RIFF_HEADER_LEN;
+ return globPtr + dataOffset;
}
void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int flags, HESoundModifiers modifiers, Audio::Mixer *mixer) {
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 9f97c191361..f8da13e1c22 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -25,9 +25,13 @@
#include "scumm/he/sound_he.h"
#include "scumm/he/intern_he.h"
#include "scumm/resource.h"
+
#include "common/util.h"
#include "common/file.h"
#include "common/debug.h"
+#include "common/memstream.h"
+
+#include "audio/decoders/adpcm.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
@@ -170,7 +174,7 @@ public:
void milesRestoreChannel(int channel);
void milesFeedMixer();
bool milesPauseMixerSubSystem(bool paused);
- byte *milesGetAudioDataFromResource(int globType, int globNum, int &riffLength, int &compType);
+ byte *milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign);
};
} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 6b830c963a3..0e896e0be4c 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -60,7 +60,6 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
_baseSndSize = 0;
memset(_heChannel, 0, sizeof(_heChannel));
- _heSoundChannels = new Audio::SoundHandle[8]();
_useMilesSoundSystem =
parent->_game.id == GID_MOONBASE ||
@@ -73,7 +72,6 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
SoundHE::~SoundHE() {
free(_heSpoolingMusicTable);
- delete[] _heSoundChannels;
if (_heSpoolingMusicFile.isOpen())
_heSpoolingMusicFile.close();
@@ -365,17 +363,15 @@ int SoundHE::getNextDynamicChannel() {
}
bool SoundHE::isSoundCodeUsed(int sound) {
- int chan = -1;
- for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
- if (_heChannel[i].sound == sound)
- chan = i;
- }
+ int chan;
- if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
- return _heChannel[chan].hasSoundTokens;
- } else {
- return false;
+ if ((chan = hsFindSoundChannel(sound)) != -1) {
+ if (_heChannel[chan].hasSoundTokens) {
+ return true;
+ }
}
+
+ return false;
}
int SoundHE::getChannelPosition(int channel) {
@@ -401,11 +397,11 @@ int SoundHE::getSoundPosition(int sound) {
}
int SoundHE::getSoundVar(int sound, int var) {
- if (_vm->_game.heversion >= 90 && var == 26) {
+ if (_vm->_game.heversion >= 90 && var == HSND_SNDVAR_TOKENS) {
return isSoundCodeUsed(sound);
}
- assertRange(0, var, 25, "sound variable");
+ assertRange(0, var, HSND_MAX_SOUND_VARS - 1, "sound variable");
int chan = -1;
for (int i = 0; i < ARRAYSIZE(_heChannel); i ++) {
@@ -413,8 +409,10 @@ int SoundHE::getSoundVar(int sound, int var) {
chan = i;
}
- if (chan != -1 && _mixer->isSoundHandleActive(_heSoundChannels[chan])) {
- debug(5, "getSoundVar: sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
+ chan = hsFindSoundChannel(sound);
+
+ if (chan != -1) {
+ debug(5, "SoundHE::getSoundVar(): sound %d var %d result %d", sound, var, _heChannel[chan].soundVars[var]);
return _heChannel[chan].soundVars[var];
} else {
return 0;
@@ -431,7 +429,7 @@ void SoundHE::setSoundVar(int sound, int var, int val) {
}
if (chan != -1) {
- debug(5, " SoundHE::setSoundVar(): sound %d var %d val %d", sound, var, val);
+ debug(5, "SoundHE::setSoundVar(): sound %d var %d val %d", sound, var, val);
_heChannel[chan].soundVars[var] = val;
}
}
@@ -541,6 +539,7 @@ void SoundHE::handleSoundFrame() {
if (_stopActorTalkingFlag) {
_vm->stopTalk();
+ _vm->_haveMsg = 3;
_stopActorTalkingFlag = false;
}
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 555ea9ceb3d..1d3a8fe3716 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -77,6 +77,8 @@ namespace Scumm {
#define HSND_SBNG_VARORVAL 0x03
#define HSND_SBNG_VARVAL 0x02
+#define HSND_SNDVAR_TOKENS 26
+
#define XSH2_FLAG_HAS_PRIORITY 0x01
#define WAVE_FORMAT_PCM 1
@@ -164,7 +166,6 @@ protected:
int32 _heSpoolingMusicCount;
- Audio::SoundHandle *_heSoundChannels;
Common::File _heSpoolingMusicFile;
byte _heSpoolingCodeBuffer[HSND_MAX_SPOOLING_CODE_SIZE];
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index a4e3e19edc6..23a22c219cc 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2302,7 +2302,7 @@ void ScummEngine::syncSoundSettings() {
// Backyard Baseball 2003 uses a unique subtitle variable,
// rather than VAR_SUBTITLES
- if (_game.id == GID_BASEBALL2003) {
+ if (_scummVars && _game.id == GID_BASEBALL2003) {
_scummVars[632] = ConfMan.getBool("subtitles");
}
Commit: 4037e4889f1e2bc45cb580fa6cdd11d7ea566f24
https://github.com/scummvm/scummvm/commit/4037e4889f1e2bc45cb580fa6cdd11d7ea566f24
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Finish IMA ADPCM support for voice and SFX
Backyard Baseball 2003 should now be completely supported.
Hopefully.
Also, I still haven't found a case in which IMA ADPCM is used on
spooled music, so I'm leaving an error in there so that if I will
implement it only if such a case actually exists.
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index c53d11dfaf5..d96735b6ca8 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -186,14 +186,16 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
// is also going to trigger the appropriate callbacks
milesStopChannel(channel);
+ uint32 actualDataSize = 0;
+
// Fetch the audio format for the target sound, and fill out
// the format fields on our milesChannel
byte *audioData = milesGetAudioDataFromResource(globType, globNum, soundData,
- _milesChannels[channel]._dataFormat, _milesChannels[channel]._blockAlign);
+ _milesChannels[channel]._dataFormat, _milesChannels[channel]._blockAlign, actualDataSize);
uint32 audioDataLen = 0;
if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM)
- audioDataLen = sampleLen;
+ audioDataLen = actualDataSize;
else
audioDataLen = sampleLen * _milesChannels[channel]._blockAlign;
@@ -217,15 +219,28 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
_milesChannels[channel]._playFlags = CHANNEL_LOOPING;
// Looping sounds don't care for modifiers!
+ Audio::RewindableAudioStream *stream = nullptr;
if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_PCM) {
- Audio::RewindableAudioStream *stream =
- Audio::makeRawStream(audioData, audioDataLen, frequency, _milesChannels[channel].getOutputFlags());
+ stream = Audio::makeRawStream(audioData, audioDataLen, frequency, _milesChannels[channel].getOutputFlags());
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
- Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
} else if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM) {
- warning("HEMixer::milesStartChannel(): Looping ADPCM not yet implemented!");
+ Common::MemoryReadStream memStream(audioData, audioDataLen);
+ Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
+ frequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
+
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4);
+ uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
+ delete adpcmStream;
+
+ adpcmSize *= 2;
+ stream = Audio::makeRawStream(adpcmData, adpcmSize, frequency, _milesChannels[channel].getOutputFlags());
+ }
+
+ if (stream) {
+ _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
+ Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
}
+
} else {
_milesChannels[channel]._playFlags = CHANNEL_EMPTY_FLAGS;
@@ -244,13 +259,10 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
int scaledPan = (_milesChannels[channel]._modifiers.pan != 64) ? 2 * _milesChannels[channel]._modifiers.pan - 127 : 0;
// Play the one-shot sound!
+ Audio::SeekableAudioStream *stream = nullptr;
if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_PCM) {
- Audio::SeekableAudioStream *stream =
- Audio::makeRawStream(audioData, audioDataLen, newFrequency, _milesChannels[channel].getOutputFlags());
+ stream = Audio::makeRawStream(audioData, audioDataLen, newFrequency, _milesChannels[channel].getOutputFlags());
- stream->seek(msOffset);
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
- stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
} else if (_milesChannels[channel]._dataFormat == WAVE_FORMAT_IMA_ADPCM) {
Common::MemoryReadStream memStream(audioData, audioDataLen);
Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
@@ -258,20 +270,16 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
byte *adpcmData = (byte *)malloc(audioDataLen * 4);
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
+ delete adpcmStream;
- byte outFlags = _milesChannels[channel].getOutputFlags();
-
-#ifdef SCUMM_LITTLE_ENDIAN
- outFlags |= Audio::FLAG_LITTLE_ENDIAN;
-#endif
-
- Audio::SeekableAudioStream *stream =
- Audio::makeRawStream(adpcmData, adpcmSize, newFrequency, outFlags);
+ adpcmSize *= 2;
+ stream = Audio::makeRawStream(adpcmData, adpcmSize, newFrequency, _milesChannels[channel].getOutputFlags());
+ }
+ if (stream) {
stream->seek(msOffset);
_mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
-
}
}
}
@@ -424,7 +432,7 @@ bool HEMixer::milesPauseMixerSubSystem(bool paused) {
return true;
}
-byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign) {
+byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign, uint32 &dataSize) {
// This function is used for non streamed sound effects and voice files,
// and fetches metadata for the target sound resource
@@ -458,6 +466,15 @@ byte *HEMixer::milesGetAudioDataFromResource(int globType, int globNum, uint32 d
return nullptr;
}
+ // Check for the 'data' chunk
+ if (READ_BE_UINT32(globPtr + (dataOffset - 8)) != MKTAG('d', 'a', 't', 'a')) {
+ debug("HEMixer::milesGetAudioDataFromResource(): Did not find 'data' chunk in .wav file");
+ return nullptr;
+ }
+
+ // The 'data' chunk size is immediately after the 'data' chunk ID
+ dataSize = READ_LE_UINT32(globPtr + (dataOffset - 4));
+
return globPtr + dataOffset;
}
@@ -581,7 +598,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
for (int i = 0; i < MILES_MAX_QUEUED_STREAMS; i++)
serviceStream();
} else if (_dataFormat == WAVE_FORMAT_IMA_ADPCM) {
- warning("HEMixer::milesStartChannel(): ADPCM not yet implemented!");
+ error("HEMixer::milesStartChannel(): spooling ADPCM not yet implemented!");
} else {
debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %d",
_dataFormat, filename, offset);
@@ -633,6 +650,11 @@ byte HEMilesChannel::getOutputFlags() {
if (_numChannels == 2)
streamFlags |= Audio::FLAG_STEREO;
+#ifdef SCUMM_LITTLE_ENDIAN
+ if (_dataFormat == WAVE_FORMAT_IMA_ADPCM)
+ streamFlags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+
return streamFlags;
}
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index f8da13e1c22..e87b47cd09a 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -174,7 +174,7 @@ public:
void milesRestoreChannel(int channel);
void milesFeedMixer();
bool milesPauseMixerSubSystem(bool paused);
- byte *milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign);
+ byte *milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign, uint32 &dataSize);
};
} // End of namespace Scumm
Commit: a63178262f7ed546012ad3817a76226b69f1df92
https://github.com/scummvm/scummvm/commit/a63178262f7ed546012ad3817a76226b69f1df92
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix crash in Moonbase with invalid modifiers
Changed paths:
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 1d3a8fe3716..20e5d95981a 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -108,10 +108,14 @@ namespace Scumm {
// Used both in SoundHE and HEMixer
struct HESoundModifiers {
HESoundModifiers(int mFrequencyShift, int mPan, int mVolume) {
+ // Hey, turns out Moonbase Commander just LOVES to send out invalid modifiers :-)
+ /*
assert(mFrequencyShift >= HSND_SOUND_FREQ_BASE / HSND_MAX_FREQ_RATIO);
assert(mFrequencyShift <= HSND_SOUND_FREQ_BASE * HSND_MAX_FREQ_RATIO);
assert(mPan >= HSND_SOUND_PAN_LEFT && mPan <= HSND_SOUND_PAN_RIGHT);
assert(mVolume >= 0 && mVolume <= HSND_MAX_VOLUME);
+ */
+
frequencyShift = mFrequencyShift;
pan = mPan;
volume = mVolume;
Commit: 2a02ec9b7a1c13235c4660c4d8eb7d200d499014
https://github.com/scummvm/scummvm/commit/2a02ec9b7a1c13235c4660c4d8eb7d200d499014
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Add support for PJGames and Backyard Soccer 2004
They just... work!
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 0e896e0be4c..773d6a718b5 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -65,7 +65,9 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
parent->_game.id == GID_MOONBASE ||
parent->_game.id == GID_BASEBALL2003 ||
parent->_game.id == GID_BASKETBALL ||
- parent->_game.id == GID_FOOTBALL2002;
+ parent->_game.id == GID_FOOTBALL2002 ||
+ parent->_game.id == GID_SOCCER2004 ||
+ parent->_game.id == GID_PJGAMES;
_heMixer = new HEMixer(_mixer, _vm, _useMilesSoundSystem);
}
Commit: f9e54b50069457e8ee80fa2508024c47e26bc24c
https://github.com/scummvm/scummvm/commit/f9e54b50069457e8ee80fa2508024c47e26bc24c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Complete support for all games using Miles Sound System
This includes:
- Moonbase Commander
- Backyard Basketball
- Backyard Baseball 2003
- Backyard Football 2004
- Pajama Sam: Games to Play on Any Day
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index d96735b6ca8..1c171e56ce2 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -32,6 +32,13 @@ HEMixer::HEMixer(Audio::Mixer *mixer, ScummEngine_v60he *vm, bool useMilesSoundS
HEMixer::~HEMixer() {
}
+uint32 calculateDeflatedADPCMBlockSize(uint32 numBlocks, uint32 blockAlign, uint32 numChannels, uint32 bitsPerSample) {
+ uint32 samplesPerBlock = (blockAlign - (4 * numChannels)) * (numChannels ^ 3) + 1;
+ uint32 totalSize = numBlocks * samplesPerBlock * (bitsPerSample / 8);
+
+ return totalSize;
+}
+
void *HEMixer::getMilesSoundSystemObject() {
return nullptr;
}
@@ -228,7 +235,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
frequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
- byte *adpcmData = (byte *)malloc(audioDataLen * 4);
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4 * sizeof(byte));
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
delete adpcmStream;
@@ -268,7 +275,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
_milesChannels[channel]._baseFrequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
- byte *adpcmData = (byte *)malloc(audioDataLen * 4);
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4 * sizeof(byte));
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
delete adpcmStream;
@@ -530,6 +537,8 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
waveHeader.fmtTag = _stream.fileHandle->readUint32BE();
waveHeader.fmtSize = _stream.fileHandle->readUint32LE();
+ int fmtPos = _stream.fileHandle->pos();
+
waveHeader.wFormatTag = _stream.fileHandle->readUint16LE();
waveHeader.wChannels = _stream.fileHandle->readUint16LE();
waveHeader.dwSamplesPerSec = _stream.fileHandle->readUint32LE();
@@ -572,37 +581,57 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
// Transform the range of the pan value from [0, 127] to [-127, 127]
int scaledPan = (_modifiers.pan != 64) ? 2 * _modifiers.pan - 127 : 0;
+ int newFrequency = (_baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
- // Finally create our stream and play it!
+ // Create our stream and start it
_stream.streamObj = Audio::makeQueuingAudioStream(_baseFrequency, (waveHeader.wChannels > 1));
mixer->playStream(Audio::Mixer::kPlainSoundType, &_stream.streamHandle, _stream.streamObj, -1, 255, 0, DisposeAfterUse::NO);
- // Apply the modifiers and the loop flag,if available
- mixer->setChannelVolume(_stream.streamHandle, _modifiers.volume);
- mixer->setChannelBalance(_stream.streamHandle, scaledPan);
- int newFrequency = (_baseFrequency * modifiers.frequencyShift) / HSND_SOUND_FREQ_BASE;
- if (newFrequency)
- mixer->setChannelRate(_stream.streamHandle, newFrequency);
-
- _stream.loopFlag = flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP;
+ if (_dataFormat == WAVE_FORMAT_PCM) {
+ // Apply the modifiers and the loop flag, if available
+ mixer->setChannelVolume(_stream.streamHandle, _modifiers.volume);
+ mixer->setChannelBalance(_stream.streamHandle, scaledPan);
+ if (newFrequency)
+ mixer->setChannelRate(_stream.streamHandle, newFrequency);
- // And now we help out the stream by feeding the first bytes of data to it
- _stream.fileHandle->readUint32BE(); // Skip 'data' tag
+ _stream.loopFlag = flags & ScummEngine_v70he::HESndFlags::HE_SND_LOOP;
- _stream.dataLength = _stream.fileHandle->readUint32LE();
- _stream.curDataPos = 0;
- _stream.dataOffset = _stream.fileHandle->pos();
+ // And now we help out the stream by feeding the first bytes of data to it
+ _stream.fileHandle->readUint32BE(); // Skip 'data' tag
- if (_dataFormat == WAVE_FORMAT_PCM) {
- // Saturate the stream queue with the beginning audio data
- for (int i = 0; i < MILES_MAX_QUEUED_STREAMS; i++)
- serviceStream();
+ _stream.dataLength = _stream.fileHandle->readUint32LE();
+ _stream.curDataPos = 0;
+ _stream.dataOffset = _stream.fileHandle->pos();
} else if (_dataFormat == WAVE_FORMAT_IMA_ADPCM) {
- error("HEMixer::milesStartChannel(): spooling ADPCM not yet implemented!");
+ // IMA ADPCM might have a longer header, so use the previously obtained
+ // information to jump through blocks and find the 'data' tag
+ _stream.fileHandle->seek(fmtPos, SEEK_SET);
+ _stream.fileHandle->seek(waveHeader.fmtSize, SEEK_CUR);
+
+ uint32 curTag = 0;
+ while ((curTag = _stream.fileHandle->readUint32BE()) != MKTAG('d', 'a', 't', 'a')) {
+ uint32 blockSize = _stream.fileHandle->readUint32LE();
+ _stream.fileHandle->seek(blockSize, SEEK_CUR);
+ debug(5, "HEMixer::milesStartChannel(): APDCM spooling sound, searching for 'data' tag, now on '%s' tag...",
+ tag2str(curTag));
+
+ if (_stream.fileHandle->eos()) {
+ debug(5, "HEMixer::milesStartChannel(): APDCM spooling sound, couldn't find 'data' block, bailing out...");
+ return;
+ }
+ }
+
+ _stream.dataLength = _stream.fileHandle->readUint32LE();
+ _stream.curDataPos = 0;
+ _stream.dataOffset = _stream.fileHandle->pos();
} else {
debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %d",
_dataFormat, filename, offset);
}
+
+ // Saturate the stream queue with the beginning of the audio data
+ for (int i = 0; i < MILES_MAX_QUEUED_STREAMS; i++)
+ serviceStream();
}
void HEMilesChannel::clearChannelData() {
@@ -659,16 +688,58 @@ byte HEMilesChannel::getOutputFlags() {
}
void HEMilesChannel::serviceStream() {
+ bool reachedTheEnd = false;
+ uint32 sizeToRead = 0;
+
// This is called at each frame, to ensure that the target stream doesn't starve
if (_stream.streamObj->numQueuedStreams() < MILES_MAX_QUEUED_STREAMS) {
- int sizeToRead = MIN<int>(MILES_CHUNK_SIZE * _blockAlign, _stream.dataLength - _stream.curDataPos);
- bool reachedTheEnd = sizeToRead < MILES_CHUNK_SIZE * _blockAlign;
-
- byte *buffer = (byte *)malloc(sizeToRead);
- if (sizeToRead > 0 && buffer != nullptr) {
- int readBytes = _stream.fileHandle->read(buffer, sizeToRead);
- _stream.curDataPos += readBytes;
- _stream.streamObj->queueBuffer(buffer, readBytes, DisposeAfterUse::YES, getOutputFlags());
+ if (_dataFormat == WAVE_FORMAT_PCM) {
+ sizeToRead = MIN<uint32>(MILES_PCM_CHUNK_SIZE * _blockAlign, _stream.dataLength - _stream.curDataPos);
+ reachedTheEnd = sizeToRead < MILES_PCM_CHUNK_SIZE * _blockAlign;
+
+ byte *buffer = (byte *)malloc(sizeToRead * sizeof(byte));
+ if (sizeToRead > 0 && buffer != nullptr) {
+ int readBytes = _stream.fileHandle->read(buffer, sizeToRead);
+ _stream.curDataPos += readBytes;
+ _stream.streamObj->queueBuffer(buffer, readBytes, DisposeAfterUse::YES, getOutputFlags());
+ }
+
+ } else if (_dataFormat == WAVE_FORMAT_IMA_ADPCM) {
+ // Look, I know: it's some of the ugliest code you've ever seen. Sorry.
+ // Unfortunately when it comes to streaming ADPCM audio from a file this is as
+ // clean as I can possibly make it (instead of loading and keeping the whole
+ // thing in memory, that is).
+
+ sizeToRead = MIN<uint32>(MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM * _blockAlign, _stream.dataLength - _stream.curDataPos);
+ reachedTheEnd = sizeToRead < MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM * _blockAlign;
+
+ // We allocate a buffer which is going to be filled with
+ // (MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM) compressed blocks or less
+ byte *compressedBuffer = (byte *)malloc(sizeToRead * sizeof(byte));
+ if (sizeToRead > 0 && compressedBuffer != nullptr) {
+ int readBytes = _stream.fileHandle->read(compressedBuffer, sizeToRead);
+ _stream.curDataPos += readBytes;
+
+ // Now, the ugly trick: use a MemoryReadStream containing our compressed data,
+ // to feed an ADPCM stream, and then use the latter to read uncompressed data,
+ // and then queue the latter in the output stream.
+ // Hey, it IS ugly! ...and it works :-)
+ Common::MemoryReadStream memStream(compressedBuffer, readBytes);
+ Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO,
+ readBytes, Audio::kADPCMMSIma, _baseFrequency, _numChannels, _blockAlign);
+
+ uint32 uncompSize =
+ calculateDeflatedADPCMBlockSize(MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM, _blockAlign, _numChannels, 16);
+
+ byte *adpcmData = (byte *)malloc(uncompSize * sizeof(byte));
+ uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, uncompSize * 2);
+
+ adpcmSize *= 2;
+ _stream.streamObj->queueBuffer(adpcmData, adpcmSize, DisposeAfterUse::YES, getOutputFlags());
+
+ delete adpcmStream;
+ free(compressedBuffer);
+ }
}
if (reachedTheEnd) {
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index e87b47cd09a..917a54f6554 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -54,9 +54,10 @@ namespace Scumm {
#define CHANNEL_CALLBACK_EARLY 0x00000080
#define CHANNEL_SOFT_REMIX 0x00000100
-#define MILES_MAX_CHANNELS 8
-#define MILES_CHUNK_SIZE 4096
-#define MILES_MAX_QUEUED_STREAMS 16
+#define MILES_MAX_CHANNELS 8
+#define MILES_PCM_CHUNK_SIZE 4096u
+#define MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM 4u
+#define MILES_MAX_QUEUED_STREAMS 16
struct HESoundModifiers;
class ScummEngine_v60he;
Commit: 815e280da4d8ca8d293a48ab10a18644f0dd4b4d
https://github.com/scummvm/scummvm/commit/815e280da4d8ca8d293a48ab10a18644f0dd4b4d
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement software mixer for all the other games
Sound now appears to play correctly for all HE games.
Changed paths:
engines/scumm/actor.cpp
engines/scumm/akos.cpp
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/script_v60he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/resource.cpp
engines/scumm/scumm.cpp
engines/scumm/scumm.h
engines/scumm/sound.cpp
engines/scumm/string.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index 43701d40aca..d57a9d8237f 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2489,7 +2489,7 @@ void ActorHE::prepareDrawActorCostume(BaseCostumeRenderer *bcr) {
if (_vm->_game.heversion >= 80 && _heNoTalkAnimation == 0 && _animProgress == 0) {
if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
- int talkState = 0;
+ int talkState = -1;
if (!((SoundHE *)_vm->_sound)->isSoundCodeUsed(HSND_TALKIE_SLOT))
talkState = _vm->_rnd.getRandomNumberRng(1, 10);
@@ -2497,7 +2497,7 @@ void ActorHE::prepareDrawActorCostume(BaseCostumeRenderer *bcr) {
talkState = ((SoundHE *)_vm->_sound)->getSoundVar(HSND_TALKIE_SLOT, 19);
// Allow a talkie with tokens to kick into random mouth mode
- if (talkState == -1)
+ if (talkState == -1 || talkState == 0)
talkState = _vm->_rnd.getRandomNumberRng(1, 10);
assertRange(1, talkState, 13, "Talk state");
@@ -3148,7 +3148,7 @@ void ScummEngine::stopTalk() {
if ((_game.id == GID_DIG && !(_game.features & GF_DEMO)) || _game.id == GID_CMI) {
setTalkingActor(0);
VAR(VAR_HAVE_MSG) = 0;
- } else if (_game.heversion >= 60 && !_actorShouldStopTalking) {
+ } else if (_game.heversion >= 60) {
setTalkingActor(0);
}
diff --git a/engines/scumm/akos.cpp b/engines/scumm/akos.cpp
index 0748bb1afca..67cf50910af 100644
--- a/engines/scumm/akos.cpp
+++ b/engines/scumm/akos.cpp
@@ -1701,13 +1701,6 @@ void ScummEngine_v6::akos_processQueue() {
error("akos_queCommand(%d,%d,%d,%d)", cmd, a->_number, param1, param2);
}
}
-
- if (_game.heversion == 98 && _game.id == GID_FREDDI4 && _actorShouldStopTalking) {
- Actor *a = derefActor(getTalkingActor(), "ScummEngine_v6::akos_processQueue()");
- ((ActorHE *)a)->_heTalking = false;
- setTalkingActor(0);
- _actorShouldStopTalking = false;
- }
}
#ifdef ENABLE_SCUMM_7_8
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 1c171e56ce2..0ab69399728 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -27,9 +27,16 @@ HEMixer::HEMixer(Audio::Mixer *mixer, ScummEngine_v60he *vm, bool useMilesSoundS
_mixer = mixer;
_vm = vm;
_useMilesSoundSystem = useMilesSoundSystem;
+
+ initSoftMixerSubSystem();
}
HEMixer::~HEMixer() {
+ deinitSoftMixerSubSystem();
+}
+
+bool HEMixer::isMilesActive() {
+ return _useMilesSoundSystem;
}
uint32 calculateDeflatedADPCMBlockSize(uint32 numBlocks, uint32 blockAlign, uint32 numChannels, uint32 bitsPerSample) {
@@ -39,36 +46,27 @@ uint32 calculateDeflatedADPCMBlockSize(uint32 numBlocks, uint32 blockAlign, uint
return totalSize;
}
-void *HEMixer::getMilesSoundSystemObject() {
- return nullptr;
-}
-
bool HEMixer::initSoftMixerSubSystem() {
- return false;
-}
-
-void HEMixer::deinitSoftMixerSubSystem() {
-}
-
-void HEMixer::endNeglectProcess() {
-}
-
-void HEMixer::startLongNeglectProcess() {
-}
+ if (!isMilesActive()) {
+ return mixerInitMyMixerSubSystem();
+ }
-bool HEMixer::forceMusicBufferFill() {
- return false;
+ return true;
}
-bool HEMixer::isMixerDisabled() {
- return false;
+void HEMixer::deinitSoftMixerSubSystem() {
+ if (isMilesActive()) {
+ milesStopAllSounds();
+ } else {
+ mixerDeinitMyMixerSubSystem();
+ }
}
bool HEMixer::stopChannel(int channel) {
- if (_useMilesSoundSystem) {
+ if (isMilesActive()) {
return milesStopChannel(channel);
} else {
- //return mixerStopChannel(channel);
+ return mixerStopChannel(channel);
}
return true;
@@ -80,46 +78,51 @@ void HEMixer::stopAllChannels() {
}
}
-bool HEMixer::changeChannelVolume(int channel, int volume, bool soft) {
- return false;
-}
-
-void HEMixer::softRemixAllChannels() {
-}
-
-void HEMixer::premixUntilCritical() {
-}
-
bool HEMixer::pauseMixerSubSystem(bool paused) {
- return false;
+ if (isMilesActive()) {
+ return milesPauseMixerSubSystem(paused);
+ } else {
+ return mixerPauseMixerSubSystem(paused);
+ }
}
void HEMixer::feedMixer() {
- if (_useMilesSoundSystem) {
+ if (isMilesActive()) {
milesFeedMixer();
} else {
- //mixerFeedMixer();
+ mixerFeedMixer();
}
}
+
int HEMixer::getChannelCurrentPosition(int channel) {
- return 0;
+ // We are sure this will never be called with Miles on
+ return mixerGetChannelCurrentPosition(channel);
+}
+
+bool HEMixer::changeChannelVolume(int channel, int volume, bool soft) {
+ if (!isMilesActive()) {
+ return mixerChangeChannelVolume(channel, volume, soft);
+ }
+
+ return true;
}
bool HEMixer::startChannelNew(
int channel, int globType, int globNum, uint32 soundData, uint32 offset,
int sampleLen, int frequency, int bitsPerSample, int sampleChannels,
const HESoundModifiers &modifiers, int callbackID, int32 flags, ...) {
+
va_list params;
bool retValue;
- if (!_useMilesSoundSystem) {
+ if (!isMilesActive()) {
if (bitsPerSample != 8) {
debug(5, "HEMixer::startChannelNew(): Glob(%d, %d) is %d bits per channel, must be 8 for software mixer", globType, globNum, bitsPerSample);
return false;
}
- if (CHANNEL_CALLBACK_EARLY & flags) {
+ if (flags & CHANNEL_CALLBACK_EARLY) {
va_start(params, flags);
retValue = mixerStartChannel(
@@ -145,45 +148,440 @@ bool HEMixer::startChannelNew(
return true;
}
-void HEMixer::serviceAllStreams() {
- for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
- if (_milesChannels[i]._stream.streamObj != nullptr)
- _milesChannels[i].serviceStream();
+bool HEMixer::startChannel(int channel, int globType, int globNum,
+ uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...) {
+
+ va_list params;
+ bool retValue;
+
+ if (flags & CHANNEL_CALLBACK_EARLY) {
+ va_start(params, flags);
+
+ retValue = mixerStartChannel(
+ channel, globType, globNum, sampleDataOffset, sampleLen,
+ frequency, volume, callbackId, flags, va_arg(params, int));
+
+ va_end(params);
+ return retValue;
+ } else {
+ return mixerStartChannel(
+ channel, globType, globNum, sampleDataOffset, sampleLen,
+ frequency, volume, callbackId, flags);
}
}
-bool HEMixer::startChannel(int channel, int globType, int globNum, uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...) {
+bool HEMixer::startSpoolingChannel(int channel, Common::File &sampleFileIOHandle,
+ int sampleLen, int frequency, int volume, int callbackID, int32 flags) {
+
+ if (!isMilesActive()) {
+ return mixerStartSpoolingChannel(channel, sampleFileIOHandle, sampleLen,
+ frequency, volume, callbackID, flags);
+ }
+
return false;
}
-bool HEMixer::startSpoolingChannel(int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency, int volume, int callbackID, int32 flags, ...) {
+/* --- SOFTWARE MIXER --- */
+
+bool HEMixer::mixerInitMyMixerSubSystem() {
+ // Init the channel buffers, and kick start the mixer...
+ memset(_mixerChannels, 0, sizeof(_mixerChannels));
+
+ for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ _mixerChannels[i].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
+ _mixer->playStream(
+ Audio::Mixer::kPlainSoundType,
+ &_mixerChannels[i].handle,
+ _mixerChannels[i].stream,
+ -1,
+ Audio::Mixer::kMaxChannelVolume);
+ }
+
+ for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ mixerStartChannel(i, 0, 0, 0, 0, 0, 0, 0, CHANNEL_EMPTY_FLAGS);
+ }
+
+ return true;
+}
+
+bool HEMixer::mixerDeinitMyMixerSubSystem() {
return false;
}
-bool HEMixer::isMilesActive() {
- return _useMilesSoundSystem;
+void HEMixer::mixerFeedMixer() {
+ if (_mixerPaused) {
+ return;
+ }
+
+ if (!_vm->_insideCreateResource && _vm->_game.heversion >= 80) {
+ ((SoundHE *)_vm->_sound)->unqueueSoundCallbackScripts();
+ }
+
+ for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ if (!(_mixerChannels[i].flags & CHANNEL_LOOPING)) {
+ if ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && _mixerChannels[i].stream->endOfData()) {
+ _mixerChannels[i].flags |= CHANNEL_FINISHED;
+ _mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
+
+ debug(5, "hit finish on channel %d", i);
+ _mixerChannels[i].stream->finish();
+ _mixerChannels[i].stream = nullptr;
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
+ }
+
+ if (_mixerChannels[i].flags & CHANNEL_SPOOLING) {
+ if (_mixerChannels[i].callbackOnNextFrame) {
+ _mixerChannels[i].callbackOnNextFrame = false;
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
+ }
+
+ if ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK)) {
+ _mixerChannels[i].flags &= ~CHANNEL_LAST_CHUNK;
+ _mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
+ _mixerChannels[i].flags |= CHANNEL_FINISHED;
+
+ debug(5, "hit finish on spooling channel %d", i);
+ _mixerChannels[i].stream->finish();
+ _mixerChannels[i].stream = nullptr;
+ _mixerChannels[i].callbackOnNextFrame = true;
+ }
+ }
+ }
+ }
+
+ // Feed the streams
+ for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ if (_mixerChannels[i].flags & CHANNEL_ACTIVE) {
+ if (_mixerChannels[i].flags & CHANNEL_SPOOLING) {
+ bool looping = (_mixerChannels[i].flags & CHANNEL_LOOPING);
+
+ if (_mixerChannels[i].stream->numQueuedStreams() > 1)
+ continue;
+
+ _mixerChannels[i].fileHandle->seek(_mixerChannels[i].initialSpoolingFileOffset, SEEK_SET);
+ uint32 curOffset = _mixerChannels[i].lastReadPosition;
+
+ _mixerChannels[i].fileHandle->seek(curOffset, SEEK_CUR);
+
+ int length = SPOOL_CHUNK_SIZE;
+ if (_mixerChannels[i].lastReadPosition + SPOOL_CHUNK_SIZE >= _mixerChannels[i].sampleLen) {
+ length = _mixerChannels[i].sampleLen % SPOOL_CHUNK_SIZE;
+ _mixerChannels[i].flags |= CHANNEL_LAST_CHUNK;
+ }
+
+ // The file size is an exact multiple of SPOOL_CHUNK_SIZE, and we've already read the
+ // last chunk, so signal it this is not a looping sample, otherwise rewind the file.
+ // Should never happen, but... you know how it goes :-)
+ if (length == 0) {
+ if (looping) {
+ curOffset = _mixerChannels[i].lastReadPosition = 0;
+ _mixerChannels[i].fileHandle->seek(curOffset, SEEK_CUR);
+ } else {
+ _mixerChannels[i].flags |= CHANNEL_LAST_CHUNK;
+ continue;
+ }
+ }
+
+ byte *data = (byte *)malloc(length * sizeof(byte));
+ if (data) {
+ _mixerChannels[i].fileHandle->read(data, length);
+ _mixerChannels[i].lastReadPosition += length;
+
+ // If we've reached the end of the sample after a residual read,
+ // and if we have to loop the sample, rewind the read position
+ if (looping && _mixerChannels[i].lastReadPosition == _mixerChannels[i].sampleLen)
+ _mixerChannels[i].lastReadPosition = 0;
+
+ _mixerChannels[i].stream->queueBuffer(
+ data,
+ length,
+ DisposeAfterUse::YES,
+ mixerGetOutputFlags());
+ }
+ }
+ }
+ }
}
-bool HEMixer::changeChannelVolume(int channel, int newVolume, int soft_flag) {
+bool HEMixer::mixerIsMixerDisabled() {
return false;
}
-void HEMixer::milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers) {
- assert(channel >= 0 && channel < ARRAYSIZE(_milesChannels));
+bool HEMixer::mixerStopChannel(int channel) {
+ if ((0 <= channel) && (MIXER_MAX_CHANNELS > channel))
+ return mixerStartChannel(channel, 0, 0, 0, 0, 0, 0, 0, CHANNEL_EMPTY_FLAGS);
- if (channel >= 0 && channel < ARRAYSIZE(_milesChannels))
- _milesChannels[channel].startSpoolingChannel(filename, offset, flags, modifiers, _mixer);
+ return false;
+}
+
+bool HEMixer::mixerChangeChannelVolume(int channel, int volume, bool soft) {
+ byte newVolume = (byte)MAX<int>(0, MIN<int>(255, volume));
+ _mixerChannels[channel].volume = newVolume;
+ _mixer->setChannelVolume(_mixerChannels[channel].handle, newVolume);
+
+ return true;
}
-int HEMixer::hsFindSoundQueue(int sound) {
+int HEMixer::mixerGetChannelCurrentPosition(int channel) {
return 0;
}
-bool HEMixer::mixerStartChannel(int channel, int globType, int globNum, uint32 sampleDataOffset, int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...) {
- return false;
+bool HEMixer::mixerPauseMixerSubSystem(bool paused) {
+ _mixerPaused = paused;
+
+ _mixer->pauseAll(_mixerPaused);
+
+ return true;
+}
+
+bool HEMixer::mixerStartChannel(
+ int channel, int globType, int globNum, uint32 sampleDataOffset,
+ int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...) {
+
+ va_list params;
+
+ if ((channel < 0) || (channel > MIXER_MAX_CHANNELS))
+ return false;
+
+ if (_mixerChannels[channel].flags != CHANNEL_EMPTY_FLAGS) {
+ if (!(_mixerChannels[channel].flags & CHANNEL_FINISHED)) {
+
+ _mixerChannels[channel].flags |= CHANNEL_FINISHED;
+ _mixerChannels[channel].flags &= ~CHANNEL_ACTIVE;
+
+ debug(5, "stopping handle on channel %d", channel);
+ _mixer->stopHandle(_mixerChannels[channel].handle);
+ _mixerChannels[channel].stream = nullptr;
+
+ // Signal the sound engine that a sound has finished playing
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_STOPPED, _mixerChannels[channel].callbackID);
+ }
+ }
+
+ if (flags != CHANNEL_EMPTY_FLAGS) {
+ if (sampleLen <= 0) {
+ error("HEMixer::mixerStartChannel(): Sample invalid size %d", sampleLen);
+ }
+
+ if ((flags & CHANNEL_LOOPING) && (sampleLen <= MIXER_PCM_CHUNK_SIZE)) {
+ error("HEMixer::mixerStartChannel(): Sample too small to loop (%d)", sampleLen);
+ }
+ }
+
+ _mixerChannels[channel].flags = flags;
+ _mixerChannels[channel].volume = MIN<int>(MAX<int>(0, volume), 255);
+ _mixerChannels[channel].number = channel;
+ _mixerChannels[channel].frequency = frequency;
+ _mixerChannels[channel].callbackID = callbackID;
+ _mixerChannels[channel].lastReadPosition = 0;
+ _mixerChannels[channel].dataOffset = sampleDataOffset;
+ _mixerChannels[channel].sampleLen = sampleLen;
+ _mixerChannels[channel].globType = globType;
+ _mixerChannels[channel].globNum = globNum;
+
+ if (flags & CHANNEL_CALLBACK_EARLY) {
+ va_start(params, flags);
+ _mixerChannels[channel].endSampleAdjustment = va_arg(params, int);
+ va_end(params);
+ } else {
+ _mixerChannels[channel].endSampleAdjustment = 0;
+ }
+
+ if (flags != CHANNEL_EMPTY_FLAGS) {
+ byte *ptr = _vm->getResourceAddress((ResType)globType, globNum);
+
+ if (READ_BE_UINT32(ptr) == MKTAG('W', 'S', 'O', 'U')) {
+ ptr += 8;
+ }
+
+ byte *data = (byte *)malloc(_mixerChannels[channel].sampleLen * sizeof(byte));
+
+ if (!data)
+ return false;
+
+ // The following feature is the only thing which would justify
+ // the pain of having non-spooling audio files played as chunks.
+ //
+ // The purpose of this is to produce a callback before the end of
+ // the sound, precisely endSampleAdjustment bytes earlier.
+ //
+ // I haven't seen this one used anywhere (except in puttcircus,
+ // which only gives 2 as a value - which is a value which of course
+ // does nothing even in the original). Please, (don't) prove me wrong :-)
+ int offset = _mixerChannels[channel].endSampleAdjustment;
+ if (offset != 0 && offset != 2)
+ error("HEMixer::mixerStartChannel(): Unimplemented early callback with value %d, in room %d, for sound %d!\n",
+ offset, _vm->_currentRoom, globNum);
+
+ ptr += sampleDataOffset;
+
+ memcpy(data, ptr, _mixerChannels[channel].sampleLen);
+
+ byte *dataTmp = data;
+ int rampUpSampleCount = 64;
+ for (int i = 0; i < rampUpSampleCount; i++) {
+ *dataTmp = 128 + (((*dataTmp - 128) * i) / rampUpSampleCount);
+ dataTmp++;
+ }
+
+ _mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
+
+ _mixer->playStream(
+ Audio::Mixer::kPlainSoundType,
+ &_mixerChannels[channel].handle,
+ _mixerChannels[channel].stream,
+ -1,
+ volume);
+
+ // Only HE60/61/62 games allowed for samples pitch shifting
+ if (_vm->_game.heversion < 70)
+ _mixer->setChannelRate(_mixerChannels[channel].handle, frequency);
+
+ if (_mixerChannels[channel].flags & CHANNEL_LOOPING) {
+ Audio::RewindableAudioStream *stream = Audio::makeRawStream(
+ data,
+ _mixerChannels[channel].sampleLen,
+ MIXER_DEFAULT_SAMPLE_RATE,
+ mixerGetOutputFlags(),
+ DisposeAfterUse::YES);
+
+ _mixerChannels[channel].stream->queueAudioStream(Audio::makeLoopingAudioStream(stream, 0), DisposeAfterUse::YES);
+ } else {
+ _mixerChannels[channel].stream->queueBuffer(
+ data,
+ _mixerChannels[channel].sampleLen,
+ DisposeAfterUse::YES,
+ mixerGetOutputFlags());
+ }
+ } else {
+ _mixerChannels[channel].callbackOnNextFrame = false;
+ _mixerChannels[channel].stream = nullptr;
+ }
+
+ return true;
+}
+
+bool HEMixer::mixerStartSpoolingChannel(
+ int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
+ int volume, int callbackID, uint32 flags) {
+
+ uint32 initialReadCount;
+
+ if ((channel < 0) || (channel > MIXER_MAX_CHANNELS))
+ return false;
+
+ if (_mixerChannels[channel].flags != CHANNEL_EMPTY_FLAGS) {
+ if (!(_mixerChannels[channel].flags & CHANNEL_FINISHED)) {
+
+ _mixerChannels[channel].flags |= CHANNEL_FINISHED;
+ _mixerChannels[channel].flags &= ~CHANNEL_ACTIVE;
+
+ debug(5, "stopping handle on channel %d", channel);
+ _mixer->stopHandle(_mixerChannels[channel].handle);
+ _mixerChannels[channel].stream = nullptr;
+
+ // Signal the sound engine that a sound has finished playing
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_STOPPED, callbackID);
+ }
+ }
+
+ if (flags != CHANNEL_EMPTY_FLAGS) {
+ if (sampleLen <= 0) {
+ error("HEMixer::mixerStartSpoolingChannel(): Sample invalid size %d", sampleLen);
+ }
+
+ if ((flags & CHANNEL_LOOPING) && (sampleLen <= MIXER_PCM_CHUNK_SIZE)) {
+ error("HEMixer::mixerStartSpoolingChannel(): Sample too small to loop (%d)", sampleLen);
+ }
+ }
+
+ _mixerChannels[channel].flags = flags | CHANNEL_SPOOLING;
+ _mixerChannels[channel].volume = MIN<int>(MAX<int>(0, volume), 255);
+ _mixerChannels[channel].number = channel;
+ _mixerChannels[channel].frequency = frequency;
+ _mixerChannels[channel].callbackID = callbackID;
+ _mixerChannels[channel].lastReadPosition = 0;
+ _mixerChannels[channel].dataOffset = 0;
+ _mixerChannels[channel].globType = rtSpoolBuffer;
+ _mixerChannels[channel].globNum = channel + 1;
+ _mixerChannels[channel].endSampleAdjustment = 0;
+
+ if (_vm->_res->createResource(
+ (ResType)_mixerChannels[channel].globType,
+ _mixerChannels[channel].globNum, SPOOL_CHUNK_SIZE) == nullptr) {
+ return false;
+ }
+
+ _mixerChannels[channel].fileHandle = &sampleFileIOHandle;
+ _mixerChannels[channel].sampleLen = sampleLen;
+ initialReadCount = MIN<int>(sampleLen, SPOOL_CHUNK_SIZE);
+ _mixerChannels[channel].initialSpoolingFileOffset = sampleFileIOHandle.pos();
+
+ _mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
+
+ _mixer->playStream(
+ Audio::Mixer::kPlainSoundType,
+ &_mixerChannels[channel].handle,
+ _mixerChannels[channel].stream,
+ -1,
+ volume);
+
+ byte *data = (byte *)malloc(initialReadCount * sizeof(byte));
+ if (data) {
+ sampleFileIOHandle.read(data, initialReadCount);
+
+ _mixerChannels[channel].lastReadPosition += initialReadCount;
+
+ byte *dataTmp = data;
+ int rampUpSampleCount = 64;
+ for (int i = 0; i < rampUpSampleCount; i++) {
+ *dataTmp = 128 + (((*dataTmp - 128) * i) / rampUpSampleCount);
+ dataTmp++;
+ }
+
+ _mixerChannels[channel].stream->queueBuffer(
+ data,
+ initialReadCount,
+ DisposeAfterUse::YES,
+ mixerGetOutputFlags());
+ }
+
+ return true;
+}
+
+byte HEMixer::mixerGetOutputFlags() {
+ // Just plain mono 8-bit sound...
+
+ byte streamFlags = 0;
+ streamFlags |= Audio::FLAG_UNSIGNED;
+
+#ifdef SCUMM_LITTLE_ENDIAN
+ streamFlags |= Audio::FLAG_LITTLE_ENDIAN;
+#endif
+
+ return streamFlags;
+}
+
+
+/* --- MILES FUNCTIONS --- */
+
+void HEMixer::milesServiceAllStreams() {
+ for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
+ if (_milesChannels[i]._stream.streamObj != nullptr)
+ _milesChannels[i].serviceStream();
+ }
+}
+
+void HEMixer::milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers) {
+ assert(channel >= 0 && channel < ARRAYSIZE(_milesChannels));
+
+ if (channel >= 0 && channel < ARRAYSIZE(_milesChannels))
+ _milesChannels[channel].startSpoolingChannel(filename, offset, flags, modifiers, _mixer);
}
-bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 soundData, uint32 offset, int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
+bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 soundData, uint32 offset,
+ int sampleLen, int bitsPerSample, int sampleChannels, int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...) {
+
// This function executes either a one-shot or a looping sfx/voice file
// which will entirely fit in RAM (as opposed to spooling sounds)
debug(5, "HEMixer::milesStartChannel(): Starting sound with resource %d in channel %d, modifiers v %d p %d f %d",
@@ -395,17 +793,13 @@ void HEMixer::milesStopAndCallback(int channel, int messageId) {
((SoundHE *)_vm->_sound)->digitalSoundCallback(messageId, channel);
}
-void HEMixer::milesRestoreChannel(int channel) {
- milesStopAndCallback(channel, HSND_SOUND_TIMEOUT);
-}
-
void HEMixer::milesFeedMixer() {
if (_mixerPaused) {
return;
}
// Feed the audio streams
- serviceAllStreams();
+ milesServiceAllStreams();
// Check for any sound which has finished playing and call the milesStopAndCallback function
for (int i = 0; i < MILES_MAX_CHANNELS; i++) {
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 917a54f6554..1978afd1c4f 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -34,12 +34,8 @@
#include "audio/decoders/adpcm.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
+#include "audio/rate.h"
-namespace Audio {
-class AudioStream;
-class Mixer;
-class QueuingAudioStream;
-} // namespace Audio
namespace Scumm {
@@ -49,11 +45,15 @@ namespace Scumm {
#define CHANNEL_LOOPING 0x00000004
#define CHANNEL_LAST_CHUNK 0x00000008
#define CHANNEL_SPOOLING 0x00000010
-#define CHANNEL_SPOOL_READ 0x00000020
-#define CHANNEL_SPOOL_CRITICAL 0x00000040
#define CHANNEL_CALLBACK_EARLY 0x00000080
#define CHANNEL_SOFT_REMIX 0x00000100
+#define MIXER_MAX_CHANNELS 8
+#define MIXER_PCM_CHUNK_SIZE 4096
+#define MIXER_MAX_QUEUED_STREAMS 8
+#define MIXER_DEFAULT_SAMPLE_RATE 11025
+#define SPOOL_CHUNK_SIZE (8 * 1024)
+
#define MILES_MAX_CHANNELS 8
#define MILES_PCM_CHUNK_SIZE 4096u
#define MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM 4u
@@ -118,64 +118,87 @@ protected:
Audio::Mixer *_mixer;
bool _useMilesSoundSystem = false;
bool _mixerPaused = false;
+ int _pauseCount = 0;
HEMilesChannel _milesChannels[MILES_MAX_CHANNELS];
+ struct HEMixerChannel {
+ Audio::SoundHandle handle;
+ Audio::QueuingAudioStream *stream = nullptr;
+ Common::File *fileHandle = nullptr;
+ int number;
+ int volume;
+ int frequency;
+ int globType;
+ int globNum;
+ int callbackID;
+ int endSampleAdjustment;
+ uint32 lastReadPosition;
+ uint32 initialSpoolingFileOffset;
+ uint32 sampleLen;
+ uint32 dataOffset;
+ uint32 flags;
+ bool callbackOnNextFrame;
+ };
+
+ HEMixerChannel _mixerChannels[MIXER_MAX_CHANNELS];
+
+
public:
HEMixer(Audio::Mixer *mixer, ScummEngine_v60he *vm, bool useMiles);
~HEMixer();
- void *getMilesSoundSystemObject();
bool initSoftMixerSubSystem();
void deinitSoftMixerSubSystem();
- void endNeglectProcess();
- void startLongNeglectProcess();
- bool forceMusicBufferFill();
- bool isMixerDisabled();
bool stopChannel(int channel);
void stopAllChannels();
- bool changeChannelVolume(int channel, int volume, bool soft);
- void softRemixAllChannels();
- void premixUntilCritical();
bool pauseMixerSubSystem(bool paused);
void feedMixer();
- void serviceAllStreams();
int getChannelCurrentPosition(int channel);
-
+ bool changeChannelVolume(int channel, int volume, bool soft);
bool startChannelNew(
int channel, int globType, int globNum, uint32 soundData, uint32 offset,
int sampleLen, int frequency, int bitsPerSample, int sampleChannels,
const HESoundModifiers &modifiers, int callbackId, int32 flags, ...);
-
bool startChannel(
int channel, int globType, int globNum, uint32 sampleDataOffset,
int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...);
-
bool startSpoolingChannel(
int channel, Common::File &spoolingFile, int sampleLen, int frequency,
- int volume, int callbackID, int32 flags, ...);
+ int volume, int callbackID, int32 flags);
+ /* --- MILES MIXER CODE --- */
bool isMilesActive();
- bool changeChannelVolume(int channel, int newVolume, int soft_flag);
void milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers);
- int hsFindSoundQueue(int sound);
- bool mixerStartChannel(
- int channel, int globType, int globNum, uint32 sampleDataOffset,
- int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...);
-
bool milesStartChannel(
int channel, int globType, int globNum, uint32 sound_data, uint32 offset,
int sampleLen, int bitsPerSample, int sampleChannels,
int frequency, HESoundModifiers modifiers, int callbackID, uint32 flags, ...);
-
bool milesStopChannel(int channel);
void milesStopAllSounds();
void milesModifySound(int channel, int offset, HESoundModifiers modifiers, int flags);
void milesStopAndCallback(int channel, int messageId);
- void milesRestoreChannel(int channel);
void milesFeedMixer();
+ void milesServiceAllStreams();
bool milesPauseMixerSubSystem(bool paused);
byte *milesGetAudioDataFromResource(int globType, int globNum, uint32 dataOffset, uint16 &compType, uint16 &blockAlign, uint32 &dataSize);
+
+ /* --- SOFTWARE MIXER CODE --- */
+ bool mixerInitMyMixerSubSystem();
+ bool mixerDeinitMyMixerSubSystem();
+ void mixerFeedMixer();
+ bool mixerIsMixerDisabled();
+ bool mixerStopChannel(int channel);
+ bool mixerChangeChannelVolume(int channel, int volume, bool soft);
+ int mixerGetChannelCurrentPosition(int channel);
+ bool mixerPauseMixerSubSystem(bool paused);
+ bool mixerStartChannel(
+ int channel, int globType, int globNum, uint32 sampleDataOffset,
+ int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...);
+ bool mixerStartSpoolingChannel(
+ int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
+ int volume, int callbackID, uint32 flags);
+ byte mixerGetOutputFlags();
};
} // End of namespace Scumm
diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp
index e74921e3610..a1bb059c4f5 100644
--- a/engines/scumm/he/script_v60he.cpp
+++ b/engines/scumm/he/script_v60he.cpp
@@ -1010,7 +1010,7 @@ void ScummEngine_v60he::o60_soundOps() {
}
break;
case SO_SOUND_VOLUME_RAMP:
- // WORKAROUND: For error in room script 228 (room 2) of fbear.
+ // No-op in the original as well.
break;
case SO_SOUND_FREQUENCY:
// Fatty Bear's Birthday surprise uses this when playing the
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 773d6a718b5..633d36f9fe5 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -61,7 +61,7 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
memset(_heChannel, 0, sizeof(_heChannel));
- _useMilesSoundSystem =
+ bool useMilesSoundSystem =
parent->_game.id == GID_MOONBASE ||
parent->_game.id == GID_BASEBALL2003 ||
parent->_game.id == GID_BASKETBALL ||
@@ -69,7 +69,7 @@ SoundHE::SoundHE(ScummEngine *parent, Audio::Mixer *mixer, Common::Mutex *mutex)
parent->_game.id == GID_SOCCER2004 ||
parent->_game.id == GID_PJGAMES;
- _heMixer = new HEMixer(_mixer, _vm, _useMilesSoundSystem);
+ _heMixer = new HEMixer(_mixer, _vm, useMilesSoundSystem);
}
SoundHE::~SoundHE() {
@@ -92,6 +92,10 @@ void SoundHE::startSound(int sound, int heOffset, int heChannel, int heFlags, in
void SoundHE::addSoundToQueue(int sound, int heOffset, int heChannel, int heFlags, int heFreq, int hePan, int heVol) {
int i = _soundQueuePos;
+
+ if (_vm->_game.heversion < 95 && heChannel == -1)
+ heChannel = 1;
+
while (i--) {
if (_soundQueue[i].sound == sound && !(heFlags & ScummEngine_v70he::HESndFlags::HE_SND_APPEND))
// Sound is already queued
@@ -105,7 +109,7 @@ void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, in
int channel = hsFindSoundChannel(sound);
if (channel >= 0 && _heChannel[channel].sound) {
// The implementation for this is only available for the Miles mixer
- if (_useMilesSoundSystem) {
+ if (_heMixer->isMilesActive()) {
_heMixer->milesModifySound(channel, offset, HESoundModifiers(frequencyShift, pan, volume), flags);
}
}
@@ -114,44 +118,35 @@ void SoundHE::modifySound(int sound, int offset, int frequencyShift, int pan, in
void SoundHE::processSoundQueues() {
int snd, heOffset, heChannel, heFlags, heFreq, hePan, heVol;
- if (_vm->_game.heversion >= 72) {
- for (int i = 0; i <_soundQueuePos; i++) {
- snd = _soundQueue[i].sound;
- heOffset = _soundQueue[i].offset;
- heChannel = _soundQueue[i].channel;
- heFlags = _soundQueue[i].flags;
- heFreq = _soundQueue[i].freq;
- hePan = _soundQueue[i].pan;
- heVol = _soundQueue[i].vol;
-
- if (snd) {
- if (_vm->_game.heversion < 99) {
- triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers());
- } else {
- triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers(heFreq, hePan, heVol));
- }
- }
- }
- _soundQueuePos = 0;
- } else {
- while (_soundQueuePos) {
- _soundQueuePos--;
- snd = _soundQueue[_soundQueuePos].sound;
- heOffset = _soundQueue[_soundQueuePos].offset;
- heChannel = _soundQueue[_soundQueuePos].channel;
- heFlags = _soundQueue[_soundQueuePos].flags;
-
- if (snd) {
- triggerSound(snd, heOffset, heChannel, heFlags, HESoundModifiers());
+ for (int i = 0; i <_soundQueuePos; i++) {
+ snd = _soundQueue[i].sound;
+ heOffset = _soundQueue[i].offset;
+ heChannel = _soundQueue[i].channel;
+ heFlags = _soundQueue[i].flags;
+ heFreq = _soundQueue[i].freq;
+ hePan = _soundQueue[i].pan;
+ heVol = _soundQueue[i].vol;
+
+ HESoundModifiers modifiers =
+ _vm->_game.heversion < 99 ? HESoundModifiers() : HESoundModifiers(heFreq, hePan, heVol);
+
+ if (snd) {
+ if (((_vm->_game.heversion >= 80) && (_vm->_game.heversion < 95)) &&
+ (_soundQueue[i].flags & ScummEngine_v70he::HESndFlags::HE_SND_VOL)) {
+ setSoundVolume(snd, _soundQueue[i].vol);
+ } else {
+ triggerSound(snd, heOffset, heChannel, heFlags, modifiers);
}
}
}
+ _soundQueuePos = 0;
+
Sound::processSoundQueues();
}
int SoundHE::isSoundRunning(int sound) const {
- if (_vm->_game.heversion >= 70) {
+ if (_vm->_game.heversion >= 70 || sound == HSND_TALKIE_SLOT) {
if (sound >= HSND_CHANNEL_0) {
sound = _heChannel[sound - HSND_CHANNEL_0].sound;
}
@@ -164,19 +159,19 @@ int SoundHE::isSoundRunning(int sound) const {
} else {
if (sound == -2) {
sound = _heChannel[0].sound;
+ if (hsFindSoundChannel(sound) != -1) {
+ return sound;
+ }
} else if (sound == -1) {
sound = _currentMusic;
+ if (_vm->_musicEngine && _vm->_musicEngine->getSoundStatus(sound))
+ return sound;
+ } else if (sound > 0) {
+ if (hsFindSoundChannel(sound) != -1) {
+ return sound;
+ }
}
- if (_mixer->isSoundIDActive(sound))
- return sound;
-
- if (isSoundInQueue(sound))
- return sound;
-
- if (_vm->_musicEngine && _vm->_musicEngine->getSoundStatus(sound))
- return sound;
-
return 0;
}
}
@@ -185,94 +180,90 @@ bool SoundHE::isSoundInUse(int sound) const {
// If our sound is a channel number, search for it
// between the currently playing sounds first, then
// search the sound queue...
- if (sound >= HSND_CHANNEL_0) {
- int channel = sound - HSND_CHANNEL_0;
- sound = _heChannel[channel].sound;
- if (sound)
- return sound;
+ if (_vm->_game.heversion >= 70 || (_vm->_game.heversion < 70 && sound > 0)) {
+ if (sound >= HSND_CHANNEL_0) {
+ int channel = sound - HSND_CHANNEL_0;
+ sound = _heChannel[channel].sound;
+
+ if (sound)
+ return sound;
- for (int i = 0; i < _soundQueuePos; i++) {
- if (_soundQueue[i].channel == channel) {
- return _soundQueue[i].sound;
+ for (int i = 0; i < _soundQueuePos; i++) {
+ if (_soundQueue[i].channel == channel) {
+ return _soundQueue[i].sound;
+ }
}
- }
- return 0;
- }
+ return 0;
+ }
- // ...otherwise the sound parameter is a proper
- // sound number, so search the queue
- int i = _soundQueuePos;
- while (i) {
- if (sound == _soundQueue[--i].sound)
- return sound;
+ // ...otherwise the sound parameter is a proper
+ // sound number, so search the queue
+ int i = _soundQueuePos;
+ while (i) {
+ if (sound == _soundQueue[--i].sound)
+ return sound;
+ }
}
// If it's not in the queue, look to see if it is actually playing
- return (isSoundRunning(sound));
+ return isSoundRunning(sound);
}
void SoundHE::stopSound(int sound) {
- if (_vm->_game.heversion >= 70) {
- int channel = -1;
-
- if (sound >= HSND_CHANNEL_0 && sound <= HSND_CHANNEL_7) {
- channel = sound - HSND_CHANNEL_0;
-
- if (_heChannel[channel].sound) {
- sound = _heChannel[channel].sound;
- stopDigitalSound(sound);
- }
+ int channel = -1;
- for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
- if (_soundQueue[i].channel == channel)
- _soundQueue[i].sound = 0;
- }
- } else {
- if (_vm->_game.heversion >= 95 && sound == HSND_DYN_SOUND_CHAN) {
- for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
- if (_soundQueue[i].channel == HSND_DYN_SOUND_CHAN) {
- _soundQueue[i].sound = 0;
- }
- }
- } else {
- if (hsFindSoundChannel(sound) != -1) {
- stopDigitalSound(sound);
- }
+ if (_vm->_game.heversion == 60) {
+ if (sound == -2) {
+ sound = _heChannel[0].sound;
+ } else if (sound == -1) {
+ sound = _currentMusic;
+ Sound::stopSound(sound);
- for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
- if (_soundQueue[i].sound == sound)
- _soundQueue[i].sound = 0;
+ for (int i = 0; i < ARRAYSIZE(_heChannel); i++) {
+ if (_heChannel[i].sound == sound) {
+ _heChannel[i].clearChannel();
}
}
+ return;
}
+ }
- if ((sound == HSND_TALKIE_SLOT) || (channel == _vm->VAR(_vm->VAR_TALK_CHANNEL))) {
- _vm->_talkDelay = 0;
+ if (sound >= HSND_CHANNEL_0 && sound <= HSND_CHANNEL_7) {
+ channel = sound - HSND_CHANNEL_0;
+
+ if (_heChannel[channel].sound) {
+ sound = _heChannel[channel].sound;
+ stopDigitalSound(sound);
}
- } else if (_vm->_game.heversion >= 60) {
- if (sound == -2) {
- sound = _heChannel[0].sound;
- } else if (sound == -1) {
- sound = _currentMusic;
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].channel == channel)
+ _soundQueue[i].sound = 0;
}
+ } else {
+ if (_vm->_game.heversion >= 95 && sound == HSND_DYN_SOUND_CHAN) {
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].channel == HSND_DYN_SOUND_CHAN) {
+ _soundQueue[i].sound = 0;
+ }
+ }
+ } else {
+ if (hsFindSoundChannel(sound) != -1) {
+ stopDigitalSound(sound);
+ }
- Sound::stopSound(sound);
-
- for (int i = 0; i < ARRAYSIZE(_heChannel); i++) {
- if (_heChannel[i].sound == sound) {
- _heChannel[i].sound = 0;
- _heChannel[i].priority = 0;
- _heChannel[i].frequency = 0;
- _heChannel[i].timeout = 0;
- _heChannel[i].hasSoundTokens = false;
- _heChannel[i].codeOffset = 0;
- memset(_heChannel[i].soundVars, 0, sizeof(_heChannel[i].soundVars));
+ for (int i = 0; i < ARRAYSIZE(_soundQueue); i++) {
+ if (_soundQueue[i].sound == sound)
+ _soundQueue[i].sound = 0;
}
}
}
+
+ if ((sound == HSND_TALKIE_SLOT) || (_vm->VAR_TALK_CHANNEL != 0xFF && channel == _vm->VAR(_vm->VAR_TALK_CHANNEL))) {
+ _vm->_talkDelay = 0;
+ }
}
void SoundHE::stopAllSounds() {
@@ -436,6 +427,20 @@ void SoundHE::setSoundVar(int sound, int var, int val) {
}
}
+void SoundHE::setSoundVolume(int sound, int volume) {
+ int channel;
+
+ if (sound < HSND_CHANNEL_0) {
+ if ((channel = hsFindSoundChannel(sound)) == -1) {
+ return;
+ }
+ } else {
+ channel = sound - HSND_CHANNEL_0;
+ }
+
+ _heMixer->changeChannelVolume(channel, volume, true);
+}
+
void SoundHE::setOverrideFreq(int freq) {
_overrideFreq = freq;
}
@@ -573,6 +578,10 @@ void SoundHE::unqueueSoundCallbackScripts() {
args[2] = 0;
args[3] = 0;
+ debug(5, "SoundHE::unqueueSoundCallbackScripts(): dequeued callback for sound %d in channel %d",
+ _soundCallbackScripts[i].sound,
+ _soundCallbackScripts[i].channel);
+
_vm->runScript(_vm->VAR(_vm->VAR_SOUND_CALLBACK_SCRIPT), 0, 0, args);
}
@@ -636,7 +645,9 @@ void SoundHE::digitalSoundCallback(int message, int channel) {
_heChannel[channel].clearChannel();
- queueSoundCallbackScript(sound, channel, message);
+ if (_vm->_game.heversion >= 80)
+ queueSoundCallbackScript(sound, channel, message);
+
break;
}
@@ -648,6 +659,7 @@ void SoundHE::queueSoundCallbackScript(int sound, int channel, int message) {
if (!_mixer->isReady())
return;
+ debug(5, "SoundHE::queueSoundCallbackScript(): callback for channel %d, sound %d, attempting queueing...", channel, sound);
// Check if we are about to duplicate this event...
for (int i = 0; i < _soundCallbacksQueueSize; i++) {
@@ -950,7 +962,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
pcm.wBlockAlign = _heSpoolingMusicFile.readUint16LE();
pcm.wBitsPerSample = _heSpoolingMusicFile.readUint16LE();
- if (_useMilesSoundSystem) {
+ if (_heMixer->isMilesActive()) {
if (pcm.wFormatTag != WAVE_FORMAT_PCM && pcm.wFormatTag != WAVE_FORMAT_IMA_ADPCM) {
error("SoundHE::triggerSpoolingSound(): Illegal .wav format for Miles mixer, song %d - %d",
song, pcm.wFormatTag);
@@ -1008,7 +1020,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
error("SoundHE::triggerSpoolingSound(): Illegal spooling sound %d, id %s", song, tag2str(id));
}
- if (_useMilesSoundSystem) {
+ if (_heMixer->isMilesActive()) {
if (offset)
debug("SoundHE::triggerSpoolingSound(): Starting offsets into music files not supported with Miles currently");
@@ -1683,8 +1695,6 @@ void SoundHE::createSound(int baseSound, int sound) {
_vm->_res->unlock(rtSound, baseSound);
_vm->_res->unlock(rtSound, sound);
-
- _heMixer->softRemixAllChannels();
}
byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 20e5d95981a..07c288b32c2 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -108,7 +108,7 @@ namespace Scumm {
// Used both in SoundHE and HEMixer
struct HESoundModifiers {
HESoundModifiers(int mFrequencyShift, int mPan, int mVolume) {
- // Hey, turns out Moonbase Commander just LOVES to send out invalid modifiers :-)
+ // Hey, turns out Moonbase Commander just LOVES sending out invalid modifiers :-)
/*
assert(mFrequencyShift >= HSND_SOUND_FREQ_BASE / HSND_MAX_FREQ_RATIO);
assert(mFrequencyShift <= HSND_SOUND_FREQ_BASE * HSND_MAX_FREQ_RATIO);
@@ -199,8 +199,6 @@ public: // Used by createSound()
} _heChannel[HSND_MAX_CHANNELS];
- bool _useMilesSoundSystem = false;
-
// Used throughout script functions
int32 _createSndId;
int32 _createSndLastAppend;
@@ -228,6 +226,7 @@ public:
int getSoundPosition(int sound);
int getSoundVar(int sound, int var);
void setSoundVar(int sound, int var, int val);
+ void setSoundVolume(int sound, int volume);
void triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerSpoolingSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers);
void triggerDigitalSound(int soundId, int heOffset, int heChannel, int heFlags);
diff --git a/engines/scumm/resource.cpp b/engines/scumm/resource.cpp
index e69dff29d0f..e0abe6a9a63 100644
--- a/engines/scumm/resource.cpp
+++ b/engines/scumm/resource.cpp
@@ -869,9 +869,10 @@ byte *ResourceManager::createResource(ResType type, ResId idx, uint32 size) {
_types[type][idx]._address = ptr;
_types[type][idx]._size = size;
setResourceCounter(type, idx, 1);
- return ptr;
_vm->_insideCreateResource--;
+
+ return ptr;
}
ResourceManager::Resource::Resource() {
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 23a22c219cc..c52b6fe67c9 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2410,7 +2410,7 @@ Common::Error ScummEngine::go() {
// Run the main loop
scummLoop(delta);
- if (_game.heversion >= 70) {
+ if (_game.heversion >= 60) {
((SoundHE *)_sound)->feedMixer();
}
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index 561dc154434..ac263983b43 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1568,7 +1568,6 @@ protected:
byte _charsetBuffer[512];
bool _keepText = false;
- bool _actorShouldStopTalking = false;
byte _msgCount = 0;
int _nextLeft = 0, _nextTop = 0;
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index 3916d14c892..9fc23198c51 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -207,10 +207,6 @@ void Sound::startSound(int sound, int offset, int channel, int flags, int freq,
_vm->VAR(_vm->VAR_LAST_SOUND) = sound;
_lastSound = sound;
- // HE music resources are in separate file
- if (sound <= _vm->_numSounds)
- _vm->ensureResourceLoaded(rtSound, sound);
-
addSoundToQueue(sound, offset, channel, flags, freq, pan, volume);
}
@@ -633,7 +629,7 @@ void Sound::processSfxQueues() {
#endif
}
} else if (_vm->_game.heversion >= 60) {
- finished = !isSoundRunning(1);
+ finished = !isSoundInUse(1);
} else {
finished = !_mixer->isSoundHandleActive(*_talkChannelHandle);
}
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index f26b45da7a4..6680ce05c26 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -847,7 +847,7 @@ void ScummEngine_v2::drawSentence() {
void ScummEngine::CHARSET_1() {
Actor *a;
- if (_game.heversion >= 70 && _haveMsg == 3) {
+ if (_game.heversion >= 60 && _haveMsg == 3) {
stopTalk();
return;
}
@@ -1180,7 +1180,7 @@ void ScummEngine::CHARSET_1() {
} else {
if (_game.features & GF_16BIT_COLOR) {
// HE games which use sprites for subtitles
- } else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(1)) {
+ } else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(HSND_TALKIE_SLOT)) {
// Special case for HE games
} else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
// Special case for Loom (CD), since it only uses CD audio.for sound
Commit: 85cde3582db0bc9748bbf097658f49b5448ecc29
https://github.com/scummvm/scummvm/commit/85cde3582db0bc9748bbf097658f49b5448ecc29
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Remove leftover debug messages
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 0ab69399728..b20b7139ee9 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -223,7 +223,6 @@ void HEMixer::mixerFeedMixer() {
_mixerChannels[i].flags |= CHANNEL_FINISHED;
_mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
- debug(5, "hit finish on channel %d", i);
_mixerChannels[i].stream->finish();
_mixerChannels[i].stream = nullptr;
((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
@@ -240,7 +239,6 @@ void HEMixer::mixerFeedMixer() {
_mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
_mixerChannels[i].flags |= CHANNEL_FINISHED;
- debug(5, "hit finish on spooling channel %d", i);
_mixerChannels[i].stream->finish();
_mixerChannels[i].stream = nullptr;
_mixerChannels[i].callbackOnNextFrame = true;
@@ -349,7 +347,6 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].flags |= CHANNEL_FINISHED;
_mixerChannels[channel].flags &= ~CHANNEL_ACTIVE;
- debug(5, "stopping handle on channel %d", channel);
_mixer->stopHandle(_mixerChannels[channel].handle);
_mixerChannels[channel].stream = nullptr;
@@ -476,7 +473,6 @@ bool HEMixer::mixerStartSpoolingChannel(
_mixerChannels[channel].flags |= CHANNEL_FINISHED;
_mixerChannels[channel].flags &= ~CHANNEL_ACTIVE;
- debug(5, "stopping handle on channel %d", channel);
_mixer->stopHandle(_mixerChannels[channel].handle);
_mixerChannels[channel].stream = nullptr;
Commit: 26b6a9d6ef8b963b706e5b1328c114a62601e1bd
https://github.com/scummvm/scummvm/commit/26b6a9d6ef8b963b706e5b1328c114a62601e1bd
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Reimplement support for Lokalizator.big files
Changed paths:
engines/scumm/he/sound_he.cpp
engines/scumm/scumm.h
engines/scumm/string.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 633d36f9fe5..bf3e412d0f8 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -1549,6 +1549,12 @@ void SoundHE::playVoice(uint32 offset, uint32 length) {
file.seek(offset, SEEK_SET);
+ if (_vm->hasLocalizer()) {
+ file.seek(offset + 4, SEEK_SET);
+ length = file.readUint32BE();
+ file.seek(offset, SEEK_SET);
+ }
+
_vm->_res->createResource(rtSound, 1, length);
ptr = _vm->getResourceAddress(rtSound, 1);
file.read(ptr, length);
diff --git a/engines/scumm/scumm.h b/engines/scumm/scumm.h
index ac263983b43..20b0ab0774b 100644
--- a/engines/scumm/scumm.h
+++ b/engines/scumm/scumm.h
@@ -1621,6 +1621,7 @@ public:
byte *get2byteCharPtr(int idx);
bool isScummvmKorTarget();
+ bool hasLocalizer();
//protected:
byte *_2byteFontPtr = nullptr;
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 6680ce05c26..a6650119727 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -498,7 +498,6 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
}
value[i] = 0;
digiTalkieLength = atoi(value);
- // TODO: Properly wire up Localizer to new sound system
((SoundHE *)_sound)->playVoice(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset, digiTalkieLength);
break;
case 104:
@@ -520,9 +519,6 @@ bool ScummEngine_v72he::handleNextCharsetCode(Actor *a, int *code) {
i++;
}
value[i] = 0;
- digiTalkieOffset = atoi(value);
- //digiTalkieLength = 0;
- // TODO: Properly wire up Localizer to new sound system
((SoundHE *)_sound)->playVoiceFile(value);
break;
case 119:
@@ -1801,6 +1797,10 @@ int ScummEngine::convertStringMessage(byte *dst, int dstSize, int var) {
}
+bool ScummEngine::hasLocalizer() {
+ return _localizer != nullptr;
+}
+
#pragma mark -
#pragma mark --- Charset initialisation ---
#pragma mark -
Commit: d6812afea78b3dd4b36465447a9a73a7916de0a2
https://github.com/scummvm/scummvm/commit/d6812afea78b3dd4b36465447a9a73a7916de0a2
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE: Remove leftover comment
Changed paths:
engines/scumm/string.cpp
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index a6650119727..0e68cced28e 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -424,7 +424,6 @@ bool ScummEngine::handleNextCharsetCode(Actor *a, int *code) {
buffer += 14;
if (_game.heversion >= 60) {
#ifdef ENABLE_HE
- // TODO: Properly wire up Localizer to new sound system
((SoundHE *)_sound)->playVoice(_localizer ? _localizer->mapTalk(digiTalkieOffset) : digiTalkieOffset, digiTalkieLength);
#else
((SoundHE *)_sound)->playVoice(digiTalkieOffset, digiTalkieLength);
Commit: 6da4114418cf22ed8a97fc05e463b30e46cbf1ab
https://github.com/scummvm/scummvm/commit/6da4114418cf22ed8a97fc05e463b30e46cbf1ab
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix subtitles behaviour
Changed paths:
engines/scumm/string.cpp
diff --git a/engines/scumm/string.cpp b/engines/scumm/string.cpp
index 0e68cced28e..c14369c2659 100644
--- a/engines/scumm/string.cpp
+++ b/engines/scumm/string.cpp
@@ -1175,7 +1175,7 @@ void ScummEngine::CHARSET_1() {
} else {
if (_game.features & GF_16BIT_COLOR) {
// HE games which use sprites for subtitles
- } else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundRunning(HSND_TALKIE_SLOT)) {
+ } else if (_game.heversion >= 60 && !ConfMan.getBool("subtitles") && _sound->isSoundInUse(HSND_TALKIE_SLOT)) {
// Special case for HE games
} else if (_game.id == GID_LOOM && !ConfMan.getBool("subtitles") && (_sound->pollCD())) {
// Special case for Loom (CD), since it only uses CD audio.for sound
Commit: 7dd3014087cf8f11e07e75623d29c281a91b7b95
https://github.com/scummvm/scummvm/commit/7dd3014087cf8f11e07e75623d29c281a91b7b95
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Implement early callbacks and getChannelPosition()
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index b20b7139ee9..6aa3a17d18e 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -58,7 +58,7 @@ void HEMixer::deinitSoftMixerSubSystem() {
if (isMilesActive()) {
milesStopAllSounds();
} else {
- mixerDeinitMyMixerSubSystem();
+ mixerStopAllSounds();
}
}
@@ -94,12 +94,6 @@ void HEMixer::feedMixer() {
}
}
-
-int HEMixer::getChannelCurrentPosition(int channel) {
- // We are sure this will never be called with Miles on
- return mixerGetChannelCurrentPosition(channel);
-}
-
bool HEMixer::changeChannelVolume(int channel, int volume, bool soft) {
if (!isMilesActive()) {
return mixerChangeChannelVolume(channel, volume, soft);
@@ -204,8 +198,12 @@ bool HEMixer::mixerInitMyMixerSubSystem() {
return true;
}
-bool HEMixer::mixerDeinitMyMixerSubSystem() {
- return false;
+bool HEMixer::mixerStopAllSounds() {
+ for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ mixerStopChannel(i);
+ }
+
+ return true;
}
void HEMixer::mixerFeedMixer() {
@@ -219,13 +217,21 @@ void HEMixer::mixerFeedMixer() {
for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
if (!(_mixerChannels[i].flags & CHANNEL_LOOPING)) {
- if ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && _mixerChannels[i].stream->endOfData()) {
- _mixerChannels[i].flags |= CHANNEL_FINISHED;
- _mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
+ if (!(_mixerChannels[i].flags & CHANNEL_SPOOLING)) {
+ if (((_mixerChannels[i].flags & CHANNEL_ACTIVE) && _mixerChannels[i].stream->endOfData()) ||
+ ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK))) {
+
+ // For early callbacks which are bigger than the sample length
+ if (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK)
+ _mixerChannels[i].flags &= ~CHANNEL_LAST_CHUNK;
+
+ _mixerChannels[i].flags |= CHANNEL_FINISHED;
+ _mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
- _mixerChannels[i].stream->finish();
- _mixerChannels[i].stream = nullptr;
- ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
+ _mixerChannels[i].stream->finish();
+ _mixerChannels[i].stream = nullptr;
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
+ }
}
if (_mixerChannels[i].flags & CHANNEL_SPOOLING) {
@@ -261,9 +267,9 @@ void HEMixer::mixerFeedMixer() {
_mixerChannels[i].fileHandle->seek(curOffset, SEEK_CUR);
- int length = SPOOL_CHUNK_SIZE;
- if (_mixerChannels[i].lastReadPosition + SPOOL_CHUNK_SIZE >= _mixerChannels[i].sampleLen) {
- length = _mixerChannels[i].sampleLen % SPOOL_CHUNK_SIZE;
+ int length = MIXER_SPOOL_CHUNK_SIZE;
+ if (_mixerChannels[i].lastReadPosition + MIXER_SPOOL_CHUNK_SIZE >= _mixerChannels[i].sampleLen) {
+ length = _mixerChannels[i].sampleLen % MIXER_SPOOL_CHUNK_SIZE;
_mixerChannels[i].flags |= CHANNEL_LAST_CHUNK;
}
@@ -320,10 +326,6 @@ bool HEMixer::mixerChangeChannelVolume(int channel, int volume, bool soft) {
return true;
}
-int HEMixer::mixerGetChannelCurrentPosition(int channel) {
- return 0;
-}
-
bool HEMixer::mixerPauseMixerSubSystem(bool paused) {
_mixerPaused = paused;
@@ -380,6 +382,19 @@ bool HEMixer::mixerStartChannel(
va_start(params, flags);
_mixerChannels[channel].endSampleAdjustment = va_arg(params, int);
va_end(params);
+
+ // The original has a very convoluted way of making sure that the sample
+ // both callbacks earlier and stops earlier. We just set a shorter sample length
+ // as this should ensure the same effect on both ends. Hopefully it's still accurate.
+ if ((int)_mixerChannels[channel].sampleLen >= _mixerChannels[channel].endSampleAdjustment) {
+ _mixerChannels[channel].sampleLen -= _mixerChannels[channel].endSampleAdjustment;
+ } else {
+ // Sometimes a game can give an end sample adjustment which is bigger than the sample itself
+ // (looking at you, Backyard Baseball '97). In this case we should at least give one frame
+ // for the sound to play for a while.
+ _mixerChannels[channel].flags |= CHANNEL_LAST_CHUNK;
+ }
+
} else {
_mixerChannels[channel].endSampleAdjustment = 0;
}
@@ -396,20 +411,6 @@ bool HEMixer::mixerStartChannel(
if (!data)
return false;
- // The following feature is the only thing which would justify
- // the pain of having non-spooling audio files played as chunks.
- //
- // The purpose of this is to produce a callback before the end of
- // the sound, precisely endSampleAdjustment bytes earlier.
- //
- // I haven't seen this one used anywhere (except in puttcircus,
- // which only gives 2 as a value - which is a value which of course
- // does nothing even in the original). Please, (don't) prove me wrong :-)
- int offset = _mixerChannels[channel].endSampleAdjustment;
- if (offset != 0 && offset != 2)
- error("HEMixer::mixerStartChannel(): Unimplemented early callback with value %d, in room %d, for sound %d!\n",
- offset, _vm->_currentRoom, globNum);
-
ptr += sampleDataOffset;
memcpy(data, ptr, _mixerChannels[channel].sampleLen);
@@ -504,13 +505,13 @@ bool HEMixer::mixerStartSpoolingChannel(
if (_vm->_res->createResource(
(ResType)_mixerChannels[channel].globType,
- _mixerChannels[channel].globNum, SPOOL_CHUNK_SIZE) == nullptr) {
+ _mixerChannels[channel].globNum, MIXER_SPOOL_CHUNK_SIZE) == nullptr) {
return false;
}
_mixerChannels[channel].fileHandle = &sampleFileIOHandle;
_mixerChannels[channel].sampleLen = sampleLen;
- initialReadCount = MIN<int>(sampleLen, SPOOL_CHUNK_SIZE);
+ initialReadCount = MIN<int>(sampleLen, MIXER_SPOOL_CHUNK_SIZE);
_mixerChannels[channel].initialSpoolingFileOffset = sampleFileIOHandle.pos();
_mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 1978afd1c4f..b34e84ba36c 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -52,7 +52,7 @@ namespace Scumm {
#define MIXER_PCM_CHUNK_SIZE 4096
#define MIXER_MAX_QUEUED_STREAMS 8
#define MIXER_DEFAULT_SAMPLE_RATE 11025
-#define SPOOL_CHUNK_SIZE (8 * 1024)
+#define MIXER_SPOOL_CHUNK_SIZE (8 * 1024)
#define MILES_MAX_CHANNELS 8
#define MILES_PCM_CHUNK_SIZE 4096u
@@ -154,7 +154,6 @@ public:
void stopAllChannels();
bool pauseMixerSubSystem(bool paused);
void feedMixer();
- int getChannelCurrentPosition(int channel);
bool changeChannelVolume(int channel, int volume, bool soft);
bool startChannelNew(
int channel, int globType, int globNum, uint32 soundData, uint32 offset,
@@ -185,12 +184,11 @@ public:
/* --- SOFTWARE MIXER CODE --- */
bool mixerInitMyMixerSubSystem();
- bool mixerDeinitMyMixerSubSystem();
void mixerFeedMixer();
bool mixerIsMixerDisabled();
bool mixerStopChannel(int channel);
+ bool mixerStopAllSounds();
bool mixerChangeChannelVolume(int channel, int volume, bool soft);
- int mixerGetChannelCurrentPosition(int channel);
bool mixerPauseMixerSubSystem(bool paused);
bool mixerStartChannel(
int channel, int globType, int globNum, uint32 sampleDataOffset,
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index bf3e412d0f8..8bd082344d3 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -369,13 +369,11 @@ bool SoundHE::isSoundCodeUsed(int sound) {
int SoundHE::getChannelPosition(int channel) {
int soundPos;
- if (_vm->_game.heversion >= 80) {
- int frequency = _vm->_game.heversion >= 95 ? _heChannel[channel].frequency : HSND_DEFAULT_FREQUENCY;
- soundPos = (int)(((uint64)_vm->getHETimer(HSND_TIMER_SLOT + channel) * (uint64)frequency) / 1000);
- } else {
- soundPos = _heMixer->getChannelCurrentPosition(channel);
- }
+ int frequency = _vm->_game.heversion >= 95 ? _heChannel[channel].frequency : HSND_DEFAULT_FREQUENCY;
+ soundPos = (int)(((uint64)_vm->getHETimer(HSND_TIMER_SLOT + channel) * (uint64)frequency) / 1000);
+
+ debug(5, "SoundHE::getChannelPosition(): channel %d pos %d ms", channel, soundPos);
return soundPos;
}
@@ -1321,6 +1319,8 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
_heChannel[channel].sound = sound;
_heChannel[channel].priority = priority;
+ _vm->setHETimer(channel + HSND_TIMER_SLOT);
+
return;
}
Commit: 79803bbe54155c6ac2bef0085de25595e36a5261
https://github.com/scummvm/scummvm/commit/79803bbe54155c6ac2bef0085de25595e36a5261
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Reimplement separate volume controls
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 6aa3a17d18e..ca544b2f445 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -425,7 +425,7 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
_mixer->playStream(
- Audio::Mixer::kPlainSoundType,
+ globNum == 1 ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType,
&_mixerChannels[channel].handle,
_mixerChannels[channel].stream,
-1,
@@ -517,7 +517,7 @@ bool HEMixer::mixerStartSpoolingChannel(
_mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
_mixer->playStream(
- Audio::Mixer::kPlainSoundType,
+ Audio::Mixer::kMusicSoundType,
&_mixerChannels[channel].handle,
_mixerChannels[channel].stream,
-1,
Commit: 47301a17ffa1a7d654f5c5352788e0da8df762d8
https://github.com/scummvm/scummvm/commit/47301a17ffa1a7d654f5c5352788e0da8df762d8
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix music reloading after exiting save screen
o60_isSoundRunning() expects a proper sound number instead
of a boolean; whoops!
Changed paths:
engines/scumm/he/script_v60he.cpp
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/script_v60he.cpp b/engines/scumm/he/script_v60he.cpp
index a1bb059c4f5..2bd6d992bfc 100644
--- a/engines/scumm/he/script_v60he.cpp
+++ b/engines/scumm/he/script_v60he.cpp
@@ -1196,7 +1196,7 @@ void ScummEngine_v60he::o60_isSoundRunning() {
int snd = pop();
if (snd)
- snd = _sound->isSoundInUse(snd);
+ snd = _sound->isSoundRunning(snd);
push(snd);
}
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 8bd082344d3..12cf9faa512 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -146,6 +146,37 @@ void SoundHE::processSoundQueues() {
}
int SoundHE::isSoundRunning(int sound) const {
+ // If our sound is a channel number, search for it
+ // between the currently playing sounds first, then
+ // search the sound queue...
+
+ if (_vm->_game.heversion >= 70 || (_vm->_game.heversion < 70 && sound > 0)) {
+ if (sound >= HSND_CHANNEL_0) {
+ int channel = sound - HSND_CHANNEL_0;
+ sound = _heChannel[channel].sound;
+
+ if (sound)
+ return sound;
+
+ for (int i = 0; i < _soundQueuePos; i++) {
+ if (_soundQueue[i].channel == channel) {
+ return _soundQueue[i].sound;
+ }
+ }
+
+ return 0;
+ }
+
+ // ...otherwise the sound parameter is a proper
+ // sound number, so search the queue
+ int i = _soundQueuePos;
+ while (i) {
+ if (sound == _soundQueue[--i].sound)
+ return sound;
+ }
+ }
+
+ // If it's not in the queue, go check if it is actually playing
if (_vm->_game.heversion >= 70 || sound == HSND_TALKIE_SLOT) {
if (sound >= HSND_CHANNEL_0) {
sound = _heChannel[sound - HSND_CHANNEL_0].sound;
@@ -177,38 +208,8 @@ int SoundHE::isSoundRunning(int sound) const {
}
bool SoundHE::isSoundInUse(int sound) const {
- // If our sound is a channel number, search for it
- // between the currently playing sounds first, then
- // search the sound queue...
-
- if (_vm->_game.heversion >= 70 || (_vm->_game.heversion < 70 && sound > 0)) {
- if (sound >= HSND_CHANNEL_0) {
- int channel = sound - HSND_CHANNEL_0;
- sound = _heChannel[channel].sound;
-
- if (sound)
- return sound;
-
- for (int i = 0; i < _soundQueuePos; i++) {
- if (_soundQueue[i].channel == channel) {
- return _soundQueue[i].sound;
- }
- }
-
- return 0;
- }
-
- // ...otherwise the sound parameter is a proper
- // sound number, so search the queue
- int i = _soundQueuePos;
- while (i) {
- if (sound == _soundQueue[--i].sound)
- return sound;
- }
- }
- // If it's not in the queue, look to see if it is actually playing
- return isSoundRunning(sound);
+ return isSoundRunning(sound) != 0;
}
void SoundHE::stopSound(int sound) {
@@ -373,7 +374,6 @@ int SoundHE::getChannelPosition(int channel) {
int frequency = _vm->_game.heversion >= 95 ? _heChannel[channel].frequency : HSND_DEFAULT_FREQUENCY;
soundPos = (int)(((uint64)_vm->getHETimer(HSND_TIMER_SLOT + channel) * (uint64)frequency) / 1000);
- debug(5, "SoundHE::getChannelPosition(): channel %d pos %d ms", channel, soundPos);
return soundPos;
}
Commit: ae49dc0c7ea17271188aaf782b3fe2710b0a614e
https://github.com/scummvm/scummvm/commit/ae49dc0c7ea17271188aaf782b3fe2710b0a614e
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Reimplement separate volume controls (pt.2)
I forgot to do so for the Miles Sound System games :-)
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index ca544b2f445..bbc36ad5cf5 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -425,7 +425,7 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
_mixer->playStream(
- globNum == 1 ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType,
+ globNum == HSND_TALKIE_SLOT ? Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType,
&_mixerChannels[channel].handle,
_mixerChannels[channel].stream,
-1,
@@ -616,6 +616,10 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
// This flag signals that there's an active sound in our channel!
_milesChannels[channel]._audioHandleActive = true;
+ Audio::Mixer::SoundType soundType =
+ globNum == HSND_TALKIE_SLOT ?
+ Audio::Mixer::kSpeechSoundType : Audio::Mixer::kSFXSoundType;
+
// Play the sound, whether in one-shot fashion or as a loop
if (flags & CHANNEL_LOOPING) {
_milesChannels[channel]._playFlags = CHANNEL_LOOPING;
@@ -639,7 +643,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
}
if (stream) {
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
+ _mixer->playStream(soundType, &_milesChannels[channel]._audioHandle,
Audio::makeLoopingAudioStream(stream, 0), channel, 255, 0, DisposeAfterUse::NO);
}
@@ -680,7 +684,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
if (stream) {
stream->seek(msOffset);
- _mixer->playStream(Audio::Mixer::kPlainSoundType, &_milesChannels[channel]._audioHandle,
+ _mixer->playStream(soundType, &_milesChannels[channel]._audioHandle,
stream, channel, _milesChannels[channel]._modifiers.volume, scaledPan, DisposeAfterUse::NO);
}
}
@@ -976,7 +980,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
// Create our stream and start it
_stream.streamObj = Audio::makeQueuingAudioStream(_baseFrequency, (waveHeader.wChannels > 1));
- mixer->playStream(Audio::Mixer::kPlainSoundType, &_stream.streamHandle, _stream.streamObj, -1, 255, 0, DisposeAfterUse::NO);
+ mixer->playStream(Audio::Mixer::kMusicSoundType, &_stream.streamHandle, _stream.streamObj, -1, 255, 0, DisposeAfterUse::NO);
if (_dataFormat == WAVE_FORMAT_PCM) {
// Apply the modifiers and the loop flag, if available
Commit: 3e65e4c4aa451f5845c363bfc2f77f5ed523e62f
https://github.com/scummvm/scummvm/commit/3e65e4c4aa451f5845c363bfc2f77f5ed523e62f
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix early callbacks cutting sound too early
After some comparisons this seems the most accurate emulation
of the early callback behaviour.
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index bbc36ad5cf5..a121fed6fe1 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -218,19 +218,37 @@ void HEMixer::mixerFeedMixer() {
for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
if (!(_mixerChannels[i].flags & CHANNEL_LOOPING)) {
if (!(_mixerChannels[i].flags & CHANNEL_SPOOLING)) {
+
+ // Early callback! Play just one more audio frame from the residual data...
+ if (_mixerChannels[i].flags & CHANNEL_CALLBACK_EARLY) {
+
+ bool validEarlyCallback =
+ (_mixerChannels[i].flags & CHANNEL_ACTIVE) &&
+ !(_mixerChannels[i].flags & CHANNEL_LAST_CHUNK) &&
+ _mixerChannels[i].stream->numQueuedStreams() == 1 &&
+ _mixerChannels[i].residualData;
+
+ if (validEarlyCallback) {
+ _mixerChannels[i].flags |= CHANNEL_LAST_CHUNK;
+ }
+ }
+
if (((_mixerChannels[i].flags & CHANNEL_ACTIVE) && _mixerChannels[i].stream->endOfData()) ||
((_mixerChannels[i].flags & CHANNEL_ACTIVE) && (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK))) {
- // For early callbacks which are bigger than the sample length
+ bool isEarlyCallback = (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK);
+
+ // For early callbacks...
if (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK)
_mixerChannels[i].flags &= ~CHANNEL_LAST_CHUNK;
+
_mixerChannels[i].flags |= CHANNEL_FINISHED;
_mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
_mixerChannels[i].stream->finish();
_mixerChannels[i].stream = nullptr;
- ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID, isEarlyCallback);
}
}
@@ -377,7 +395,9 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].sampleLen = sampleLen;
_mixerChannels[channel].globType = globType;
_mixerChannels[channel].globNum = globNum;
+ _mixerChannels[channel].residualData = nullptr;
+ bool hasCallbackData = false;
if (flags & CHANNEL_CALLBACK_EARLY) {
va_start(params, flags);
_mixerChannels[channel].endSampleAdjustment = va_arg(params, int);
@@ -386,13 +406,17 @@ bool HEMixer::mixerStartChannel(
// The original has a very convoluted way of making sure that the sample
// both callbacks earlier and stops earlier. We just set a shorter sample length
// as this should ensure the same effect on both ends. Hopefully it's still accurate.
- if ((int)_mixerChannels[channel].sampleLen >= _mixerChannels[channel].endSampleAdjustment) {
- _mixerChannels[channel].sampleLen -= _mixerChannels[channel].endSampleAdjustment;
- } else {
- // Sometimes a game can give an end sample adjustment which is bigger than the sample itself
- // (looking at you, Backyard Baseball '97). In this case we should at least give one frame
- // for the sound to play for a while.
- _mixerChannels[channel].flags |= CHANNEL_LAST_CHUNK;
+ if (_mixerChannels[channel].endSampleAdjustment != 0) {
+ if ((int)_mixerChannels[channel].sampleLen >= _mixerChannels[channel].endSampleAdjustment) {
+ _mixerChannels[channel].sampleLen -= _mixerChannels[channel].endSampleAdjustment;
+ _mixerChannels[channel].residualData = (byte *)malloc(_mixerChannels[channel].endSampleAdjustment * sizeof(byte));
+ hasCallbackData = _mixerChannels[channel].residualData != nullptr;
+ } else {
+ // Sometimes a game can give an end sample adjustment which is bigger than the sample itself
+ // (looking at you, Backyard Baseball '97). In this case we should at least give one frame
+ // for the sound to play for a while.
+ _mixerChannels[channel].flags |= CHANNEL_LAST_CHUNK;
+ }
}
} else {
@@ -415,6 +439,14 @@ bool HEMixer::mixerStartChannel(
memcpy(data, ptr, _mixerChannels[channel].sampleLen);
+ // Residual early callback data
+ if (hasCallbackData) {
+ memcpy(
+ _mixerChannels[channel].residualData,
+ &ptr[_mixerChannels[channel].sampleLen],
+ _mixerChannels[channel].endSampleAdjustment);
+ }
+
byte *dataTmp = data;
int rampUpSampleCount = 64;
for (int i = 0; i < rampUpSampleCount; i++) {
@@ -451,6 +483,15 @@ bool HEMixer::mixerStartChannel(
DisposeAfterUse::YES,
mixerGetOutputFlags());
}
+
+ if (hasCallbackData && !(_mixerChannels[channel].flags & CHANNEL_LOOPING)) {
+ _mixerChannels[channel].stream->queueBuffer(
+ _mixerChannels[channel].residualData,
+ _mixerChannels[channel].endSampleAdjustment,
+ DisposeAfterUse::YES,
+ mixerGetOutputFlags());
+ }
+
} else {
_mixerChannels[channel].callbackOnNextFrame = false;
_mixerChannels[channel].stream = nullptr;
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index b34e84ba36c..3eba92adf80 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -139,6 +139,8 @@ protected:
uint32 dataOffset;
uint32 flags;
bool callbackOnNextFrame;
+ bool isUsingStreamOverride;
+ byte *residualData = nullptr; // For early callbacks
};
HEMixerChannel _mixerChannels[MIXER_MAX_CHANNELS];
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 12cf9faa512..7ffc71ab448 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -447,6 +447,10 @@ void SoundHE::setupHEMusicFile() {
uint32 id, len;
Common::String musicFilename(_vm->generateFilename(-4));
+ // For engine restarts
+ if (_heSpoolingMusicFile.isOpen())
+ _heSpoolingMusicFile.close();
+
if (_heSpoolingMusicFile.open(musicFilename)) {
id = _heSpoolingMusicFile.readUint32BE();
@@ -603,7 +607,7 @@ void SoundHE::checkSoundTimeouts() {
}
}
-void SoundHE::digitalSoundCallback(int message, int channel) {
+void SoundHE::digitalSoundCallback(int message, int channel, bool earlyCallback) {
// The action done for each sound is always the same;
// it's useful to keep track of the message for debugging
// purposes though...
@@ -612,7 +616,11 @@ void SoundHE::digitalSoundCallback(int message, int channel) {
debug(5, "SoundHE::digitalSoundCallback(): TIMEOUT, channel %d", channel);
break;
case HSND_SOUND_ENDED:
- debug(5, "SoundHE::digitalSoundCallback(): ENDED, channel %d", channel);
+ if (earlyCallback)
+ debug(5, "SoundHE::digitalSoundCallback(): ENDED with EARLY CALLBACK, channel %d", channel);
+ else
+ debug(5, "SoundHE::digitalSoundCallback(): ENDED, channel %d", channel);
+
break;
case HSND_SOUND_STOPPED:
debug(5, "SoundHE::digitalSoundCallback(): STOPPED, channel %d", channel);
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 07c288b32c2..73720208fd8 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -243,7 +243,7 @@ public:
void feedMixer();
void unqueueSoundCallbackScripts();
void checkSoundTimeouts();
- void digitalSoundCallback(int message, int channel);
+ void digitalSoundCallback(int message, int channel, bool earlyCallback = false);
void queueSoundCallbackScript(int sound, int channel, int message);
void runSoundCode();
void processSoundOpcodes(int sound, byte *codePtr, int *soundVars);
Commit: 7287a5ff8e47ed04c8709361522aabdc81bd1884
https://github.com/scummvm/scummvm/commit/7287a5ff8e47ed04c8709361522aabdc81bd1884
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Reimplement audio modding
For now, only for games which already supported it in the past.
I'll expand it for the Miles Sound System as soon as a real
necessity comes along.
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index a121fed6fe1..8845e0e69de 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -164,17 +164,103 @@ bool HEMixer::startChannel(int channel, int globType, int globNum,
}
}
-bool HEMixer::startSpoolingChannel(int channel, Common::File &sampleFileIOHandle,
+bool HEMixer::startSpoolingChannel(int channel, int song, Common::File &sampleFileIOHandle,
int sampleLen, int frequency, int volume, int callbackID, int32 flags) {
if (!isMilesActive()) {
- return mixerStartSpoolingChannel(channel, sampleFileIOHandle, sampleLen,
+ return mixerStartSpoolingChannel(channel, song, sampleFileIOHandle, sampleLen,
frequency, volume, callbackID, flags);
}
return false;
}
+bool HEMixer::audioOverrideExists(int soundId, bool justGetInfo, int *duration, Audio::SeekableAudioStream **outStream) {
+ if (!_vm->_enableAudioOverride) {
+ return false;
+ }
+
+ const char *formats[] = {
+#ifdef USE_FLAC
+ "flac",
+#endif
+ "wav",
+#ifdef USE_VORBIS
+ "ogg",
+#endif
+#ifdef USE_MAD
+ "mp3",
+#endif
+ };
+
+ Audio::SeekableAudioStream *(*formatDecoders[])(Common::SeekableReadStream *, DisposeAfterUse::Flag) = {
+#ifdef USE_FLAC
+ Audio::makeFLACStream,
+#endif
+ Audio::makeWAVStream,
+#ifdef USE_VORBIS
+ Audio::makeVorbisStream,
+#endif
+#ifdef USE_MAD
+ Audio::makeMP3Stream,
+#endif
+ };
+
+ STATIC_ASSERT(
+ ARRAYSIZE(formats) == ARRAYSIZE(formatDecoders),
+ formats_formatDecoders_must_have_same_size);
+
+ const char *type;
+ if (soundId == HSND_TALKIE_SLOT) {
+ // Speech audio doesn't have a unique ID,
+ // so we use the file offset instead.
+ // _heTalkOffset is set at playVoice.
+ type = "speech";
+ soundId = ((SoundHE *)(_vm->_sound))->getCurrentSpeechOffset();
+ } else {
+ // Music and sfx share the same prefix.
+ type = "sound";
+ }
+
+ for (int i = 0; i < ARRAYSIZE(formats); i++) {
+ Common::Path pathDir(Common::String::format("%s%d.%s", type, soundId, formats[i]));
+ Common::Path pathSub(Common::String::format("%s/%d.%s", type, soundId, formats[i]));
+
+ debug(5, "HEMixer::audioOverrideExists(): %s or %s", pathSub.toString().c_str(), pathDir.toString().c_str());
+
+ // First check if the file exists before opening it to
+ // reduce the amount of "opening %s failed" in the console.
+ // Prefer files in subdirectory.
+ Common::File soundFileOverride;
+ bool foundFile = (soundFileOverride.exists(pathSub) && soundFileOverride.open(pathSub)) ||
+ (soundFileOverride.exists(pathDir) && soundFileOverride.open(pathDir));
+ if (foundFile) {
+ soundFileOverride.seek(0, SEEK_SET);
+ Common::SeekableReadStream *oStr = soundFileOverride.readStream(soundFileOverride.size());
+ soundFileOverride.close();
+
+ Audio::SeekableAudioStream *seekStream = formatDecoders[i](oStr, DisposeAfterUse::YES);
+ if (duration != nullptr) {
+ *duration = seekStream->getLength().msecs();
+ }
+
+ if (justGetInfo) {
+ delete seekStream;
+ return true;
+ }
+
+ debug(5, "HEMixer::audioOverrideExists(): %s loaded from %s", formats[i], soundFileOverride.getName());
+
+ *outStream = seekStream;
+ return true;
+ }
+ }
+
+ debug(5, "HEMixer::audioOverrideExists(): file not found");
+
+ return false;
+}
+
/* --- SOFTWARE MIXER --- */
bool HEMixer::mixerInitMyMixerSubSystem() {
@@ -211,14 +297,30 @@ void HEMixer::mixerFeedMixer() {
return;
}
+ // Unqueue any callback scripts, if available...
if (!_vm->_insideCreateResource && _vm->_game.heversion >= 80) {
((SoundHE *)_vm->_sound)->unqueueSoundCallbackScripts();
}
+ // Then check if any sound has ended; if so, issue a callback...
for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
if (!(_mixerChannels[i].flags & CHANNEL_LOOPING)) {
- if (!(_mixerChannels[i].flags & CHANNEL_SPOOLING)) {
+ // This is the check for audio overrides, it is sufficient to check if the stream is empty
+ if ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && _mixerChannels[i].isUsingStreamOverride) {
+ if (_mixerChannels[i].stream->endOfData()) {
+ _mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
+ _mixerChannels[i].flags |= CHANNEL_FINISHED;
+
+ _mixerChannels[i].stream->finish();
+ _mixerChannels[i].stream = nullptr;
+ ((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID, false);
+ }
+
+ continue;
+ }
+
+ if (!(_mixerChannels[i].flags & CHANNEL_SPOOLING)) {
// Early callback! Play just one more audio frame from the residual data...
if (_mixerChannels[i].flags & CHANNEL_CALLBACK_EARLY) {
@@ -271,8 +373,12 @@ void HEMixer::mixerFeedMixer() {
}
}
- // Feed the streams
+ // Finally, feed the streams...
for (int i = 0; i < MIXER_MAX_CHANNELS; i++) {
+ // Do not feed the streams for audio overrides!
+ if (_mixerChannels[i].isUsingStreamOverride)
+ continue;
+
if (_mixerChannels[i].flags & CHANNEL_ACTIVE) {
if (_mixerChannels[i].flags & CHANNEL_SPOOLING) {
bool looping = (_mixerChannels[i].flags & CHANNEL_LOOPING);
@@ -396,6 +502,30 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].globType = globType;
_mixerChannels[channel].globNum = globNum;
_mixerChannels[channel].residualData = nullptr;
+ _mixerChannels[channel].isUsingStreamOverride = false;
+
+ Audio::SeekableAudioStream *audioOverride = nullptr;
+ if (audioOverrideExists(globNum, false, nullptr, &audioOverride)) {
+ _mixerChannels[channel].isUsingStreamOverride = true;
+ bool shouldLoop = (_mixerChannels[channel].flags & CHANNEL_LOOPING);
+
+ _mixerChannels[channel].stream = Audio::makeQueuingAudioStream(
+ audioOverride->getRate(),
+ audioOverride->isStereo());
+
+ _mixer->playStream(
+ Audio::Mixer::kMusicSoundType,
+ &_mixerChannels[channel].handle,
+ _mixerChannels[channel].stream,
+ -1,
+ volume);
+
+ _mixerChannels[channel].stream->queueAudioStream(
+ Audio::makeLoopingAudioStream(audioOverride, shouldLoop ? 0 : 1),
+ DisposeAfterUse::YES);
+
+ return true;
+ }
bool hasCallbackData = false;
if (flags & CHANNEL_CALLBACK_EARLY) {
@@ -501,7 +631,7 @@ bool HEMixer::mixerStartChannel(
}
bool HEMixer::mixerStartSpoolingChannel(
- int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
+ int channel, int song, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
int volume, int callbackID, uint32 flags) {
uint32 initialReadCount;
@@ -543,6 +673,30 @@ bool HEMixer::mixerStartSpoolingChannel(
_mixerChannels[channel].globType = rtSpoolBuffer;
_mixerChannels[channel].globNum = channel + 1;
_mixerChannels[channel].endSampleAdjustment = 0;
+ _mixerChannels[channel].isUsingStreamOverride = false;
+
+ Audio::SeekableAudioStream *audioOverride = nullptr;
+ if (audioOverrideExists(song, false, nullptr, &audioOverride)) {
+ _mixerChannels[channel].isUsingStreamOverride = true;
+ bool shouldLoop = (_mixerChannels[channel].flags & CHANNEL_LOOPING);
+
+ _mixerChannels[channel].stream = Audio::makeQueuingAudioStream(
+ audioOverride->getRate(),
+ audioOverride->isStereo());
+
+ _mixer->playStream(
+ Audio::Mixer::kMusicSoundType,
+ &_mixerChannels[channel].handle,
+ _mixerChannels[channel].stream,
+ -1,
+ volume);
+
+ _mixerChannels[channel].stream->queueAudioStream(
+ Audio::makeLoopingAudioStream(audioOverride, shouldLoop ? 0 : 1),
+ DisposeAfterUse::YES);
+
+ return true;
+ }
if (_vm->_res->createResource(
(ResType)_mixerChannels[channel].globType,
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index 3eba92adf80..d846395b5db 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -34,8 +34,10 @@
#include "audio/decoders/adpcm.h"
#include "audio/audiostream.h"
#include "audio/decoders/raw.h"
-#include "audio/rate.h"
-
+#include "audio/decoders/wave.h"
+#include "audio/decoders/mp3.h"
+#include "audio/decoders/vorbis.h"
+#include "audio/decoders/flac.h"
namespace Scumm {
@@ -165,9 +167,12 @@ public:
int channel, int globType, int globNum, uint32 sampleDataOffset,
int sampleLen, int frequency, int volume, int callbackId, int32 flags, ...);
bool startSpoolingChannel(
- int channel, Common::File &spoolingFile, int sampleLen, int frequency,
+ int channel, int song, Common::File &spoolingFile, int sampleLen, int frequency,
int volume, int callbackID, int32 flags);
+ bool audioOverrideExists(int globNum, bool justGetInfo,
+ int *duration = nullptr, Audio::SeekableAudioStream **outStream = nullptr);
+
/* --- MILES MIXER CODE --- */
bool isMilesActive();
void milesStartSpoolingChannel(int channel, const char *filename, long offset, int flags, HESoundModifiers modifiers);
@@ -196,7 +201,7 @@ public:
int channel, int globType, int globNum, uint32 sampleDataOffset,
int sampleLen, int frequency, int volume, int callbackID, uint32 flags, ...);
bool mixerStartSpoolingChannel(
- int channel, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
+ int channel, int song, Common::File &sampleFileIOHandle, int sampleLen, int frequency,
int volume, int callbackID, uint32 flags);
byte mixerGetOutputFlags();
};
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 7ffc71ab448..4c0f9afd166 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -912,7 +912,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
// Old spooled music format (raw audio)
if (_vm->_game.heversion < 80) {
_heMixer->startSpoolingChannel(
- channel, _heSpoolingMusicFile, songsize,
+ channel, song, _heSpoolingMusicFile, songsize,
HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel,
CHANNEL_ACTIVE);
@@ -1043,7 +1043,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
songsize -= offset;
}
- _heMixer->startSpoolingChannel(channel, _heSpoolingMusicFile, songsize,
+ _heMixer->startSpoolingChannel(channel, song, _heSpoolingMusicFile, songsize,
HSND_DEFAULT_FREQUENCY, HSND_MAX_VOLUME, channel, CHANNEL_ACTIVE);
}
@@ -1354,8 +1354,15 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
}
}
+ int overrideDuration;
+ if (_heMixer->audioOverrideExists(globNum, true, &overrideDuration)) {
+ _heChannel[channel].timeout = overrideDuration;
+ }
+
for (int i = 0; i < HSND_MAX_SOUND_VARS; i++) {
_heChannel[channel].soundVars[i] = 0;
+ _heChannel[channel].codeOffset = -1;
+ _heChannel[channel].hasSoundTokens = false;
}
}
@@ -1443,87 +1450,6 @@ void SoundHE::triggerMidiSound(int soundId, int heOffset) {
}
}
-Audio::RewindableAudioStream *SoundHE::tryLoadAudioOverride(int soundId, int *duration) {
- if (!_vm->_enableAudioOverride) {
- return nullptr;
- }
-
- const char *formats[] = {
-#ifdef USE_FLAC
- "flac",
-#endif
- "wav",
-#ifdef USE_VORBIS
- "ogg",
-#endif
-#ifdef USE_MAD
- "mp3",
-#endif
- };
-
- Audio::SeekableAudioStream *(*formatDecoders[])(Common::SeekableReadStream *, DisposeAfterUse::Flag) = {
-#ifdef USE_FLAC
- Audio::makeFLACStream,
-#endif
- Audio::makeWAVStream,
-#ifdef USE_VORBIS
- Audio::makeVorbisStream,
-#endif
-#ifdef USE_MAD
- Audio::makeMP3Stream,
-#endif
- };
-
- STATIC_ASSERT(
- ARRAYSIZE(formats) == ARRAYSIZE(formatDecoders),
- formats_formatDecoders_must_have_same_size
- );
-
- const char *type;
- if (soundId == 1) {
- // Speech audio doesn't have a unique ID,
- // so we use the file offset instead.
- // _heTalkOffset is set at playVoice.
- type = "speech";
- soundId = _heTalkOffset;
- } else {
- // Music and sfx share the same prefix.
- type = "sound";
- }
-
- for (int i = 0; i < ARRAYSIZE(formats); i++) {
- Common::Path pathDir(Common::String::format("%s%d.%s", type, soundId, formats[i]));
- Common::Path pathSub(Common::String::format("%s/%d.%s", type, soundId, formats[i]));
-
- debug(5, "tryLoadAudioOverride: %s or %s", pathSub.toString().c_str(), pathDir.toString().c_str());
-
- // First check if the file exists before opening it to
- // reduce the amount of "opening %s failed" in the console.
- // Prefer files in subdirectory.
- Common::File soundFileOverride;
- bool foundFile = (soundFileOverride.exists(pathSub) && soundFileOverride.open(pathSub)) ||
- (soundFileOverride.exists(pathDir) && soundFileOverride.open(pathDir));
- if (foundFile) {
- soundFileOverride.seek(0, SEEK_SET);
- Common::SeekableReadStream *oStr = soundFileOverride.readStream(soundFileOverride.size());
- soundFileOverride.close();
-
- Audio::SeekableAudioStream *seekStream = formatDecoders[i](oStr, DisposeAfterUse::YES);
- if (duration != nullptr) {
- *duration = seekStream->getLength().msecs();
- }
-
- debug(5, "tryLoadAudioOverride: %s loaded from %s", formats[i], soundFileOverride.getName());
-
- return seekStream;
- }
- }
-
- debug(5, "tryLoadAudioOverride: file not found");
-
- return nullptr;
-}
-
void SoundHE::playVoice(uint32 offset, uint32 length) {
byte *ptr;
int talkieChannel = (_vm->VAR_TALK_CHANNEL != 0xFF) ? _vm->VAR(_vm->VAR_TALK_CHANNEL) : 0;
@@ -1758,6 +1684,10 @@ byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
return nullptr;
}
+int SoundHE::getCurrentSpeechOffset() {
+ return _heTalkOffset;
+}
+
#endif
} // End of namespace Scumm
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 73720208fd8..a9cc04edd7c 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -256,6 +256,7 @@ public:
int getChannelPosition(int channel);
byte *findWavBlock(uint32 tag, const byte *block);
+ int getCurrentSpeechOffset();
protected:
void processSoundQueues() override;
@@ -269,9 +270,6 @@ private:
int _inUnqueueCallbackScripts = 0;
int _soundsDebugFrameCounter = 0;
int _scummOverrideFrequency = 0;
-
-
- Audio::RewindableAudioStream *tryLoadAudioOverride(int soundID, int *duration = nullptr);
};
Commit: 972c3ca797e9f14771d3cbc8bd9fefc1abb32797
https://github.com/scummvm/scummvm/commit/972c3ca797e9f14771d3cbc8bd9fefc1abb32797
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix lipsync regression
Whoops!
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 4c0f9afd166..28ceb07468c 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -1357,12 +1357,12 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
int overrideDuration;
if (_heMixer->audioOverrideExists(globNum, true, &overrideDuration)) {
_heChannel[channel].timeout = overrideDuration;
+ _heChannel[channel].codeOffset = -1;
+ _heChannel[channel].hasSoundTokens = false;
}
for (int i = 0; i < HSND_MAX_SOUND_VARS; i++) {
_heChannel[channel].soundVars[i] = 0;
- _heChannel[channel].codeOffset = -1;
- _heChannel[channel].hasSoundTokens = false;
}
}
Commit: 6482de2194fdfab6d68f0a810cabd3a5f9d3e38c
https://github.com/scummvm/scummvm/commit/6482de2194fdfab6d68f0a810cabd3a5f9d3e38c
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Wire up pause routines
Changed paths:
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
engines/scumm/sound.h
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 28ceb07468c..9495662d122 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -309,6 +309,14 @@ void SoundHE::setupSound() {
}
}
+void SoundHE::pauseSounds(bool pause) {
+ // For MIDI audio
+ if (_vm->_imuse)
+ _vm->_imuse->pause(pause);
+
+ _heMixer->pauseMixerSubSystem(pause);
+}
+
void SoundHE::stopDigitalSound(int sound) {
if (sound == HSND_TALKIE_SLOT) {
_vm->_haveMsg = 3;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index a9cc04edd7c..4216a516042 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -219,6 +219,7 @@ public:
void stopAllSounds() override;
int hsFindSoundChannel(int sound) const;
void setupSound() override;
+ void pauseSounds(bool pause) override;
bool getHEMusicDetails(int id, int &musicOffs, int &musicSize);
int getNextDynamicChannel();
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index 0889af18ec3..4e72a302576 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -137,7 +137,7 @@ public:
void soundKludge(int *list, int num);
void talkSound(uint32 offset, uint32 length, int mode, int channel = 0);
virtual void setupSound();
- void pauseSounds(bool pause);
+ virtual void pauseSounds(bool pause);
bool isSfxFileCompressed();
bool hasSfxFile() const;
ScummFile *restoreDiMUSESpeechFile(const char *fileName);
Commit: cd32ede3773d03e5fe7685b86aefc3dbcfdcdad2
https://github.com/scummvm/scummvm/commit/cd32ede3773d03e5fe7685b86aefc3dbcfdcdad2
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix warnings
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/resource_he.cpp
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 8845e0e69de..f187bbd5561 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -1113,7 +1113,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
_stream.fileHandle->seek(offset, SEEK_CUR);
if (_stream.fileHandle->pos() != offset) {
- debug(5, "HEMilesChannel::startSpoolingChannel(): Couldn't seek file %s to offset %d", filename, offset);
+ debug(5, "HEMilesChannel::startSpoolingChannel(): Couldn't seek file %s to offset %ld", filename, offset);
_stream.fileHandle->close();
return;
}
@@ -1215,7 +1215,7 @@ void HEMilesChannel::startSpoolingChannel(const char *filename, long offset, int
_stream.curDataPos = 0;
_stream.dataOffset = _stream.fileHandle->pos();
} else {
- debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %d",
+ debug(5, "HEMixer::milesStartChannel(): Unexpected sound format %d in sound file '%s' at offset %ld",
_dataFormat, filename, offset);
}
diff --git a/engines/scumm/he/resource_he.cpp b/engines/scumm/he/resource_he.cpp
index fba3235ca7e..67367f1f8ae 100644
--- a/engines/scumm/he/resource_he.cpp
+++ b/engines/scumm/he/resource_he.cpp
@@ -385,7 +385,7 @@ int ScummEngine_v72he::getSoundResourceSize(ResId id) {
} else {
if (READ_BE_UINT32(ptr) == MKTAG('W', 'S', 'O', 'U')) {
// Wrapped .wav file
- byte *data = ((SoundHE *)_sound)->findWavBlock(MKTAG('d', 'a', 't', 'a'), ptr);
+ const byte *data = ((SoundHE *)_sound)->findWavBlock(MKTAG('d', 'a', 't', 'a'), ptr);
if (data)
return READ_LE_UINT32(data + 4);
} else {
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 9495662d122..c8ac6d35046 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -882,7 +882,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
Common::String filename(_vm->generateFilename(-4));
int fileOffset = 0;
int songsize = 0;
- int id, len;
+ uint32 id, len;
hsStopDigitalSound(_heChannel[channel].sound);
@@ -1386,7 +1386,7 @@ void SoundHE::hsStopDigitalSound(int sound) {
void SoundHE::triggerDigitalSound(int sound, int offset, int channel, int flags) {
byte *soundAddr;
int soundCode, soundData, bitsPerSample, sampleChannels;
- byte *soundResPtr;
+ const byte *soundResPtr;
uint32 soundLength, soundFrequency;
int soundPriority;
@@ -1417,14 +1417,14 @@ void SoundHE::triggerDigitalSound(int sound, int offset, int channel, int flags)
}
}
- soundResPtr = (byte *)_vm->findResource(MKTAG('S', 'B', 'N', 'G'), soundAddr);
+ soundResPtr = _vm->findResource(MKTAG('S', 'B', 'N', 'G'), soundAddr);
if (soundResPtr == nullptr) {
soundCode = -1;
} else {
soundCode = soundResPtr - soundAddr + 8;
}
- soundResPtr = (byte *)_vm->findResource(MKTAG('S', 'D', 'A', 'T'), soundAddr);
+ soundResPtr = _vm->findResource(MKTAG('S', 'D', 'A', 'T'), soundAddr);
if (soundResPtr == nullptr)
error("SoundHE::triggerDigitalSound(): Can't find SDAT section in sound %d", sound);
@@ -1588,9 +1588,6 @@ void SoundHE::createSound(int baseSound, int sound) {
}
}
- byte *pRiff = nullptr;
- byte *pData = nullptr;
-
// Find where the actual sound data is located...
if (sndIsWav) {
baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
@@ -1601,9 +1598,6 @@ void SoundHE::createSound(int baseSound, int sound) {
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", sound);
- pData = baseSndPtr;
- pRiff = baseSndPtr + 8;
-
if (_baseSndSize == 0) {
_baseSndSize = READ_LE_UINT32(baseSndPtr + sizeof(uint32)) - 8;
}
@@ -1645,8 +1639,8 @@ void SoundHE::createSound(int baseSound, int sound) {
_vm->_res->unlock(rtSound, sound);
}
-byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
- byte *wsouPtr = (byte *)block;
+const byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
+ const byte *wsouPtr = block;
// For compatibility reason with old sound formats this
// doesn't error out, and instead gracefully returns a nullptr.
@@ -1654,7 +1648,7 @@ byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
return nullptr;
// Skip over the WSOU header...
- byte *soundPtr = wsouPtr + 8;
+ const byte *soundPtr = wsouPtr + 8;
if (READ_BE_UINT32(soundPtr) != MKTAG('R', 'I', 'F', 'F'))
error("SoundHE::findWavBlock(): Expected RIFF block");
@@ -1662,23 +1656,23 @@ byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
assert((riffLength & 1) == 0); // It must be even, since all sub-blocks must be padded to even.
// Skip over RIFF and length and go to the actual sound data...
- byte *wavePtr = soundPtr + 8;
+ const byte *wavePtr = soundPtr + 8;
assert(READ_BE_UINT32(wavePtr) == MKTAG('W', 'A', 'V', 'E'));
wavePtr += 4; // Skip over the WAVE tag
riffLength -= 4;
// Walk the nested blocks of the .wav file...
while (riffLength > 0) {
- int chunkID = READ_BE_UINT32(wavePtr);
- int chunkLength = READ_LE_UINT32(wavePtr + 4);
+ uint32 chunkID = READ_BE_UINT32(wavePtr);
+ uint32 chunkLength = READ_LE_UINT32(wavePtr + 4);
if (chunkLength < 0)
error("SoundHE::findWavBlock(): Illegal chunk length - %d bytes", chunkLength);
- if (chunkLength > riffLength)
+ if ((int)chunkLength > (int)riffLength)
error("SoundHE::findWavBlock(): Chunk extends beyond file end - %d versus %d", chunkLength, riffLength);
riffLength -= 8;
- if ((uint32)chunkID == tag)
+ if (chunkID == tag)
return wavePtr;
wavePtr += 8;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 4216a516042..9e70e21a3f3 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -256,7 +256,7 @@ public:
void createSound(int snd1id, int snd2id);
int getChannelPosition(int channel);
- byte *findWavBlock(uint32 tag, const byte *block);
+ const byte *findWavBlock(uint32 tag, const byte *block);
int getCurrentSpeechOffset();
protected:
Commit: ce7f42238bc670174925bba7ae25fad34ef30177
https://github.com/scummvm/scummvm/commit/ce7f42238bc670174925bba7ae25fad34ef30177
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix warnings pt.2
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index c8ac6d35046..3a620b47af5 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -1544,8 +1544,13 @@ void SoundHE::createSound(int baseSound, int sound) {
_vm->_res->lock(rtSound, baseSound);
_vm->_res->lock(rtSound, sound);
- baseSndPtr = (byte *)_vm->getResourceAddress(rtSound, baseSound);
- sndPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
+ const byte *baseSndPtrConst = _vm->getResourceAddress(rtSound, baseSound);
+ const byte *sndPtrConst = _vm->getResourceAddress(rtSound, sound);
+
+ // The whole point of this create sound mechanism is to MODIFY
+ // these buffers, hence the deliberate non-const cast!
+ baseSndPtr = const_cast<byte *>(baseSndPtrConst);
+ sndPtr = const_cast<byte *>(sndPtrConst);
channel = hsFindSoundChannel(baseSound);
@@ -1553,10 +1558,10 @@ void SoundHE::createSound(int baseSound, int sound) {
if (!sndIsWav) {
// For non-WAV files we have to deal with sound variables (i.e. skip them :-) )
- baseSndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
+ baseSndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
if (baseSndSbngPtr != nullptr) {
- sndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
+ sndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
if (sndSbngPtr != nullptr) {
if (channel != -1 && (_heChannel[channel].codeOffset > 0)) {
@@ -1590,11 +1595,11 @@ void SoundHE::createSound(int baseSound, int sound) {
// Find where the actual sound data is located...
if (sndIsWav) {
- baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
+ baseSndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtrConst));
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", baseSound);
- sndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtr);
+ sndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtrConst));
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", sound);
@@ -1604,11 +1609,11 @@ void SoundHE::createSound(int baseSound, int sound) {
sndSize = READ_LE_UINT32(sndPtr + sizeof(uint32)) - 8;
} else {
- baseSndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
+ baseSndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", baseSound);
- sndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
+ sndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", sound);
@@ -1665,7 +1670,7 @@ const byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
while (riffLength > 0) {
uint32 chunkID = READ_BE_UINT32(wavePtr);
uint32 chunkLength = READ_LE_UINT32(wavePtr + 4);
- if (chunkLength < 0)
+ if ((int)chunkLength < 0)
error("SoundHE::findWavBlock(): Illegal chunk length - %d bytes", chunkLength);
if ((int)chunkLength > (int)riffLength)
error("SoundHE::findWavBlock(): Chunk extends beyond file end - %d versus %d", chunkLength, riffLength);
Commit: 081ab649402ab3412fa6c916f9ee83e4cf91c7bf
https://github.com/scummvm/scummvm/commit/081ab649402ab3412fa6c916f9ee83e4cf91c7bf
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Revert some warning fixes as they broke createSound()
I guess those warnings will have to be fixed in a different way...
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 3a620b47af5..08cae633022 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -1544,13 +1544,8 @@ void SoundHE::createSound(int baseSound, int sound) {
_vm->_res->lock(rtSound, baseSound);
_vm->_res->lock(rtSound, sound);
- const byte *baseSndPtrConst = _vm->getResourceAddress(rtSound, baseSound);
- const byte *sndPtrConst = _vm->getResourceAddress(rtSound, sound);
-
- // The whole point of this create sound mechanism is to MODIFY
- // these buffers, hence the deliberate non-const cast!
- baseSndPtr = const_cast<byte *>(baseSndPtrConst);
- sndPtr = const_cast<byte *>(sndPtrConst);
+ baseSndPtr = (byte *)_vm->getResourceAddress(rtSound, baseSound);
+ sndPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
channel = hsFindSoundChannel(baseSound);
@@ -1558,10 +1553,10 @@ void SoundHE::createSound(int baseSound, int sound) {
if (!sndIsWav) {
// For non-WAV files we have to deal with sound variables (i.e. skip them :-) )
- baseSndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
+ baseSndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
if (baseSndSbngPtr != nullptr) {
- sndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
+ sndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
if (sndSbngPtr != nullptr) {
if (channel != -1 && (_heChannel[channel].codeOffset > 0)) {
@@ -1595,11 +1590,11 @@ void SoundHE::createSound(int baseSound, int sound) {
// Find where the actual sound data is located...
if (sndIsWav) {
- baseSndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtrConst));
+ baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", baseSound);
- sndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtrConst));
+ sndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtr);
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", sound);
@@ -1609,11 +1604,11 @@ void SoundHE::createSound(int baseSound, int sound) {
sndSize = READ_LE_UINT32(sndPtr + sizeof(uint32)) - 8;
} else {
- baseSndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
+ baseSndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", baseSound);
- sndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
+ sndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", sound);
Commit: e394ce8b7b068cfc70985e5fe37b727ff4d572ef
https://github.com/scummvm/scummvm/commit/e394ce8b7b068cfc70985e5fe37b727ff4d572ef
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Add temp workaround for sounds created within createSound()
This allows people to continue testing the sound engine, and
will get removed as soon as I have the time to implement a
proper solution to this issue.
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index f187bbd5561..58d8c7f2424 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -598,12 +598,19 @@ bool HEMixer::mixerStartChannel(
_mixer->setChannelRate(_mixerChannels[channel].handle, frequency);
if (_mixerChannels[channel].flags & CHANNEL_LOOPING) {
+ // Workaround from hell which will get removed
+ // before this goes into the main tree: scrap the
+ // data copy into a malloc buffer, and just use
+ // the original resource address; this fixes sounds
+ // appended with the create-sound opcode.
+ free(data);
+
Audio::RewindableAudioStream *stream = Audio::makeRawStream(
- data,
+ ptr,
_mixerChannels[channel].sampleLen,
MIXER_DEFAULT_SAMPLE_RATE,
mixerGetOutputFlags(),
- DisposeAfterUse::YES);
+ DisposeAfterUse::NO);
_mixerChannels[channel].stream->queueAudioStream(Audio::makeLoopingAudioStream(stream, 0), DisposeAfterUse::YES);
} else {
Commit: b014bd8d9c69f330dbf2c43db3bb2be97e735cfb
https://github.com/scummvm/scummvm/commit/b014bd8d9c69f330dbf2c43db3bb2be97e735cfb
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix miscalculation of channel timer
Fixes music loop in Pajama Sam 1, room 43
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 08cae633022..214632229c1 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -717,11 +717,8 @@ void SoundHE::runSoundCode() {
continue;
}
- soundPos = _vm->getHETimer(chan + HSND_TIMER_SLOT) * _heChannel[chan].frequency / 1000;
- soundPos += _vm->VAR(_vm->VAR_SOUND_TOKEN_OFFSET);
-
- if (soundPos < 0)
- soundPos = 0;
+ soundPos = getChannelPosition(chan) + _vm->VAR(_vm->VAR_SOUND_TOKEN_OFFSET);
+ soundPos = MAX<int>(0, soundPos);
if (_heChannel[chan].codeBuffer == nullptr) {
soundPtr = _vm->getResourceAddress(rtSound, _heChannel[chan].sound);
Commit: e4f408604a67948b983b837909d87eebb8d7a6ac
https://github.com/scummvm/scummvm/commit/e4f408604a67948b983b837909d87eebb8d7a6ac
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Wait for the spooling stream to end to callback
Fixes overlaps between the previous song ending and the new
song beginning
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 58d8c7f2424..53f69a812bd 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -355,18 +355,22 @@ void HEMixer::mixerFeedMixer() {
}
if (_mixerChannels[i].flags & CHANNEL_SPOOLING) {
- if (_mixerChannels[i].callbackOnNextFrame) {
+ // Callback, assuming the stream has finished playing the final chunk of the song...
+ if (_mixerChannels[i].callbackOnNextFrame && _mixerChannels[i].stream->endOfData()) {
_mixerChannels[i].callbackOnNextFrame = false;
+
+ _mixerChannels[i].stream->finish();
+ _mixerChannels[i].stream = nullptr;
+
((SoundHE *)_vm->_sound)->digitalSoundCallback(HSND_SOUND_ENDED, _mixerChannels[i].callbackID);
}
+ // Last chunk fetched! Signal a callback for the next frame...
if ((_mixerChannels[i].flags & CHANNEL_ACTIVE) && (_mixerChannels[i].flags & CHANNEL_LAST_CHUNK)) {
_mixerChannels[i].flags &= ~CHANNEL_LAST_CHUNK;
_mixerChannels[i].flags &= ~CHANNEL_ACTIVE;
_mixerChannels[i].flags |= CHANNEL_FINISHED;
- _mixerChannels[i].stream->finish();
- _mixerChannels[i].stream = nullptr;
_mixerChannels[i].callbackOnNextFrame = true;
}
}
Commit: 9b4cba48c999ac1e4aada1dac7281c98bf400b41
https://github.com/scummvm/scummvm/commit/9b4cba48c999ac1e4aada1dac7281c98bf400b41
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Slightly simplify lipsync code
Changed paths:
engines/scumm/actor.cpp
diff --git a/engines/scumm/actor.cpp b/engines/scumm/actor.cpp
index d57a9d8237f..e4b35b53875 100644
--- a/engines/scumm/actor.cpp
+++ b/engines/scumm/actor.cpp
@@ -2491,9 +2491,7 @@ void ActorHE::prepareDrawActorCostume(BaseCostumeRenderer *bcr) {
if (_vm->getTalkingActor() == _number && !_vm->_string[0].no_talk_anim) {
int talkState = -1;
- if (!((SoundHE *)_vm->_sound)->isSoundCodeUsed(HSND_TALKIE_SLOT))
- talkState = _vm->_rnd.getRandomNumberRng(1, 10);
- else
+ if (((SoundHE *)_vm->_sound)->isSoundCodeUsed(HSND_TALKIE_SLOT))
talkState = ((SoundHE *)_vm->_sound)->getSoundVar(HSND_TALKIE_SLOT, 19);
// Allow a talkie with tokens to kick into random mouth mode
Commit: 3b4f57d33f2d9ec8918e9789ad46c6471fe29589
https://github.com/scummvm/scummvm/commit/3b4f57d33f2d9ec8918e9789ad46c6471fe29589
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Change position of he_mixer.o in module.mk
Changed paths:
engines/scumm/module.mk
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index b155953d244..1085312726e 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -20,11 +20,11 @@ MODULE_OBJS := \
gfx_mac.o \
gfx_towns.o \
gfx.o \
+ he/mixer_he.o \
he/resource_he.o \
he/script_v60he.o \
he/script_v70he.o \
he/sound_he.o \
- he/mixer_he.o \
help.o \
imuse/imuse.o \
imuse/imuse_part.o \
Commit: 3ec70bf972280d6393d8dd49bbb233c643d65818
https://github.com/scummvm/scummvm/commit/3ec70bf972280d6393d8dd49bbb233c643d65818
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Address code review comments
Changed paths:
engines/scumm/he/mixer_he.cpp
engines/scumm/he/mixer_he.h
engines/scumm/he/script_v100he.cpp
engines/scumm/he/script_v70he.cpp
engines/scumm/he/script_v80he.cpp
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 53f69a812bd..7e3d9714ab0 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -19,7 +19,7 @@
*
*/
-#include "mixer_he.h"
+#include "scumm/he/mixer_he.h"
namespace Scumm {
@@ -414,7 +414,7 @@ void HEMixer::mixerFeedMixer() {
}
}
- byte *data = (byte *)malloc(length * sizeof(byte));
+ byte *data = (byte *)malloc(length);
if (data) {
_mixerChannels[i].fileHandle->read(data, length);
_mixerChannels[i].lastReadPosition += length;
@@ -543,7 +543,7 @@ bool HEMixer::mixerStartChannel(
if (_mixerChannels[channel].endSampleAdjustment != 0) {
if ((int)_mixerChannels[channel].sampleLen >= _mixerChannels[channel].endSampleAdjustment) {
_mixerChannels[channel].sampleLen -= _mixerChannels[channel].endSampleAdjustment;
- _mixerChannels[channel].residualData = (byte *)malloc(_mixerChannels[channel].endSampleAdjustment * sizeof(byte));
+ _mixerChannels[channel].residualData = (byte *)malloc(_mixerChannels[channel].endSampleAdjustment);
hasCallbackData = _mixerChannels[channel].residualData != nullptr;
} else {
// Sometimes a game can give an end sample adjustment which is bigger than the sample itself
@@ -564,7 +564,7 @@ bool HEMixer::mixerStartChannel(
ptr += 8;
}
- byte *data = (byte *)malloc(_mixerChannels[channel].sampleLen * sizeof(byte));
+ byte *data = (byte *)malloc(_mixerChannels[channel].sampleLen);
if (!data)
return false;
@@ -729,7 +729,7 @@ bool HEMixer::mixerStartSpoolingChannel(
-1,
volume);
- byte *data = (byte *)malloc(initialReadCount * sizeof(byte));
+ byte *data = (byte *)malloc(initialReadCount);
if (data) {
sampleFileIOHandle.read(data, initialReadCount);
@@ -840,7 +840,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
frequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
- byte *adpcmData = (byte *)malloc(audioDataLen * 4 * sizeof(byte));
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4);
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
delete adpcmStream;
@@ -880,7 +880,7 @@ bool HEMixer::milesStartChannel(int channel, int globType, int globNum, uint32 s
Audio::AudioStream *adpcmStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, audioDataLen, Audio::kADPCMMSIma,
_milesChannels[channel]._baseFrequency, _milesChannels[channel]._numChannels, _milesChannels[channel]._blockAlign);
- byte *adpcmData = (byte *)malloc(audioDataLen * 4 * sizeof(byte));
+ byte *adpcmData = (byte *)malloc(audioDataLen * 4);
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, audioDataLen * 2);
delete adpcmStream;
@@ -1298,7 +1298,7 @@ void HEMilesChannel::serviceStream() {
sizeToRead = MIN<uint32>(MILES_PCM_CHUNK_SIZE * _blockAlign, _stream.dataLength - _stream.curDataPos);
reachedTheEnd = sizeToRead < MILES_PCM_CHUNK_SIZE * _blockAlign;
- byte *buffer = (byte *)malloc(sizeToRead * sizeof(byte));
+ byte *buffer = (byte *)malloc(sizeToRead);
if (sizeToRead > 0 && buffer != nullptr) {
int readBytes = _stream.fileHandle->read(buffer, sizeToRead);
_stream.curDataPos += readBytes;
@@ -1316,7 +1316,7 @@ void HEMilesChannel::serviceStream() {
// We allocate a buffer which is going to be filled with
// (MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM) compressed blocks or less
- byte *compressedBuffer = (byte *)malloc(sizeToRead * sizeof(byte));
+ byte *compressedBuffer = (byte *)malloc(sizeToRead);
if (sizeToRead > 0 && compressedBuffer != nullptr) {
int readBytes = _stream.fileHandle->read(compressedBuffer, sizeToRead);
_stream.curDataPos += readBytes;
@@ -1332,7 +1332,7 @@ void HEMilesChannel::serviceStream() {
uint32 uncompSize =
calculateDeflatedADPCMBlockSize(MILES_IMA_ADPCM_PER_FRAME_CHUNKS_NUM, _blockAlign, _numChannels, 16);
- byte *adpcmData = (byte *)malloc(uncompSize * sizeof(byte));
+ byte *adpcmData = (byte *)malloc(uncompSize);
uint32 adpcmSize = adpcmStream->readBuffer((int16 *)(void *)adpcmData, uncompSize * 2);
adpcmSize *= 2;
diff --git a/engines/scumm/he/mixer_he.h b/engines/scumm/he/mixer_he.h
index d846395b5db..36552365529 100644
--- a/engines/scumm/he/mixer_he.h
+++ b/engines/scumm/he/mixer_he.h
@@ -19,7 +19,7 @@
*
*/
-#if !defined(SCUMM_HE_MIXER_HE_H)
+#ifndef SCUMM_HE_MIXER_HE_H
#define SCUMM_HE_MIXER_HE_H
#include "scumm/he/sound_he.h"
@@ -41,14 +41,14 @@
namespace Scumm {
-#define CHANNEL_EMPTY_FLAGS 0x00000000
-#define CHANNEL_ACTIVE 0x00000001
-#define CHANNEL_FINISHED 0x00000002
-#define CHANNEL_LOOPING 0x00000004
-#define CHANNEL_LAST_CHUNK 0x00000008
-#define CHANNEL_SPOOLING 0x00000010
-#define CHANNEL_CALLBACK_EARLY 0x00000080
-#define CHANNEL_SOFT_REMIX 0x00000100
+#define CHANNEL_EMPTY_FLAGS (0 << 0)
+#define CHANNEL_ACTIVE (1 << 0)
+#define CHANNEL_FINISHED (1 << 1)
+#define CHANNEL_LOOPING (1 << 2)
+#define CHANNEL_LAST_CHUNK (1 << 3)
+#define CHANNEL_SPOOLING (1 << 4)
+#define CHANNEL_CALLBACK_EARLY (1 << 7)
+#define CHANNEL_SOFT_REMIX (1 << 8)
#define MIXER_MAX_CHANNELS 8
#define MIXER_PCM_CHUNK_SIZE 4096
diff --git a/engines/scumm/he/script_v100he.cpp b/engines/scumm/he/script_v100he.cpp
index 82a089dae64..6246e60254e 100644
--- a/engines/scumm/he/script_v100he.cpp
+++ b/engines/scumm/he/script_v100he.cpp
@@ -747,16 +747,16 @@ void ScummEngine_v100he::o100_createSound() {
byte subOp = fetchScriptByte();
switch (subOp) {
- case SO_INIT:
+ case SO_INIT: // 0
_heSndResId = pop();
break;
- case SO_NEW:
+ case SO_NEW: // 53
((SoundHE *)_sound)->createSound(_heSndResId, -1);
break;
- case SO_SOUND_ADD:
+ case SO_SOUND_ADD: // 128
((SoundHE *)_sound)->createSound(_heSndResId, pop());
break;
- case SO_END:
+ case SO_END: // 92
break;
default:
error("o100_createSound: default case %d", subOp);
@@ -1741,47 +1741,47 @@ void ScummEngine_v100he::o100_soundOps() {
byte subOp = fetchScriptByte();
switch (subOp) {
- case SO_AT:
+ case SO_AT: // 6
_heSndFlags |= HE_SND_OFFSET;
_heSndOffset = pop();
break;
- case SO_LOAD:
+ case SO_LOAD: // 47
copyScriptString(filename, sizeof(filename));
_heSndSoundId = pop();
if (_heSndSoundId)
debug(0, "Load sound %d from file %s\n", _heSndSoundId, filename);
break;
- case SO_NOW:
+ case SO_NOW: // 55
_heSndFlags |= HE_SND_QUICK_START;
break;
- case SO_VARIABLE:
+ case SO_VARIABLE: // 83
value = pop();
var = pop();
_heSndSoundId = pop();
((SoundHE *)_sound)->setSoundVar(_heSndSoundId, var, value);
break;
- case SO_END:
+ case SO_END: // 92
if (_heSndStartNewSoundFlag) {
_sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags, _heSndFrequencyShift, _heSndPan, _heSndVol);
} else {
_sound->modifySound(_heSndSoundId, _heSndOffset, _heSndFrequencyShift, _heSndPan, _heSndVol, _heSndFlags);
}
break;
- case SO_SOUND_ADD:
+ case SO_SOUND_ADD: // 128
_heSndFlags |= HE_SND_APPEND;
break;
- case SO_SOUND_CHANNEL:
+ case SO_SOUND_CHANNEL: // 129
_heSndChannel = pop();
break;
- case SO_SOUND_FREQUENCY:
+ case SO_SOUND_FREQUENCY: // 130
_heSndFlags |= HE_SND_FREQUENCY;
_heSndFrequencyShift = pop();
break;
- case SO_SOUND_LOOPING:
+ case SO_SOUND_LOOPING: // 131
_heSndFlags |= HE_SND_LOOP;
break;
- case SO_SOUND_MODIFY:
- case SO_SOUND_START:
+ case SO_SOUND_MODIFY: // 132
+ case SO_SOUND_START: // 134
_heSndStartNewSoundFlag = (SO_SOUND_START == subOp);
_heSndSoundId = pop();
_heSndOffset = 0;
@@ -1791,14 +1791,14 @@ void ScummEngine_v100he::o100_soundOps() {
_heSndPan = HSND_SOUND_PAN_CENTER;
_heSndFlags = 0;
break;
- case SO_SOUND_PAN:
+ case SO_SOUND_PAN: // 133
_heSndFlags |= HE_SND_PAN;
_heSndPan = pop();
break;
- case SO_SOUND_SOFT:
+ case SO_SOUND_SOFT: // 135
_heSndFlags |= HE_SND_SOFT_SOUND;
break;
- case SO_SOUND_VOLUME:
+ case SO_SOUND_VOLUME: // 136
_heSndFlags |= HE_SND_VOL;
_heSndVol = pop();
break;
diff --git a/engines/scumm/he/script_v70he.cpp b/engines/scumm/he/script_v70he.cpp
index 125e3dd0809..9ebdf4152b6 100644
--- a/engines/scumm/he/script_v70he.cpp
+++ b/engines/scumm/he/script_v70he.cpp
@@ -57,48 +57,48 @@ void ScummEngine_v70he::o70_soundOps() {
byte subOp = fetchScriptByte();
switch (subOp) {
- case SO_SOFT:
+ case SO_SOFT: // 9
_heSndFlags |= HE_SND_SOFT_SOUND;
break;
- case SO_VARIABLE:
+ case SO_VARIABLE: // 23
value = pop();
var = pop();
_heSndSoundId = pop();
((SoundHE *)_sound)->setSoundVar(_heSndSoundId, var, value);
break;
- case SO_SOUND_VOLUME:
+ case SO_SOUND_VOLUME: // 25
value = pop();
_heSndSoundId = pop();
_sound->startSound(_heSndSoundId, 0, 0, HE_SND_VOL, HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, value);
break;
- case SO_NOW:
+ case SO_NOW: // 56
_heSndFlags |= HE_SND_QUICK_START;
break;
- case SO_SOUND_ADD:
+ case SO_SOUND_ADD: // 164
_heSndFlags |= HE_SND_APPEND;
break;
- case SO_SOUND_START_VOLUME:
+ case SO_SOUND_START_VOLUME: // 222
// WORKAROUND: For errors in room script 240 (room 4) of maze
break;
- case SO_SOUND_FREQUENCY:
+ case SO_SOUND_FREQUENCY: // 224
_heSndFrequency = pop();
break;
- case SO_SOUND_CHANNEL:
+ case SO_SOUND_CHANNEL: // 230
_heSndChannel = pop();
break;
- case SO_AT:
+ case SO_AT: // 231
_heSndOffset = pop();
break;
- case SO_SOUND_START:
+ case SO_SOUND_START: // 232
_heSndSoundId = pop();
_heSndOffset = 0;
_heSndFrequency = 11025; // This is set but apparently is not used?
_heSndChannel = VAR(VAR_SOUND_CHANNEL);
break;
- case SO_SOUND_LOOPING:
+ case SO_SOUND_LOOPING: // 245
_heSndFlags |= HE_SND_LOOP;
break;
- case SO_END:
+ case SO_END: // 255
_sound->startSound(_heSndSoundId, _heSndOffset, _heSndChannel, _heSndFlags,
HSND_BASE_FREQ_FACTOR, HSND_SOUND_PAN_CENTER, HSND_MAX_VOLUME);
_heSndFlags = 0;
diff --git a/engines/scumm/he/script_v80he.cpp b/engines/scumm/he/script_v80he.cpp
index 8bda0b7baac..b1b376e8a36 100644
--- a/engines/scumm/he/script_v80he.cpp
+++ b/engines/scumm/he/script_v80he.cpp
@@ -67,17 +67,17 @@ void ScummEngine_v80he::o80_createSound() {
byte subOp = fetchScriptByte();
switch (subOp) {
- case SO_ADD:
+ case SO_ADD: // 27
((SoundHE *)_sound)->createSound(_heSndResId, pop());
break;
- case SO_NEW:
+ case SO_NEW: // 217
((SoundHE *)_sound)->createSound(_heSndResId, -1);
break;
- case SO_SOUND_START:
+ case SO_SOUND_START: // 232
_heSndResId = pop();
break;
- case SO_END:
- // dummy case
+ case SO_END: // 255
+ // Dummy case
break;
default:
error("o80_createSound: default case %d", subOp);
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 214632229c1..1618fc79284 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -775,13 +775,13 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
debug(5, "SoundHE::processSoundOpcodes(): sound %d opcode %d", sound, opcode);
switch (opcode) {
- case HSND_SBNG_END: // Continue
+ case HSND_SBNG_END: // Continue - 0
break;
- case HSND_SBNG_FACE: // Set talk state
+ case HSND_SBNG_FACE: // Set talk state - 16
val = READ_LE_UINT16(codePtr); codePtr += 2;
setSoundVar(sound, 19, val);
break;
- case HSND_SBNG_SET_SET: // Set var
+ case HSND_SBNG_SET_SET: // Set var - 32
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -789,7 +789,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
}
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_ADD: // Add
+ case HSND_SBNG_SET_ADD: // Add - 48
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -798,7 +798,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) + val;
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_SUB: // Subtract
+ case HSND_SBNG_SET_SUB: // Subtract - 56
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -807,7 +807,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) - val;
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_MUL: // Multiple
+ case HSND_SBNG_SET_MUL: // Multiple - 64
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -816,7 +816,7 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) * val;
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_DIV: // Divide
+ case HSND_SBNG_SET_DIV: // Divide - 80
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = READ_LE_UINT16(codePtr); codePtr += 2;
if (arg == 2) {
@@ -829,12 +829,12 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
val = getSoundVar(sound, var) / val;
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_INC: // Increment
+ case HSND_SBNG_SET_INC: // Increment - 96
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) + 1;
setSoundVar(sound, var, val);
break;
- case HSND_SBNG_SET_DEC: // Decrement
+ case HSND_SBNG_SET_DEC: // Decrement - 104
var = READ_LE_UINT16(codePtr); codePtr += 2;
val = getSoundVar(sound, var) - 1;
setSoundVar(sound, var, val);
Commit: 989eda7b08baaca00910fd4f5ee4a3da1a059f93
https://github.com/scummvm/scummvm/commit/989eda7b08baaca00910fd4f5ee4a3da1a059f93
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Show message box for dev code path
Also, changed error to warning, if we already have a message
box, having an error is a little bit overkill...
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 1618fc79284..dbeadd7131a 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -33,6 +33,7 @@
#include "common/memstream.h"
#include "common/timer.h"
#include "common/util.h"
+#include "common/translation.h"
#include "audio/audiostream.h"
#include "audio/decoders/adpcm.h"
@@ -1510,8 +1511,13 @@ void SoundHE::playVoiceFile(char *filename) {
// Originally this served the purpose of fetching voice lines from separate files
// instead of using a single .HE2 bundle.
//
- // Again, we should never end up in here, but IF WE DO we issue an error.
- error("SoundHE::playVoiceFile(): Unimplemented development codepath, please file a ticket!");
+ // Again, we should never end up in here, but IF WE DO we issue a message box.
+ GUIErrorMessageWithURL(_(
+ "Unimplemented development codepath encountered within the sound engine,\n"
+ "please file a ticket at https://bugs.scummvm.org."),
+ "https://bugs.scummvm.org");
+
+ warning("SoundHE::playVoiceFile(): Unimplemented development codepath");
}
#ifdef ENABLE_HE
Commit: 65655e99084aa806876f48afc02069e8cf818b10
https://github.com/scummvm/scummvm/commit/65655e99084aa806876f48afc02069e8cf818b10
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix update of age member of _heChannel
Changed paths:
engines/scumm/he/sound_he.cpp
engines/scumm/he/sound_he.h
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index dbeadd7131a..4d6beb0bed1 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -847,6 +847,8 @@ void SoundHE::processSoundOpcodes(int sound, byte *codePtr, int *soundVars) {
}
void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags, HESoundModifiers modifiers) {
+ _dynamicSoundAgeCounter++;
+
if (_vm->_game.heversion >= 95) {
if (heChannel == HSND_DYN_SOUND_CHAN) {
heChannel = getNextDynamicChannel();
@@ -1055,6 +1057,7 @@ void SoundHE::triggerSpoolingSound(int song, int offset, int channel, int flags,
_vm->setHETimer(channel + HSND_TIMER_SLOT);
+ _heChannel[channel].age = _dynamicSoundAgeCounter;
_heChannel[channel].sound = song;
_heChannel[channel].priority = 255;
_heChannel[channel].frequency = HSND_DEFAULT_FREQUENCY;
@@ -1340,6 +1343,7 @@ void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundDa
_vm->setHETimer(channel + HSND_TIMER_SLOT);
+ _heChannel[channel].age = _dynamicSoundAgeCounter;
_heChannel[channel].sound = sound;
_heChannel[channel].priority = priority;
_heChannel[channel].codeOffset = soundCode;
diff --git a/engines/scumm/he/sound_he.h b/engines/scumm/he/sound_he.h
index 9e70e21a3f3..5fb2d284bf9 100644
--- a/engines/scumm/he/sound_he.h
+++ b/engines/scumm/he/sound_he.h
@@ -169,7 +169,6 @@ protected:
};
int32 _heSpoolingMusicCount;
-
Common::File _heSpoolingMusicFile;
byte _heSpoolingCodeBuffer[HSND_MAX_SPOOLING_CODE_SIZE];
@@ -270,7 +269,7 @@ private:
int _soundAlreadyInQueueCount = 0;
int _inUnqueueCallbackScripts = 0;
int _soundsDebugFrameCounter = 0;
- int _scummOverrideFrequency = 0;
+ int _dynamicSoundAgeCounter = 0;
};
Commit: 3bd897e26fb6a51ebe3a26f774a75693682546cd
https://github.com/scummvm/scummvm/commit/3bd897e26fb6a51ebe3a26f774a75693682546cd
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix const warning
...hopefully.
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index 4d6beb0bed1..ae2c7f0b89d 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -1551,8 +1551,8 @@ void SoundHE::createSound(int baseSound, int sound) {
_vm->_res->lock(rtSound, baseSound);
_vm->_res->lock(rtSound, sound);
- baseSndPtr = (byte *)_vm->getResourceAddress(rtSound, baseSound);
- sndPtr = (byte *)_vm->getResourceAddress(rtSound, sound);
+ baseSndPtr = _vm->getResourceAddress(rtSound, baseSound);
+ sndPtr = _vm->getResourceAddress(rtSound, sound);
channel = hsFindSoundChannel(baseSound);
@@ -1560,10 +1560,10 @@ void SoundHE::createSound(int baseSound, int sound) {
if (!sndIsWav) {
// For non-WAV files we have to deal with sound variables (i.e. skip them :-) )
- baseSndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
+ baseSndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), baseSndPtr);
if (baseSndSbngPtr != nullptr) {
- sndSbngPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
+ sndSbngPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'B', 'N', 'G'), sndPtr);
if (sndSbngPtr != nullptr) {
if (channel != -1 && (_heChannel[channel].codeOffset > 0)) {
@@ -1597,11 +1597,11 @@ void SoundHE::createSound(int baseSound, int sound) {
// Find where the actual sound data is located...
if (sndIsWav) {
- baseSndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr);
+ baseSndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), baseSndPtr));
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", baseSound);
- sndPtr = (byte *)findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtr);
+ sndPtr = const_cast<byte *>(findWavBlock(MKTAG('d', 'a', 't', 'a'), sndPtr));
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find data tag", sound);
@@ -1611,11 +1611,11 @@ void SoundHE::createSound(int baseSound, int sound) {
sndSize = READ_LE_UINT32(sndPtr + sizeof(uint32)) - 8;
} else {
- baseSndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
+ baseSndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), baseSndPtr);
if (baseSndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", baseSound);
- sndPtr = (byte *)((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
+ sndPtr = ((ScummEngine_v71he *)_vm)->heFindResource(MKTAG('S', 'D', 'A', 'T'), sndPtr);
if (sndPtr == nullptr)
error("SoundHE::createSound(): Bad format for sound %d, couldn't find SDAT tag", sound);
Commit: 4aa6c3d34acf9a0f8c26c8dfc24e0a1238834544
https://github.com/scummvm/scummvm/commit/4aa6c3d34acf9a0f8c26c8dfc24e0a1238834544
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Remove workaround for looping sounds
Changed paths:
engines/scumm/he/mixer_he.cpp
diff --git a/engines/scumm/he/mixer_he.cpp b/engines/scumm/he/mixer_he.cpp
index 7e3d9714ab0..0fc9258f524 100644
--- a/engines/scumm/he/mixer_he.cpp
+++ b/engines/scumm/he/mixer_he.cpp
@@ -564,28 +564,38 @@ bool HEMixer::mixerStartChannel(
ptr += 8;
}
- byte *data = (byte *)malloc(_mixerChannels[channel].sampleLen);
-
- if (!data)
- return false;
-
+ byte *data = nullptr;
ptr += sampleDataOffset;
- memcpy(data, ptr, _mixerChannels[channel].sampleLen);
-
- // Residual early callback data
- if (hasCallbackData) {
- memcpy(
- _mixerChannels[channel].residualData,
- &ptr[_mixerChannels[channel].sampleLen],
- _mixerChannels[channel].endSampleAdjustment);
- }
+ // Some looping sounds need the original resource address to
+ // work properly (e.g. the ones created with createSound()),
+ // so we only copy the data over and create a 64 samples fade-in
+ // just for non-looping sounds. Also, remember that non-looping
+ // sounds might have early callbacks, so we still have to copy
+ // data over, instead of using the original buffer.
+ if (!(_mixerChannels[channel].flags & CHANNEL_LOOPING)) {
+ data = (byte *)malloc(_mixerChannels[channel].sampleLen);
+
+ if (!data)
+ return false;
+
+ memcpy(data, ptr, _mixerChannels[channel].sampleLen);
+
+ // Residual early callback data
+ if (hasCallbackData) {
+ memcpy(
+ _mixerChannels[channel].residualData,
+ &ptr[_mixerChannels[channel].sampleLen],
+ _mixerChannels[channel].endSampleAdjustment);
+ }
- byte *dataTmp = data;
- int rampUpSampleCount = 64;
- for (int i = 0; i < rampUpSampleCount; i++) {
- *dataTmp = 128 + (((*dataTmp - 128) * i) / rampUpSampleCount);
- dataTmp++;
+ // Fade-in to avoid possible sound popping...
+ byte *dataTmp = data;
+ int rampUpSampleCount = 64;
+ for (int i = 0; i < rampUpSampleCount; i++) {
+ *dataTmp = 128 + (((*dataTmp - 128) * i) / rampUpSampleCount);
+ dataTmp++;
+ }
}
_mixerChannels[channel].stream = Audio::makeQueuingAudioStream(MIXER_DEFAULT_SAMPLE_RATE, false);
@@ -602,13 +612,6 @@ bool HEMixer::mixerStartChannel(
_mixer->setChannelRate(_mixerChannels[channel].handle, frequency);
if (_mixerChannels[channel].flags & CHANNEL_LOOPING) {
- // Workaround from hell which will get removed
- // before this goes into the main tree: scrap the
- // data copy into a malloc buffer, and just use
- // the original resource address; this fixes sounds
- // appended with the create-sound opcode.
- free(data);
-
Audio::RewindableAudioStream *stream = Audio::makeRawStream(
ptr,
_mixerChannels[channel].sampleLen,
@@ -618,6 +621,7 @@ bool HEMixer::mixerStartChannel(
_mixerChannels[channel].stream->queueAudioStream(Audio::makeLoopingAudioStream(stream, 0), DisposeAfterUse::YES);
} else {
+ // If we're here, data is surely a correctly allocated buffer...
_mixerChannels[channel].stream->queueBuffer(
data,
_mixerChannels[channel].sampleLen,
Commit: 97de0d932d6c8b362dd1bc9de7045c6986be5347
https://github.com/scummvm/scummvm/commit/97de0d932d6c8b362dd1bc9de7045c6986be5347
Author: AndywinXp (andywinxp at gmail.com)
Date: 2023-08-18T22:20:45+02:00
Commit Message:
SCUMM: HE (Sound): Fix compiling when HE engine is not enabled
Changed paths:
engines/scumm/he/sound_he.cpp
diff --git a/engines/scumm/he/sound_he.cpp b/engines/scumm/he/sound_he.cpp
index ae2c7f0b89d..fd2d976925c 100644
--- a/engines/scumm/he/sound_he.cpp
+++ b/engines/scumm/he/sound_he.cpp
@@ -869,7 +869,9 @@ void SoundHE::triggerSound(int soundId, int heOffset, int heChannel, int heFlags
} else if (READ_BE_UINT32(soundAddr) == MKTAG('W', 'S', 'O', 'U')) {
triggerRIFFSound(soundId, heOffset, heChannel, heFlags, modifiers);
} else if (READ_BE_UINT32(soundAddr) == MKTAG('X', 'S', 'O', 'U')) {
+#ifdef ENABLE_HE
triggerXSOUSound(soundId, heOffset, heChannel, heFlags);
+#endif
} else {
error("SoundHE::triggerSound(): Illegal sound %d type %s", soundId, tag2str(READ_BE_UINT32(soundAddr)));
}
@@ -1220,6 +1222,8 @@ void SoundHE::triggerRIFFSound(int soundId, int heOffset, int heChannel, int heF
heFlags, bitsPerSample, sampleChannelCount, modifiers);
}
+#ifdef ENABLE_HE
+
void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heFlags) {
int sampleFrequency, bitsPerSample, sampleChannelCount, soundPriority;
int soundCodeOffset, soundDataOffset, sampleCount;
@@ -1279,6 +1283,8 @@ void SoundHE::triggerXSOUSound(int heSound, int heOffset, int heChannel, int heF
heFlags, bitsPerSample, sampleChannelCount, HESoundModifiers());
}
+#endif
+
void SoundHE::hsStartDigitalSound(int sound, int offset, byte *addr, int soundData,
int globType, int globNum, int sampleCount, int frequency, int channel, int priority,
int soundCode, int flags, int bitsPerSample, int soundChannelCount, HESoundModifiers modifiers) {
@@ -1693,10 +1699,10 @@ const byte *SoundHE::findWavBlock(uint32 tag, const byte *block) {
return nullptr;
}
+#endif
+
int SoundHE::getCurrentSpeechOffset() {
return _heTalkOffset;
}
-#endif
-
} // End of namespace Scumm
More information about the Scummvm-git-logs
mailing list