[Scummvm-git-logs] scummvm master -> 1c50d0abd6f997e514ca8f969f678c9f83c04d4c
bluegr
noreply at scummvm.org
Mon Dec 23 00:36:41 UTC 2024
This automated email contains information about 1 new commit which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
1c50d0abd6 SCUMM: Split the CD audio functionality into a separate sound class
Commit: 1c50d0abd6f997e514ca8f969f678c9f83c04d4c
https://github.com/scummvm/scummvm/commit/1c50d0abd6f997e514ca8f969f678c9f83c04d4c
Author: Filippos Karapetis (bluegr at gmail.com)
Date: 2024-12-23T02:36:06+02:00
Commit Message:
SCUMM: Split the CD audio functionality into a separate sound class
This helps to cleanly separate the CD audio logic from the main digital
audio functionality
Changed paths:
A engines/scumm/soundcd.cpp
A engines/scumm/soundcd.h
engines/scumm/module.mk
engines/scumm/players/player_towns.cpp
engines/scumm/saveload.cpp
engines/scumm/scumm.cpp
engines/scumm/sound.cpp
engines/scumm/sound.h
diff --git a/engines/scumm/module.mk b/engines/scumm/module.mk
index 46bc0a2019e..5c1edf0996e 100644
--- a/engines/scumm/module.mk
+++ b/engines/scumm/module.mk
@@ -84,6 +84,7 @@ MODULE_OBJS := \
script.o \
scumm.o \
sound.o \
+ soundcd.o \
soundse.o \
string.o \
usage_bits.o \
diff --git a/engines/scumm/players/player_towns.cpp b/engines/scumm/players/player_towns.cpp
index b5c232add22..20f475a786f 100644
--- a/engines/scumm/players/player_towns.cpp
+++ b/engines/scumm/players/player_towns.cpp
@@ -266,7 +266,6 @@ void Player_Towns_v1::stopSound(int sound) {
if (sound == 0 || sound == _cdaCurrentSound) {
_cdaCurrentSound = 0;
_vm->_sound->stopCD();
- _vm->_sound->stopCDTimer();
}
if (sound != 0 && sound == _eupCurrentSound) {
@@ -281,7 +280,6 @@ void Player_Towns_v1::stopSound(int sound) {
void Player_Towns_v1::stopAllSounds() {
_cdaCurrentSound = 0;
_vm->_sound->stopCD();
- _vm->_sound->stopCDTimer();
_eupCurrentSound = 0;
_eupLooping = false;
diff --git a/engines/scumm/saveload.cpp b/engines/scumm/saveload.cpp
index c436d171ba5..3cabc0edc32 100644
--- a/engines/scumm/saveload.cpp
+++ b/engines/scumm/saveload.cpp
@@ -2044,20 +2044,7 @@ void ScummEngine::saveLoadWithSerializer(Common::Serializer &s) {
syncWithSerializer(s, info);
if (s.isLoading() && info.playing) {
- if (info.numLoops < 0 && _game.platform != Common::kPlatformFMTowns) {
- // If we are loading, and the music being loaded was supposed to loop
- // forever, then resume playing it. This helps a lot when the audio CD
- // is used to provide ambient music (see bug #1150).
- // FM-Towns versions handle this in Player_Towns_v1::restoreAfterLoad().
- _sound->playCDTrackInternal(info.track, info.numLoops, info.start, info.duration);
- } else if (_game.id == GID_LOOM && info.start != 0 && info.duration != 0) {
- // Reload audio for LOOM CD/Steam. We move the offset forward by a little bit
- // to restore the correct sync.
- int startOffset = (int)(VAR(VAR_MUSIC_TIMER) * 1.25);
-
- _sound->_cdMusicTimer = VAR(VAR_MUSIC_TIMER);
- _sound->playCDTrackInternal(info.track, info.numLoops, info.start + startOffset, info.duration - VAR(VAR_MUSIC_TIMER));
- }
+ _sound->restoreCDAudioAfterLoad(info);
}
}
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index a3efc1adda9..dd51b67327a 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -2887,21 +2887,7 @@ void ScummEngine::scummLoop(int delta) {
scummLoop_updateScummVars();
- if (_game.features & GF_AUDIOTRACKS) {
- VAR(VAR_MUSIC_TIMER) = _sound->getCDMusicTimer();
- } else if (VAR_MUSIC_TIMER != 0xFF) {
- if (_sound->useReplacementAudio() && _sound->getCurrentCDSound()) {
- // The replacement music timer operates on real time, adjusted to
- // the expected length of the Loom Overture (since there are so
- // many different recordings of it). It's completely independent of
- // the SCUMM engine's timer frequency.
- _sound->updateMusicTimer();
- VAR(VAR_MUSIC_TIMER) = _sound->getMusicTimer();
- } else if (_musicEngine) {
- // The music engine generates the timer data for us.
- VAR(VAR_MUSIC_TIMER) = _musicEngine->getMusicTimer() * getTimerFrequency() / 240.0;
- }
- }
+ _sound->updateMusicTimer();
// Another v8 quirk: runAllScripts() is called here; after that we can
// finally restore the blastTexts/blastObject rects...
diff --git a/engines/scumm/sound.cpp b/engines/scumm/sound.cpp
index a7557d64a12..2fd230e80ad 100644
--- a/engines/scumm/sound.cpp
+++ b/engines/scumm/sound.cpp
@@ -20,13 +20,11 @@
*/
#include "common/config-manager.h"
-#include "common/timer.h"
#include "common/util.h"
#include "common/ptr.h"
#include "common/substream.h"
#include "scumm/actor.h"
-#include "scumm/cdda.h"
#include "scumm/file.h"
#include "scumm/imuse_digi/dimuse_engine.h"
#include "scumm/players/player_towns.h"
@@ -35,11 +33,9 @@
#include "scumm/sound.h"
#include "audio/audiostream.h"
-#include "audio/timestamp.h"
#include "audio/decoders/flac.h"
#include "audio/mididrv.h"
#include "audio/mixer.h"
-#include "audio/decoders/adpcm.h"
#include "audio/decoders/mp3.h"
#include "audio/decoders/raw.h"
#include "audio/decoders/voc.h"
@@ -59,15 +55,9 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
:
_vm(parent),
_mixer(mixer),
- _useReplacementAudioTracks(useReplacementAudioTracks),
- _replacementTrackStartTime(0),
- _musicTimer(0),
- _cdMusicTimerMod(0),
- _cdMusicTimer(0),
_speechTimerMod(0),
_midiQueuePos(0),
_soundQueuePos(0),
- _sfxFilename(),
_sfxFileEncByte(0),
_offsetTable(nullptr),
_numSoundEffects(0),
@@ -81,7 +71,6 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
_mouthSyncMode(false),
_endOfMouthSync(false),
_curSoundPos(0),
- _currentCDSound(0),
_currentMusic(0),
_lastSound(0),
_soundsPaused(false),
@@ -93,26 +82,11 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
_musicType = MDT_NONE;
- _fileBasedCDStatus.playing = false;
- _fileBasedCDStatus.track = 0;
- _fileBasedCDStatus.start = 0;
- _fileBasedCDStatus.duration = 0;
- _fileBasedCDStatus.numLoops = 0;
- _fileBasedCDStatus.volume = Audio::Mixer::kMaxChannelVolume;
- _fileBasedCDStatus.balance = 0;
-
- _hasFileBasedCDAudio = _vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU");
- _loomOvertureTransition = DEFAULT_LOOM_OVERTURE_TRANSITION + ConfMan.getInt("loom_overture_ticks");
-
- _fileBasedCDAudioHandle = new Audio::SoundHandle();
_talkChannelHandle = new Audio::SoundHandle();
- if (_vm->_game.features & GF_DOUBLEFINE_PAK) {
+ if (_vm->_game.features & GF_DOUBLEFINE_PAK)
_soundSE = new SoundSE(_vm, _mixer);
-
- if (_vm->_game.id == GID_MONKEY)
- _hasFileBasedCDAudio = true;
- }
+ _soundCD = new SoundCD(_vm, _mixer, _soundSE, useReplacementAudioTracks);
_useRemasteredAudio = ConfMan.getBool("use_remastered_audio");
@@ -125,94 +99,15 @@ Sound::Sound(ScummEngine *parent, Audio::Mixer *mixer, bool useReplacementAudioT
}
Sound::~Sound() {
- stopCDTimer();
- stopCD();
free(_offsetTable);
- delete _fileBasedCDAudioHandle;
delete _talkChannelHandle;
delete _soundSE;
+ delete _soundCD;
if (_vm->_game.version >= 5 && _vm->_game.version <= 7 && _vm->_game.heversion == 0) {
stopSpeechTimer();
}
}
-bool Sound::isRolandLoom() const {
- return
- (_vm->_game.id == GID_LOOM) &&
- (_vm->_game.version == 3) &&
- (_vm->_game.platform == Common::kPlatformDOS) &&
- (_vm->VAR(_vm->VAR_SOUNDCARD) == 4);
-}
-
-#define JIFFIES_TO_TICKS(x) (40 * ((double)(x)) / _vm->getTimerFrequency())
-#define TICKS_TO_JIFFIES(x) ((double)(x) * (_vm->getTimerFrequency() / 40))
-
-#define TICKS_TO_TIMER(x) ((((x) * 204) / _loomOvertureTransition) + 1)
-#define TIMER_TO_TICKS(x) ((((x) - 1) * _loomOvertureTransition) / 204)
-
-void Sound::updateMusicTimer() {
- bool isLoomOverture = (isRolandLoom() && _currentCDSound == 56 && !(_vm->_game.features & GF_DEMO));
-
- // If the replacement track has ended, reset the timer to 0 like when
- // playing the original music. We make an exception for the Overture,
- // since it may need to keep running after the track has ended.
- //
- // This is also why we can't query the CD audio manager for the current
- // position. That, and the fact that the CD manager does not provide
- // this information at the time of writing.
-
- if (!pollCD() && !isLoomOverture) {
- _currentCDSound = 0;
- _musicTimer = 0;
- _replacementTrackStartTime = 0;
- return;
- }
-
- // Time is measured in "ticks", with ten ticks per second. This should
- // be exact enough, while providing an easily understandable unit of
- // measurement for the adjustment slider.
-
- // The rate at which the timer is advanced is hard-coded for the Loom
- // Overture. When playing the original music the rate is apparently
- // based on the MIDI tempo of it. But at least for Loom, the Overture
- // seems to be the only piece of music where timing matters.
-
- // These are the values the timer will have to reach or exceed for the
- // Overture to work correctly:
-
- // 4 - Fade in the "OVERTURE" text
- // 198 - Fade down the "OVERTURE" text
- // 204 - Show the LucasFilm logo
- // 278 - End the Overture
-
- // VAR_TOTAL_TIMER measures time in "jiffies", or frames. This will
- // eventually overflow, but I don't expect that to ever be a problem.
-
- int32 now = _vm->VAR(_vm->VAR_TIMER_TOTAL);
-
- int32 ticks = JIFFIES_TO_TICKS(now - _replacementTrackStartTime);
-
- // If the track ends before the timer reaches 198, skip ahead. (If the
- // timer didn't even reach 4 you weren't really trying, and must be
- // punished for that!)
-
- if (isLoomOverture && !pollCD()) {
- int32 fadeDownTick = TIMER_TO_TICKS(198);
- if (ticks < fadeDownTick) {
- _replacementTrackStartTime = now - TICKS_TO_JIFFIES(fadeDownTick);
- ticks = fadeDownTick;
- }
- }
-
- _musicTimer = TICKS_TO_TIMER(ticks);
-
- // But don't let the timer exceed 278 until the Overture has ended, or
- // the music will be cut off.
-
- if (isLoomOverture && pollCD() && _musicTimer >= 278)
- _musicTimer = 277;
-}
-
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;
@@ -281,58 +176,6 @@ void Sound::processSoundQueues() {
_midiQueuePos = 0;
}
-int Sound::getReplacementAudioTrack(int soundID) {
- int trackNr = -1;
-
- if (_vm->_game.id == GID_LOOM) {
- if (_vm->_game.features & GF_DEMO) {
- // If I understand correctly, the shorter demo only
- // has the Loom intro music. The longer demo has a
- // couple of tracks that it will cycle through if
- // you leave the demo running.
-
- if (isRolandLoom())
- soundID -= 10;
-
- switch (soundID) {
- case 19:
- trackNr = 2;
- break;
- case 20:
- trackNr = 4;
- break;
- case 21:
- trackNr = 7;
- break;
- case 23:
- trackNr = 8;
- break;
- case 26:
- trackNr = 3;
- break;
- }
- } else {
- if (isRolandLoom())
- soundID -= 32;
-
- // The first track, the Overture, only exists as a
- // Roland track.
- if (soundID >= 24 && soundID <= 32) {
- trackNr = soundID - 23;
- } else if (soundID == 19) {
- trackNr = 10;
- } else if (soundID == 21) {
- trackNr = 11;
- }
- }
- }
-
- if (trackNr != -1 && !_vm->existExtractedCDAudioFiles(trackNr))
- trackNr = -1;
-
- return trackNr;
-}
-
void Sound::triggerSound(int soundID) {
byte *ptr;
byte *sound;
@@ -340,50 +183,8 @@ void Sound::triggerSound(int soundID) {
int size = -1;
int rate;
- if (_useReplacementAudioTracks) {
- // Note that music does not loop. Probably because it's likely
- // to be interrupted by sound effects before it's over anyway.
- //
- // In the FM Towns version, music does play continuously (each
- // track has two versions), probably because CD audio and sound
- // effects are played independent of each other. Personally I
- // find the game harder when the music is allowed to drown out
- // the sound effects.
-
- int trackNr = getReplacementAudioTrack(soundID);
- if (trackNr != -1) {
- _currentCDSound = soundID;
- _replacementTrackStartTime = _vm->VAR(_vm->VAR_TIMER_TOTAL);
- _musicTimer = 0;
- g_system->getAudioCDManager()->play(trackNr, 1, 0, 0, true);
- return;
- }
- }
-
- if (_vm->_game.id == GID_LOOM && _vm->_game.platform == Common::kPlatformPCEngine) {
- if (soundID >= 13 && soundID <= 32) {
- static const char tracks[20] = {3, 4, 5, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 19, 20, 21};
-
- _currentCDSound = soundID;
-
- // The original game had hard-coded lengths for all
- // tracks, but this one track is the only one (as far
- // as we know) where this actually matters. See bug
- // #4914 - LOOM-PCE: Music stops prematurely.
-
- int track = tracks[soundID - 13];
- if (track == 6) {
- playCDTrack(track, 1, 0, 260);
- } else {
- playCDTrack(track, 1, 0, 0);
- }
- } else {
- if (_vm->_musicEngine) {
- _vm->_musicEngine->startSound(soundID);
- }
- }
+ if (_soundCD->triggerCDSound(soundID))
return;
- }
debugC(DEBUG_SOUND, "triggerSound #%d", soundID);
@@ -499,7 +300,7 @@ void Sound::triggerSound(int soundID) {
if (type == 2) {
// CD track resource
ptr += 0x16;
- if (soundID == _currentCDSound && pollCD() == 1)
+ if (soundID == _soundCD->_currentCDSound && _soundCD->pollCD() == 1)
return;
int track = ptr[0];
@@ -514,8 +315,8 @@ void Sound::triggerSound(int soundID) {
start += ((75 * adjustment) / 100);
}
- playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
- _currentCDSound = soundID;
+ _soundCD->playCDTrack(track, loops == 0xff ? -1 : loops, start, end <= start ? 0 : end - start);
+ _soundCD->_currentCDSound = soundID;
} else {
// All other sound types are ignored
warning("Scumm::Sound::triggerSound: encountered audio resource with chunk type 'SOUN' and sound type %d", type);
@@ -544,7 +345,7 @@ void Sound::triggerSound(int soundID) {
_vm->_musicEngine->startSound(soundID);
if (_vm->_townsPlayer)
- _currentCDSound = _vm->_townsPlayer->getCurrentCdaSound();
+ _soundCD->_currentCDSound = _vm->_townsPlayer->getCurrentCdaSound();
}
}
@@ -1022,8 +823,8 @@ int Sound::isSoundRunning(int sound) const {
}
#endif
- if (sound == _currentCDSound)
- return pollCD();
+ if (sound == _soundCD->_currentCDSound)
+ return _soundCD->pollCD();
if (_mixer->isSoundIDActive(sound))
return 1;
@@ -1064,8 +865,8 @@ bool Sound::isSoundInUse(int sound) const {
return (_vm->_imuseDigital->isSoundRunning(sound) != 0);
#endif
- if (sound == _currentCDSound)
- return pollCD() != 0;
+ if (sound == _soundCD->_currentCDSound)
+ return _soundCD->pollCD() != 0;
if (_mixer->isSoundIDActive(sound))
return true;
@@ -1109,13 +910,7 @@ bool Sound::isSoundInQueue(int sound) const {
void Sound::stopSound(int sound) {
int i;
- if (sound != 0 && sound == _currentCDSound) {
- _currentCDSound = 0;
- _musicTimer = 0;
- _replacementTrackStartTime = 0;
- stopCD();
- stopCDTimer();
- }
+ _soundCD->stopCDSound(sound);
if (_vm->_game.version < 7)
_mixer->stopID(sound);
@@ -1137,13 +932,7 @@ void Sound::stopSound(int sound) {
}
void Sound::stopAllSounds() {
- if (_currentCDSound != 0) {
- _currentCDSound = 0;
- _musicTimer = 0;
- _replacementTrackStartTime = 0;
- stopCD();
- stopCDTimer();
- }
+ _soundCD->stopAllCDSounds();
// Clear the (secondary) sound queue
_lastSound = 0;
@@ -1208,7 +997,7 @@ void Sound::pauseSounds(bool pause) {
if (_vm->_imuse)
_vm->_imuse->pause(pause);
- _soundsPaused = pause;
+ _soundsPaused = _soundCD->_soundsPaused = pause;
#ifdef ENABLE_SCUMM_7_8
if (_vm->_imuseDigital) {
@@ -1218,12 +1007,7 @@ void Sound::pauseSounds(bool pause) {
_mixer->pauseAll(pause);
- if ((_vm->_game.features & GF_AUDIOTRACKS) && _vm->VAR_MUSIC_TIMER != 0xFF && _vm->VAR(_vm->VAR_MUSIC_TIMER) > 0) {
- if (pause)
- stopCDTimer();
- else
- startCDTimer();
- }
+ _soundCD->pauseCDSounds(pause);
}
bool Sound::isSfxFileCompressed() {
@@ -1413,194 +1197,13 @@ bool Sound::speechIsPlaying() {
return _mixer->isSoundHandleActive(*_talkChannelHandle);
}
-static void cdTimerHandler(void *refCon) {
- Sound *snd = (Sound *)refCon;
-
- // FIXME: Turn off the timer when it's no longer needed. In theory, it
- // should be possible to check with pollCD(), but since CD sound isn't
- // properly restarted when reloading a saved game, I don't dare to.
- if ((snd->_cdMusicTimerMod++ & 3) == 0) {
- snd->_cdMusicTimer++;
- }
-}
-
-void Sound::startCDTimer() {
- if (_useReplacementAudioTracks)
- return;
-
- // This CD timer implementation strictly follows the original interpreters for
- // Monkey Island 1 CD and Loom CD: it works by incrementing _cdMusicTimerMod and _cdMusicTimer
- // at each quarter frame (see ScummEngine::setTimerAndShakeFrequency() for what the exact
- // frequency rate is for the particular game and engine version being ran).
- //
- // Again as per the interpreters, VAR_MUSIC_TIMER is then updated inside the SCUMM main loop.
- int32 interval = 1000000 / _vm->getTimerFrequency();
-
- // LOOM Steam uses a fixed 240Hz rate. This was probably done to get rid of some
- // audio glitches which are confirmed to be in the original. So let's activate this
- // fix for the DOS version of LOOM as well, if enhancements are enabled.
- if (_vm->_game.id == GID_LOOM && (_hasFileBasedCDAudio || _vm->enhancementEnabled(kEnhMinorBugFixes)))
- interval = 1000000 / LOOM_STEAM_CDDA_RATE;
-
- _vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
- _vm->getTimerManager()->installTimerProc(&cdTimerHandler, interval, this, "scummCDtimer");
-}
-
-void Sound::stopCDTimer() {
- if (_useReplacementAudioTracks)
- return;
-
- _vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
-}
-
-void Sound::playCDTrack(int track, int numLoops, int startFrame, int duration) {
- // Reset the music timer variable at the start of a new track
- _vm->VAR(_vm->VAR_MUSIC_TIMER) = 0;
- _cdMusicTimerMod = 0;
- _cdMusicTimer = 0;
-
- // Play it
- if (!_soundsPaused)
- playCDTrackInternal(track, numLoops, startFrame, duration);
-
- // Start the timer after starting the track. Starting an MP3 track is
- // almost instantaneous, but a CD player may take some time. Hopefully
- // playCD() will block during that delay.
- startCDTimer();
-}
-
-void Sound::playCDTrackInternal(int track, int numLoops, int startFrame, int duration) {
- _fileBasedCDStatus.track = track;
- _fileBasedCDStatus.numLoops = numLoops;
- _fileBasedCDStatus.start = startFrame;
- _fileBasedCDStatus.duration = duration;
-
- if (!_hasFileBasedCDAudio) {
- g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration);
- } else {
- // Stop any currently playing track
- _mixer->stopHandle(*_fileBasedCDAudioHandle);
-
- Audio::SeekableAudioStream *stream = nullptr;
-
- if (_vm->_game.id == GID_LOOM) {
- stream = makeCDDAStream("CDDA.SOU", DisposeAfterUse::YES);
- } else if (_soundSE) {
- stream = _soundSE->getXWBTrack(track);
- }
-
- if (!stream)
- return;
-
- Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
- Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75);
-
- _mixer->playStream(Audio::Mixer::kMusicSoundType, _fileBasedCDAudioHandle,
- Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops));
- }
-}
-
-void Sound::stopCD() {
- if (!_hasFileBasedCDAudio)
- g_system->getAudioCDManager()->stop();
- else
- _mixer->stopHandle(*_fileBasedCDAudioHandle);
-}
-
-int Sound::pollCD() const {
- if (!_hasFileBasedCDAudio)
- return g_system->getAudioCDManager()->isPlaying();
- else
- return _mixer->isSoundHandleActive(*_fileBasedCDAudioHandle);
-}
-
-void Sound::updateCD() {
- if (!_hasFileBasedCDAudio)
- g_system->getAudioCDManager()->update();
-}
-
-AudioCDManager::Status Sound::getCDStatus() {
- if (!_hasFileBasedCDAudio)
- return g_system->getAudioCDManager()->getStatus();
- else {
- AudioCDManager::Status info = _fileBasedCDStatus;
- info.playing = _mixer->isSoundHandleActive(*_fileBasedCDAudioHandle);
- return info;
- }
-}
-
void Sound::saveLoadWithSerializer(Common::Serializer &s) {
- s.syncAsSint16LE(_currentCDSound, VER(35));
+ s.syncAsSint16LE(_soundCD->_currentCDSound, VER(35));
s.syncAsSint16LE(_currentMusic, VER(35));
}
-int Sound::getCDTrackIdFromSoundId(int soundId, int &loops, int &start) {
- if (_vm->_game.id == GID_LOOM && _vm->_game.version == 4) {
- loops = 0;
- start = -1;
- return 1;
- }
-
- if (soundId != -1 && _vm->getResourceAddress(rtSound, soundId)) {
- uint8 *ptr = _vm->getResourceAddress(rtSound, soundId) + 0x18;
- loops = ptr[1];
- start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
- return ptr[0];
- }
-
- loops = 1;
- return -1;
-}
-
void Sound::restoreAfterLoad() {
- _musicTimer = 0;
- _replacementTrackStartTime = 0;
- int trackNr = -1;
- int loops = 1;
- int start = 0;
- if (_currentCDSound) {
- if (_useReplacementAudioTracks) {
- trackNr = getReplacementAudioTrack(_currentCDSound);
- } else if (_vm->_game.platform != Common::kPlatformFMTowns) {
- trackNr = getCDTrackIdFromSoundId(_currentCDSound, loops, start);
- }
-
- if (trackNr != -1) {
- if (_useReplacementAudioTracks) {
- int32 now = _vm->VAR(_vm->VAR_TIMER_TOTAL);
- uint32 frame;
-
- _musicTimer = _vm->VAR(_vm->VAR_MUSIC_TIMER);
-
- // We try to resume the audio track from where it was
- // saved. The timer isn't very accurate, but it should
- // be good enough.
- //
- // NOTE: This does not seem to work at the moment, since
- // the track immediately gets restarted in the cases I
- // tried.
-
- if (_musicTimer > 0) {
- int32 ticks = TIMER_TO_TICKS(_musicTimer);
-
- _replacementTrackStartTime = now - TICKS_TO_JIFFIES(ticks);
- frame = (75 * ticks) / 10;
- } else {
- _replacementTrackStartTime = now;
- frame = 0;
- }
-
- // If the user has fiddled with the Loom overture
- // setting, the calculated position could be outside
- // the track. But it seems a warning message is as bad
- // as it gets.
-
- g_system->getAudioCDManager()->play(trackNr, 1, frame, 0, true);
- } else if (_vm->_game.platform != Common::kPlatformFMTowns) {
- g_system->getAudioCDManager()->play(trackNr, loops, start + _vm->VAR(_vm->VAR_MUSIC_TIMER), 0, true);
- }
- }
- }
+ _soundCD->restoreAfterLoad();
}
bool Sound::isAudioDisabled() {
@@ -2551,5 +2154,22 @@ int ScummEngine::readSoundResourceSmallHeader(ResId idx) {
return 0;
}
+void Sound::updateMusicTimer() {
+ if (_vm->_game.features & GF_AUDIOTRACKS) {
+ _vm->VAR(_vm->VAR_MUSIC_TIMER) = _soundCD->getCDMusicTimer();
+ } else if (_vm->VAR_MUSIC_TIMER != 0xFF) {
+ if (_soundCD->useReplacementAudio() && _soundCD->getCurrentCDSound()) {
+ // The replacement music timer operates on real time, adjusted to
+ // the expected length of the Loom Overture (since there are so
+ // many different recordings of it). It's completely independent of
+ // the SCUMM engine's timer frequency.
+ _soundCD->updateMusicTimer();
+ _vm->VAR(_vm->VAR_MUSIC_TIMER) = _soundCD->getMusicTimer();
+ } else if (_vm->_musicEngine) {
+ // The music engine generates the timer data for us.
+ _vm->VAR(_vm->VAR_MUSIC_TIMER) = _vm->_musicEngine->getMusicTimer() * _vm->getTimerFrequency() / 240.0;
+ }
+ }
+}
} // End of namespace Scumm
diff --git a/engines/scumm/sound.h b/engines/scumm/sound.h
index dfdf6da978f..65088bf913f 100644
--- a/engines/scumm/sound.h
+++ b/engines/scumm/sound.h
@@ -26,16 +26,10 @@
#include "common/serializer.h"
#include "common/str.h"
#include "audio/mididrv.h"
-#include "backends/audiocd/audiocd.h"
#include "scumm/file.h"
+#include "scumm/soundcd.h"
#include "scumm/soundse.h"
-// The number of "ticks" (1/10th of a second) into the Overture that the
-// LucasFilm logo should appear. This corresponds to a timer value of 204.
-// The default value is selected to work well with the Ozawa recording.
-
-#define DEFAULT_LOOM_OVERTURE_TRANSITION 1160
-
#define DIGI_SND_MODE_EMPTY 0
#define DIGI_SND_MODE_SFX 1
#define DIGI_SND_MODE_TALKIE 2
@@ -103,17 +97,9 @@ protected:
uint16 _mouthSyncTimes[64];
uint _curSoundPos;
- int16 _currentCDSound;
- int16 _currentMusic;
-
- Audio::SoundHandle *_fileBasedCDAudioHandle;
- bool _hasFileBasedCDAudio;
- AudioCDManager::Status _fileBasedCDStatus;
- bool _useReplacementAudioTracks;
- int _musicTimer;
- int _loomOvertureTransition;
- uint32 _replacementTrackStartTime;
+ int16 _currentMusic; // used by HE games
+ SoundCD *_soundCD = nullptr;
SoundSE *_soundSE = nullptr;
bool _useRemasteredAudio = false;
@@ -123,8 +109,6 @@ public:
bool _soundsPaused;
byte _digiSndMode;
uint _lastSound;
- uint32 _cdMusicTimerMod;
- uint32 _cdMusicTimer;
uint32 _speechTimerMod;
MidiDriverFlags _musicType;
@@ -159,28 +143,31 @@ public:
void stopSpeechTimer();
bool speechIsPlaying(); // Used within MIDI iMUSE
- void startCDTimer();
- void stopCDTimer();
-
- void playCDTrack(int track, int numLoops, int startFrame, int duration);
- void playCDTrackInternal(int track, int numLoops, int startFrame, int duration);
- void stopCD();
- int pollCD() const;
- void updateCD();
- AudioCDManager::Status getCDStatus();
- int getCurrentCDSound() const { return _currentCDSound; }
- int getCDTrackIdFromSoundId(int soundId, int &loops, int &start);
- bool isRolandLoom() const;
- bool useReplacementAudio() const { return _useReplacementAudioTracks; }
- void updateMusicTimer();
- int getMusicTimer() const { return _musicTimer; }
- int getCDMusicTimer() const { return _cdMusicTimer; }
-
void saveLoadWithSerializer(Common::Serializer &ser) override;
void restoreAfterLoad();
bool isAudioDisabled();
+ void updateMusicTimer();
+
+ // TODO: Duplicate this in Sound as well?
+ bool isRolandLoom() const { return _soundCD->isRolandLoom(); }
+
+ // CD audio wrapper methods
+ int pollCD() const { return _soundCD->pollCD(); }
+ void updateCD() { _soundCD->updateCD(); }
+ void stopCD() {
+ _soundCD->stopCD();
+ _soundCD->stopCDTimer();
+ }
+ void playCDTrack(int track, int numLoops, int startFrame, int duration) {
+ _soundCD->playCDTrack(track, numLoops, startFrame, duration);
+ }
+ int getCurrentCDSound() const { return _soundCD->getCurrentCDSound(); }
+ void restoreCDAudioAfterLoad(AudioCDManager::Status &info) {
+ _soundCD->restoreCDAudioAfterLoad(info);
+ }
+
protected:
void setupSfxFile();
bool isSfxFinished() const;
@@ -189,8 +176,6 @@ protected:
bool isSoundInQueue(int sound) const;
virtual void processSoundQueues();
-
- int getReplacementAudioTrack(int soundID);
};
diff --git a/engines/scumm/soundcd.cpp b/engines/scumm/soundcd.cpp
new file mode 100644
index 00000000000..4beabb4c42d
--- /dev/null
+++ b/engines/scumm/soundcd.cpp
@@ -0,0 +1,468 @@
+/* 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 "audio/audiostream.h"
+#include "common/config-manager.h"
+#include "common/timer.h"
+#include "engines/engine.h"
+#include "scumm/cdda.h"
+#include "scumm/music.h"
+#include "scumm/scumm.h"
+#include "scumm/soundcd.h"
+#include "scumm/soundse.h"
+
+namespace Scumm {
+
+SoundCD::SoundCD(ScummEngine *parent, Audio::Mixer *mixer, SoundSE *soundSE, bool useReplacementAudioTracks):
+ _vm(parent), _mixer(mixer), _soundSE(soundSE), _useReplacementAudioTracks(useReplacementAudioTracks) {
+ _fileBasedCDStatus.playing = false;
+ _fileBasedCDStatus.track = 0;
+ _fileBasedCDStatus.start = 0;
+ _fileBasedCDStatus.duration = 0;
+ _fileBasedCDStatus.numLoops = 0;
+ _fileBasedCDStatus.volume = Audio::Mixer::kMaxChannelVolume;
+ _fileBasedCDStatus.balance = 0;
+
+ _hasFileBasedCDAudio =
+ (_vm->_game.id == GID_LOOM && Common::File::exists("CDDA.SOU")) ||
+ ((_vm->_game.features & GF_DOUBLEFINE_PAK) && _vm->_game.id == GID_MONKEY);
+
+ _loomOvertureTransition = DEFAULT_LOOM_OVERTURE_TRANSITION + ConfMan.getInt("loom_overture_ticks");
+
+ _fileBasedCDAudioHandle = new Audio::SoundHandle();
+}
+
+SoundCD::~SoundCD() {
+ stopCDTimer();
+ stopCD();
+ delete _fileBasedCDAudioHandle;
+}
+
+void SoundCD::stopCDSound(int sound) {
+ if (sound != 0 && sound == _currentCDSound) {
+ _currentCDSound = 0;
+ _musicTimer = 0;
+ _replacementTrackStartTime = 0;
+ stopCD();
+ stopCDTimer();
+ }
+}
+
+void SoundCD::stopAllCDSounds() {
+ if (_currentCDSound != 0) {
+ _currentCDSound = 0;
+ _musicTimer = 0;
+ _replacementTrackStartTime = 0;
+ stopCD();
+ stopCDTimer();
+ }
+}
+
+static void cdTimerHandler(void *refCon) {
+ SoundCD *snd = (SoundCD *)refCon;
+
+ // FIXME: Turn off the timer when it's no longer needed. In theory, it
+ // should be possible to check with pollCD(), but since CD sound isn't
+ // properly restarted when reloading a saved game, I don't dare to.
+ if ((snd->_cdMusicTimerMod++ & 3) == 0) {
+ snd->_cdMusicTimer++;
+ }
+}
+
+void SoundCD::startCDTimer() {
+ if (_useReplacementAudioTracks)
+ return;
+
+ // This CD timer implementation strictly follows the original interpreters for
+ // Monkey Island 1 CD and Loom CD: it works by incrementing _cdMusicTimerMod and _cdMusicTimer
+ // at each quarter frame (see ScummEngine::setTimerAndShakeFrequency() for what the exact
+ // frequency rate is for the particular game and engine version being ran).
+ //
+ // Again as per the interpreters, VAR_MUSIC_TIMER is then updated inside the SCUMM main loop.
+ int32 interval = 1000000 / _vm->getTimerFrequency();
+
+ // LOOM Steam uses a fixed 240Hz rate. This was probably done to get rid of some
+ // audio glitches which are confirmed to be in the original. So let's activate this
+ // fix for the DOS version of LOOM as well, if enhancements are enabled.
+ if (_vm->_game.id == GID_LOOM && (_hasFileBasedCDAudio || _vm->enhancementEnabled(kEnhMinorBugFixes)))
+ interval = 1000000 / LOOM_STEAM_CDDA_RATE;
+
+ _vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
+ _vm->getTimerManager()->installTimerProc(&cdTimerHandler, interval, this, "scummCDtimer");
+}
+
+void SoundCD::stopCDTimer() {
+ if (_useReplacementAudioTracks)
+ return;
+
+ _vm->getTimerManager()->removeTimerProc(&cdTimerHandler);
+}
+
+void SoundCD::playCDTrack(int track, int numLoops, int startFrame, int duration) {
+ // Reset the music timer variable at the start of a new track
+ _vm->VAR(_vm->VAR_MUSIC_TIMER) = 0;
+ _cdMusicTimerMod = 0;
+ _cdMusicTimer = 0;
+
+ // Play it
+ if (!_soundsPaused)
+ playCDTrackInternal(track, numLoops, startFrame, duration);
+
+ // Start the timer after starting the track. Starting an MP3 track is
+ // almost instantaneous, but a CD player may take some time. Hopefully
+ // playCD() will block during that delay.
+ startCDTimer();
+}
+
+void SoundCD::playCDTrackInternal(int track, int numLoops, int startFrame, int duration) {
+ _fileBasedCDStatus.track = track;
+ _fileBasedCDStatus.numLoops = numLoops;
+ _fileBasedCDStatus.start = startFrame;
+ _fileBasedCDStatus.duration = duration;
+
+ if (!_hasFileBasedCDAudio) {
+ g_system->getAudioCDManager()->play(track, numLoops, startFrame, duration);
+ } else {
+ // Stop any currently playing track
+ _mixer->stopHandle(*_fileBasedCDAudioHandle);
+
+ Audio::SeekableAudioStream *stream = nullptr;
+
+ if (_vm->_game.id == GID_LOOM) {
+ stream = makeCDDAStream("CDDA.SOU", DisposeAfterUse::YES);
+ } else if (_soundSE) {
+ stream = _soundSE->getXWBTrack(track);
+ }
+
+ if (!stream)
+ return;
+
+ Audio::Timestamp start = Audio::Timestamp(0, startFrame, 75);
+ Audio::Timestamp end = Audio::Timestamp(0, startFrame + duration, 75);
+
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, _fileBasedCDAudioHandle,
+ Audio::makeLoopingAudioStream(stream, start, end, (numLoops < 1) ? numLoops + 1 : numLoops));
+ }
+}
+
+void SoundCD::stopCD() {
+ if (!_hasFileBasedCDAudio)
+ g_system->getAudioCDManager()->stop();
+ else
+ _mixer->stopHandle(*_fileBasedCDAudioHandle);
+}
+
+int SoundCD::pollCD() const {
+ if (!_hasFileBasedCDAudio)
+ return g_system->getAudioCDManager()->isPlaying();
+ else
+ return _mixer->isSoundHandleActive(*_fileBasedCDAudioHandle);
+}
+
+void SoundCD::updateCD() {
+ if (!_hasFileBasedCDAudio)
+ g_system->getAudioCDManager()->update();
+}
+
+AudioCDManager::Status SoundCD::getCDStatus() {
+ if (!_hasFileBasedCDAudio)
+ return g_system->getAudioCDManager()->getStatus();
+ else {
+ AudioCDManager::Status info = _fileBasedCDStatus;
+ info.playing = _mixer->isSoundHandleActive(*_fileBasedCDAudioHandle);
+ return info;
+ }
+}
+
+int SoundCD::getCDTrackIdFromSoundId(int soundId, int &loops, int &start) {
+ if (_vm->_game.id == GID_LOOM && _vm->_game.version == 4) {
+ loops = 0;
+ start = -1;
+ return 1;
+ }
+
+ if (soundId != -1 && _vm->getResourceAddress(rtSound, soundId)) {
+ uint8 *ptr = _vm->getResourceAddress(rtSound, soundId) + 0x18;
+ loops = ptr[1];
+ start = (ptr[2] * 60 + ptr[3]) * 75 + ptr[4];
+ return ptr[0];
+ }
+
+ loops = 1;
+ return -1;
+}
+
+bool SoundCD::isRolandLoom() const {
+ return
+ (_vm->_game.id == GID_LOOM) &&
+ (_vm->_game.version == 3) &&
+ (_vm->_game.platform == Common::kPlatformDOS) &&
+ (_vm->VAR(_vm->VAR_SOUNDCARD) == 4);
+}
+
+#define JIFFIES_TO_TICKS(x) (40 * ((double)(x)) / _vm->getTimerFrequency())
+#define TICKS_TO_JIFFIES(x) ((double)(x) * (_vm->getTimerFrequency() / 40))
+
+#define TICKS_TO_TIMER(x) ((((x)*204) / _loomOvertureTransition) + 1)
+#define TIMER_TO_TICKS(x) ((((x)-1) * _loomOvertureTransition) / 204)
+
+void SoundCD::updateMusicTimer() {
+ bool isLoomOverture = (isRolandLoom() && _currentCDSound == 56 && !(_vm->_game.features & GF_DEMO));
+
+ // If the replacement track has ended, reset the timer to 0 like when
+ // playing the original music. We make an exception for the Overture,
+ // since it may need to keep running after the track has ended.
+ //
+ // This is also why we can't query the CD audio manager for the current
+ // position. That, and the fact that the CD manager does not provide
+ // this information at the time of writing.
+
+ if (!pollCD() && !isLoomOverture) {
+ _currentCDSound = 0;
+ _musicTimer = 0;
+ _replacementTrackStartTime = 0;
+ return;
+ }
+
+ // Time is measured in "ticks", with ten ticks per second. This should
+ // be exact enough, while providing an easily understandable unit of
+ // measurement for the adjustment slider.
+
+ // The rate at which the timer is advanced is hard-coded for the Loom
+ // Overture. When playing the original music the rate is apparently
+ // based on the MIDI tempo of it. But at least for Loom, the Overture
+ // seems to be the only piece of music where timing matters.
+
+ // These are the values the timer will have to reach or exceed for the
+ // Overture to work correctly:
+
+ // 4 - Fade in the "OVERTURE" text
+ // 198 - Fade down the "OVERTURE" text
+ // 204 - Show the LucasFilm logo
+ // 278 - End the Overture
+
+ // VAR_TOTAL_TIMER measures time in "jiffies", or frames. This will
+ // eventually overflow, but I don't expect that to ever be a problem.
+
+ int32 now = _vm->VAR(_vm->VAR_TIMER_TOTAL);
+
+ int32 ticks = JIFFIES_TO_TICKS(now - _replacementTrackStartTime);
+
+ // If the track ends before the timer reaches 198, skip ahead. (If the
+ // timer didn't even reach 4 you weren't really trying, and must be
+ // punished for that!)
+
+ if (isLoomOverture && !pollCD()) {
+ int32 fadeDownTick = TIMER_TO_TICKS(198);
+ if (ticks < fadeDownTick) {
+ _replacementTrackStartTime = now - TICKS_TO_JIFFIES(fadeDownTick);
+ ticks = fadeDownTick;
+ }
+ }
+
+ _musicTimer = TICKS_TO_TIMER(ticks);
+
+ // But don't let the timer exceed 278 until the Overture has ended, or
+ // the music will be cut off.
+
+ if (isLoomOverture && pollCD() && _musicTimer >= 278)
+ _musicTimer = 277;
+}
+
+void SoundCD::restoreAfterLoad() {
+ _musicTimer = 0;
+ _replacementTrackStartTime = 0;
+ int trackNr = -1;
+ int loops = 1;
+ int start = 0;
+ if (_currentCDSound) {
+ if (_useReplacementAudioTracks) {
+ trackNr = getReplacementAudioTrack(_currentCDSound);
+ } else if (_vm->_game.platform != Common::kPlatformFMTowns) {
+ trackNr = getCDTrackIdFromSoundId(_currentCDSound, loops, start);
+ }
+
+ if (trackNr != -1) {
+ if (_useReplacementAudioTracks) {
+ int32 now = _vm->VAR(_vm->VAR_TIMER_TOTAL);
+ uint32 frame;
+
+ _musicTimer = _vm->VAR(_vm->VAR_MUSIC_TIMER);
+
+ // We try to resume the audio track from where it was
+ // saved. The timer isn't very accurate, but it should
+ // be good enough.
+ //
+ // NOTE: This does not seem to work at the moment, since
+ // the track immediately gets restarted in the cases I
+ // tried.
+
+ if (_musicTimer > 0) {
+ int32 ticks = TIMER_TO_TICKS(_musicTimer);
+
+ _replacementTrackStartTime = now - TICKS_TO_JIFFIES(ticks);
+ frame = (75 * ticks) / 10;
+ } else {
+ _replacementTrackStartTime = now;
+ frame = 0;
+ }
+
+ // If the user has fiddled with the Loom overture
+ // setting, the calculated position could be outside
+ // the track. But it seems a warning message is as bad
+ // as it gets.
+
+ g_system->getAudioCDManager()->play(trackNr, 1, frame, 0, true);
+ } else if (_vm->_game.platform != Common::kPlatformFMTowns) {
+ g_system->getAudioCDManager()->play(trackNr, loops, start + _vm->VAR(_vm->VAR_MUSIC_TIMER), 0, true);
+ }
+ }
+ }
+}
+
+void SoundCD::restoreCDAudioAfterLoad(AudioCDManager::Status &info) {
+ if (info.numLoops < 0 && _vm->_game.platform != Common::kPlatformFMTowns) {
+ // If we are loading, and the music being loaded was supposed to loop
+ // forever, then resume playing it. This helps a lot when the audio CD
+ // is used to provide ambient music (see bug #1150).
+ // FM-Towns versions handle this in Player_Towns_v1::restoreAfterLoad().
+ playCDTrackInternal(info.track, info.numLoops, info.start, info.duration);
+ } else if (_vm->_game.id == GID_LOOM && info.start != 0 && info.duration != 0) {
+ // Reload audio for LOOM CD/Steam. We move the offset forward by a little bit
+ // to restore the correct sync.
+ int startOffset = (int)(_vm->VAR(_vm->VAR_MUSIC_TIMER) * 1.25);
+
+ _cdMusicTimer = _vm->VAR(_vm->VAR_MUSIC_TIMER);
+ playCDTrackInternal(info.track, info.numLoops, info.start + startOffset, info.duration - _vm->VAR(_vm->VAR_MUSIC_TIMER));
+ }
+}
+
+bool SoundCD::triggerCDSound(int soundID) {
+ if (_useReplacementAudioTracks) {
+ // Note that music does not loop. Probably because it's likely
+ // to be interrupted by sound effects before it's over anyway.
+ //
+ // In the FM Towns version, music does play continuously (each
+ // track has two versions), probably because CD audio and sound
+ // effects are played independent of each other. Personally I
+ // find the game harder when the music is allowed to drown out
+ // the sound effects.
+
+ int trackNr = getReplacementAudioTrack(soundID);
+ if (trackNr != -1) {
+ _currentCDSound = soundID;
+ _replacementTrackStartTime = _vm->VAR(_vm->VAR_TIMER_TOTAL);
+ _musicTimer = 0;
+ g_system->getAudioCDManager()->play(trackNr, 1, 0, 0, true);
+ return true;
+ }
+ }
+
+ if (_vm->_game.id == GID_LOOM && _vm->_game.platform == Common::kPlatformPCEngine) {
+ if (soundID >= 13 && soundID <= 32) {
+ static const char tracks[20] = {3, 4, 5, 7, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 21, 19, 20, 21};
+
+ _currentCDSound = soundID;
+
+ // The original game had hard-coded lengths for all
+ // tracks, but this one track is the only one (as far
+ // as we know) where this actually matters. See bug
+ // #4914 - LOOM-PCE: Music stops prematurely.
+
+ int track = tracks[soundID - 13];
+ if (track == 6) {
+ playCDTrack(track, 1, 0, 260);
+ } else {
+ playCDTrack(track, 1, 0, 0);
+ }
+ } else {
+ if (_vm->_musicEngine) {
+ _vm->_musicEngine->startSound(soundID);
+ }
+ }
+ return true;
+ }
+
+ return false;
+}
+
+int SoundCD::getReplacementAudioTrack(int soundID) {
+ int trackNr = -1;
+
+ if (_vm->_game.id == GID_LOOM) {
+ if (_vm->_game.features & GF_DEMO) {
+ // If I understand correctly, the shorter demo only
+ // has the Loom intro music. The longer demo has a
+ // couple of tracks that it will cycle through if
+ // you leave the demo running.
+
+ if (isRolandLoom())
+ soundID -= 10;
+
+ switch (soundID) {
+ case 19:
+ trackNr = 2;
+ break;
+ case 20:
+ trackNr = 4;
+ break;
+ case 21:
+ trackNr = 7;
+ break;
+ case 23:
+ trackNr = 8;
+ break;
+ case 26:
+ trackNr = 3;
+ break;
+ }
+ } else {
+ if (isRolandLoom())
+ soundID -= 32;
+
+ // The first track, the Overture, only exists as a
+ // Roland track.
+ if (soundID >= 24 && soundID <= 32) {
+ trackNr = soundID - 23;
+ } else if (soundID == 19) {
+ trackNr = 10;
+ } else if (soundID == 21) {
+ trackNr = 11;
+ }
+ }
+ }
+
+ if (trackNr != -1 && !_vm->existExtractedCDAudioFiles(trackNr))
+ trackNr = -1;
+
+ return trackNr;
+}
+
+void SoundCD::pauseCDSounds(bool pause) {
+ if ((_vm->_game.features & GF_AUDIOTRACKS) && _vm->VAR_MUSIC_TIMER != 0xFF && _vm->VAR(_vm->VAR_MUSIC_TIMER) > 0) {
+ if (pause)
+ stopCDTimer();
+ else
+ startCDTimer();
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/soundcd.h b/engines/scumm/soundcd.h
new file mode 100644
index 00000000000..eb55c5a750c
--- /dev/null
+++ b/engines/scumm/soundcd.h
@@ -0,0 +1,98 @@
+/* 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/>.
+ *
+ */
+
+#ifndef SCUMM_SOUNDCD_H
+#define SCUMM_SOUNDCD_H
+
+#include "backends/audiocd/audiocd.h"
+#include "common/scummsys.h"
+#include "common/serializer.h"
+
+namespace Scumm {
+
+// The number of "ticks" (1/10th of a second) into the Overture that the
+// LucasFilm logo should appear. This corresponds to a timer value of 204.
+// The default value is selected to work well with the Ozawa recording.
+
+#define DEFAULT_LOOM_OVERTURE_TRANSITION 1160
+
+class ScummEngine;
+class SoundSE;
+
+class SoundCD {
+private:
+ ScummEngine *_vm;
+ Audio::Mixer *_mixer;
+ SoundSE *_soundSE;
+
+ Audio::SoundHandle *_fileBasedCDAudioHandle;
+ bool _hasFileBasedCDAudio = false;
+ AudioCDManager::Status _fileBasedCDStatus;
+
+ int _musicTimer = 0;
+
+ bool _useReplacementAudioTracks = false;
+ int _loomOvertureTransition = 0;
+ uint32 _replacementTrackStartTime = 0;
+
+public:
+ int16 _currentCDSound = 0;
+ uint32 _cdMusicTimerMod = 0;
+ uint32 _cdMusicTimer = 0;
+ bool _soundsPaused = false;
+
+ SoundCD(ScummEngine *parent, Audio::Mixer *mixer, SoundSE *soundSE, bool useReplacementAudioTracks);
+ ~SoundCD();
+
+ bool triggerCDSound(int soundID);
+ void stopCDSound(int sound);
+ void stopAllCDSounds();
+ void pauseCDSounds(bool pause);
+
+ bool useReplacementAudio() const { return _useReplacementAudioTracks; }
+ void updateMusicTimer();
+ int getMusicTimer() const { return _musicTimer; }
+ int getCDMusicTimer() const { return _cdMusicTimer; }
+
+ void startCDTimer();
+ void stopCDTimer();
+
+ void playCDTrack(int track, int numLoops, int startFrame, int duration);
+ void stopCD();
+ int pollCD() const;
+ void updateCD();
+ AudioCDManager::Status getCDStatus();
+ int getCurrentCDSound() const { return _currentCDSound; }
+
+ void restoreAfterLoad();
+ void restoreCDAudioAfterLoad(AudioCDManager::Status &info);
+
+ bool isRolandLoom() const;
+
+private:
+ int getReplacementAudioTrack(int soundID);
+ void playCDTrackInternal(int track, int numLoops, int startFrame, int duration);
+ int getCDTrackIdFromSoundId(int soundId, int &loops, int &start);
+};
+
+} // End of namespace Scumm
+
+#endif
More information about the Scummvm-git-logs
mailing list