[Scummvm-git-logs] scummvm master -> 4a3b483aeb73f077b3898b0ae7aa16cb25a8c41e
mduggan
mgithub at guarana.org
Sat May 2 08:04:25 UTC 2020
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:
4a3b483aeb ULTIMA8: Engine support for loading crusader music
Commit: 4a3b483aeb73f077b3898b0ae7aa16cb25a8c41e
https://github.com/scummvm/scummvm/commit/4a3b483aeb73f077b3898b0ae7aa16cb25a8c41e
Author: Matthew Duggan (mgithub at guarana.org)
Date: 2020-05-02T17:02:27+09:00
Commit Message:
ULTIMA8: Engine support for loading crusader music
Changed paths:
A engines/ultima/ultima8/audio/remorse_music_process.cpp
A engines/ultima/ultima8/audio/remorse_music_process.h
A engines/ultima/ultima8/audio/u8_music_process.cpp
A engines/ultima/ultima8/audio/u8_music_process.h
engines/ultima/module.mk
engines/ultima/ultima8/audio/audio_channel.cpp
engines/ultima/ultima8/audio/audio_channel.h
engines/ultima/ultima8/audio/audio_mixer.cpp
engines/ultima/ultima8/audio/music_process.cpp
engines/ultima/ultima8/audio/music_process.h
engines/ultima/ultima8/games/game_data.cpp
engines/ultima/ultima8/games/remorse_game.cpp
engines/ultima/ultima8/gumps/remorse_menu_gump.cpp
engines/ultima/ultima8/ultima8.cpp
diff --git a/engines/ultima/module.mk b/engines/ultima/module.mk
index 01407b2f8b..90607d2e96 100644
--- a/engines/ultima/module.mk
+++ b/engines/ultima/module.mk
@@ -390,9 +390,11 @@ MODULE_OBJS := \
ultima8/audio/music_flex.o \
ultima8/audio/music_process.o \
ultima8/audio/raw_audio_sample.o \
+ ultima8/audio/remorse_music_process.o \
ultima8/audio/sonarc_audio_sample.o \
ultima8/audio/sound_flex.o \
ultima8/audio/speech_flex.o \
+ ultima8/audio/u8_music_process.o \
ultima8/conf/config_file_manager.o \
ultima8/conf/ini_file.o \
ultima8/conf/setting_manager.o \
diff --git a/engines/ultima/ultima8/audio/audio_channel.cpp b/engines/ultima/ultima8/audio/audio_channel.cpp
index 9e8383a3a0..376276f061 100644
--- a/engines/ultima/ultima8/audio/audio_channel.cpp
+++ b/engines/ultima/ultima8/audio/audio_channel.cpp
@@ -92,6 +92,10 @@ void AudioChannel::playSample(AudioSample *sample, int loop, int priority, bool
_mixer->pauseHandle(_soundHandle, true);
}
+void AudioChannel::playMusicStream(Audio::AudioStream *stream) {
+ _mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, stream);
+}
+
bool AudioChannel::isPlaying() {
if (!_mixer->isSoundHandleActive(_soundHandle))
_sample = nullptr;
diff --git a/engines/ultima/ultima8/audio/audio_channel.h b/engines/ultima/ultima8/audio/audio_channel.h
index eb7d9ea6f0..2092b026dd 100644
--- a/engines/ultima/ultima8/audio/audio_channel.h
+++ b/engines/ultima/ultima8/audio/audio_channel.h
@@ -65,6 +65,8 @@ public:
void playSample(AudioSample *sample, int loop, int priority, bool paused,
uint32 pitchShift, int lvol, int rvol);
+ void playMusicStream(Audio::AudioStream *stream);
+
bool isPlaying();
void setPitchShift(int pitchShift) {
diff --git a/engines/ultima/ultima8/audio/audio_mixer.cpp b/engines/ultima/ultima8/audio/audio_mixer.cpp
index 886219a60b..1add430ecb 100644
--- a/engines/ultima/ultima8/audio/audio_mixer.cpp
+++ b/engines/ultima/ultima8/audio/audio_mixer.cpp
@@ -23,10 +23,12 @@
#include "ultima/ultima8/misc/pent_include.h"
#include "ultima/ultima8/audio/audio_mixer.h"
#include "ultima/ultima8/audio/audio_process.h"
-#include "ultima/ultima8/audio/music_process.h"
+#include "ultima/ultima8/audio/u8_music_process.h"
+#include "ultima/ultima8/audio/remorse_music_process.h"
#include "ultima/ultima8/audio/audio_channel.h"
#include "ultima/ultima8/audio/midi_player.h"
#include "ultima/ultima8/kernel/kernel.h"
+#include "ultima/ultima8/kernel/core_app.h"
#include "audio/decoders/raw.h"
namespace Ultima {
@@ -51,7 +53,11 @@ void AudioMixer::createProcesses() {
kernel->addProcess(new AudioProcess());
// Create the Music Process
- kernel->addProcess(new MusicProcess(_midiPlayer));
+ if (GAME_IS_U8) {
+ kernel->addProcess(new U8MusicProcess(_midiPlayer));
+ } else if (GAME_IS_CRUSADER) {
+ kernel->addProcess(new RemorseMusicProcess());
+ }
}
AudioMixer::~AudioMixer(void) {
diff --git a/engines/ultima/ultima8/audio/music_process.cpp b/engines/ultima/ultima8/audio/music_process.cpp
index fe23c61b04..b3763548f0 100644
--- a/engines/ultima/ultima8/audio/music_process.cpp
+++ b/engines/ultima/ultima8/audio/music_process.cpp
@@ -35,255 +35,12 @@ DEFINE_RUNTIME_CLASSTYPE_CODE(MusicProcess, Process)
MusicProcess *MusicProcess::_theMusicProcess = nullptr;
-MusicProcess::MusicProcess() : _midiPlayer(nullptr), _state(PLAYBACK_NORMAL),
- _currentTrack(0), _combatMusicActive(false),
- _savedTrackState(nullptr) {
- Std::memset(_songBranches, (byte)-1, 128 * sizeof(int));
-}
-
-MusicProcess::MusicProcess(MidiPlayer *player) : _midiPlayer(player),
- _state(PLAYBACK_NORMAL), _currentTrack(0), _combatMusicActive(false),
- _savedTrackState(nullptr) {
- Std::memset(_songBranches, (byte)-1, 128 * sizeof(int));
-
+MusicProcess::MusicProcess() {
_theMusicProcess = this;
_type = 1; // persistent
setRunPaused();
}
-MusicProcess::~MusicProcess() {
- if (_savedTrackState)
- delete _savedTrackState;
- if (_midiPlayer)
- _midiPlayer->stop();
- _theMusicProcess = nullptr;
-}
-
-void MusicProcess::playMusic(int track) {
- _trackState._lastRequest = track;
-
- if (_combatMusicActive)
- return;
-
- if (_trackState._queued) {
- _trackState._queued = track;
- return;
- }
-
- playMusic_internal(track);
-}
-
-void MusicProcess::playCombatMusic(int track) {
- _combatMusicActive = (track != 0);
- playMusic_internal(track);
-}
-
-void MusicProcess::queueMusic(int track) {
- if (_trackState._wanted != track) {
- _trackState._queued = track;
- }
-}
-
-void MusicProcess::unqueueMusic() {
- _trackState._queued = 0;
-}
-
-void MusicProcess::restoreMusic() {
- _trackState._queued = 0;
- _combatMusicActive = false;
- playMusic_internal(_trackState._lastRequest);
-}
-
-void MusicProcess::getTrackState(TrackState &trackState) const {
- trackState = _trackState;
-}
-
-void MusicProcess::setTrackState(const TrackState &trackState) {
- _trackState = trackState;
- _state = PLAYBACK_PLAY_WANTED;
-}
-
-void MusicProcess::saveTrackState() {
- assert(!_savedTrackState);
- _savedTrackState = new TrackState(_trackState);
-}
-
-void MusicProcess::restoreTrackState() {
- if (_savedTrackState == nullptr)
- return;
-
- _trackState = *_savedTrackState;
- _state = PLAYBACK_PLAY_WANTED;
- delete _savedTrackState;
- _savedTrackState = nullptr;
-}
-
-void MusicProcess::playMusic_internal(int track) {
- if (track < 0 || track >= 128) {
- playMusic_internal(0);
- return;
- }
-
- MusicFlex *musicflex = GameData::get_instance()->getMusic();
-
- // No current track if not playing
- if (_midiPlayer && !_midiPlayer->isPlaying())
- _trackState._wanted = _currentTrack = 0;
-
- // It's already playing and we are not transitioning
- if (_currentTrack == track && _state == PLAYBACK_NORMAL) {
- return;
- } else if (_currentTrack == 0 || _state != PLAYBACK_NORMAL || !_midiPlayer) {
- _trackState._wanted = track;
- _state = PLAYBACK_PLAY_WANTED;
-
- } else {
- // We want to do a transition
- const MusicFlex::SongInfo *info = musicflex->getSongInfo(_currentTrack);
-
- uint32 measure = _midiPlayer->getSequenceCallbackData(0);
-
- // No transition info, or invalid measure, so fast change
- if (!info || (measure >= (uint32)info->_numMeasures) ||
- !info->_transitions[track] || !info->_transitions[track][measure]) {
- _currentTrack = 0;
- if (track == 0) {
- _trackState._wanted = 0;
- _state = PLAYBACK_PLAY_WANTED;
- } else {
- playMusic_internal(track);
- }
- return;
- }
-
- // Get transition info
- int trans = info->_transitions[track][measure];
- bool speed_hack = false;
-
- if (trans < 0) {
- trans = (-trans) - 1;
- speed_hack = true;
- } else {
- _midiPlayer->stop();
- trans = trans - 1;
- }
-
- // Now get the transition midi
- int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
- MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
-
- warning("Doing a MIDI transition! trans: %d xmidi: %d speedhack: %d", trans, xmidi_index, speed_hack);
-
- if (xmidi && xmidi->_data) {
- _midiPlayer->play(xmidi->_data, xmidi->_size, 1, trans, speed_hack);
- } else {
- _midiPlayer->stop();
- }
-
- _trackState._wanted = track;
- _state = PLAYBACK_TRANSITION;
- }
-}
-
-void MusicProcess::run() {
- switch (_state) {
- case PLAYBACK_NORMAL:
- if (_midiPlayer && !_midiPlayer->isPlaying() && _trackState._queued) {
- _trackState._wanted = _trackState._queued;
- _state = PLAYBACK_PLAY_WANTED;
- _trackState._queued = 0;
- }
-
- break;
-
- case PLAYBACK_TRANSITION:
- if (!_midiPlayer) {
- _state = PLAYBACK_PLAY_WANTED;
- } else {
- _state = PLAYBACK_PLAY_WANTED;
- _midiPlayer->stop();
- }
- break;
-
- case PLAYBACK_PLAY_WANTED: {
- if (_midiPlayer)
- _midiPlayer->stop();
-
- MusicFlex::XMidiData *xmidi = nullptr;
-
- if (_trackState._wanted) {
- int xmidi_index = _trackState._wanted;
- if (_midiPlayer && _midiPlayer->isFMSynth())
- xmidi_index += 128;
-
- xmidi = GameData::get_instance()->getMusic()->getXMidi(xmidi_index);
- }
-
- if (xmidi && xmidi->_data) {
-#ifdef TODO
- // TODO: support branches in tracks.
- // Not clear how to do this with the scummvm xmidi parser..
- if (song_branches[wanted_track] != -1)
- {
- XMidiEvent *event = list->findBranchEvent(song_branches[wanted_track]);
- if (!event) song_branches[wanted_track] = 0;
- }
-#endif
-
- if (_midiPlayer) {
- // if there's a track queued, only play this one once
- bool repeat = (_trackState._queued == 0);
- _midiPlayer->play(xmidi->_data, xmidi->_size, 0, 0, false);
- _midiPlayer->setLooping(repeat);
- }
-
- _currentTrack = _trackState._wanted;
- _songBranches[_trackState._wanted]++;
- } else {
- _currentTrack = _trackState._wanted = 0;
- }
- _state = PLAYBACK_NORMAL;
- }
- break;
- }
-}
-
-void MusicProcess::saveData(Common::WriteStream *ws) {
- Process::saveData(ws);
-
- // When saving the game we want to remember the track state
- // from before the menu was opened
- const TrackState *stateToSave = _savedTrackState;
- if (stateToSave == nullptr)
- stateToSave = &_trackState;
-
- ws->writeUint32LE(static_cast<uint32>(stateToSave->_wanted));
- ws->writeUint32LE(static_cast<uint32>(stateToSave->_lastRequest));
- ws->writeUint32LE(static_cast<uint32>(stateToSave->_queued));
-}
-
-bool MusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
- if (!Process::loadData(rs, version)) return false;
-
- _trackState._wanted = static_cast<int32>(rs->readUint32LE());
-
- if (version >= 4) {
- _trackState._lastRequest = static_cast<int32>(rs->readUint32LE());
- _trackState._queued = static_cast<int32>(rs->readUint32LE());
- } else {
- _trackState._lastRequest = _trackState._wanted;
- _trackState._queued = 0;
- }
-
- _state = PLAYBACK_PLAY_WANTED;
-
- _theMusicProcess = this;
-
- _midiPlayer = AudioMixer::get_instance()->getMidiPlayer();
-
- return true;
-}
-
uint32 MusicProcess::I_musicStop(const uint8 * /*args*/,
unsigned int /*argsize*/) {
if (_theMusicProcess) _theMusicProcess->playMusic_internal(0);
diff --git a/engines/ultima/ultima8/audio/music_process.h b/engines/ultima/ultima8/audio/music_process.h
index f79db53569..51318d8ba1 100644
--- a/engines/ultima/ultima8/audio/music_process.h
+++ b/engines/ultima/ultima8/audio/music_process.h
@@ -37,54 +37,15 @@ class MidiPlayer;
class MusicProcess : public Process {
friend class Debugger;
- enum PlaybackStates {
- PLAYBACK_NORMAL = 1,
- PLAYBACK_TRANSITION = 2,
- PLAYBACK_PLAY_WANTED = 3
- };
-
-public:
- //! The saveable part of track state
- struct TrackState {
- //! Track we want to play
- int _wanted;
- //! Last requested track that was not a temporary (ie, combat) track
- int _lastRequest;
- //! Track queued to start after current
- int _queued;
-
- TrackState() : _wanted(0), _lastRequest(0), _queued(0) { }
- TrackState(int wanted, int lastRequest, int queued) :
- _wanted(wanted), _lastRequest(lastRequest), _queued(queued) { }
- };
-
-private:
- void saveData(Common::WriteStream *ws) override;
-
+protected:
//! Play a music track
//! \param track The track number to play. Pass 0 to stop music
- void playMusic_internal(int track);
+ virtual void playMusic_internal(int track) = 0;
static MusicProcess *_theMusicProcess;
- MidiPlayer *_midiPlayer;
- PlaybackStates _state;
- int _songBranches[128];
-
- int _currentTrack; //! Currently playing track (don't save)
-
- TrackState _trackState;
-
- //! The track state temporarily saved when using the menu etc
- TrackState *_savedTrackState;
-
- //! Is the current music "combat" music
- bool _combatMusicActive;
-
public:
MusicProcess();
- MusicProcess(MidiPlayer *player); // Note that this does NOT delete the driver
- ~MusicProcess() override;
// p_dynamic_cast stuff
ENABLE_RUNTIME_CLASSTYPE()
@@ -95,33 +56,24 @@ public:
}
//! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
- void playMusic(int track);
+ virtual void playMusic(int track) = 0;
//! Play some combat music - the last played track will be remembered
- void playCombatMusic(int track);
+ virtual void playCombatMusic(int track) = 0;
//! Queue a track to start once the current one finishes
- void queueMusic(int track);
+ virtual void queueMusic(int track) = 0;
//! Clear any queued track (does not affect currently playing track)
- void unqueueMusic();
+ virtual void unqueueMusic() = 0;
//! Restore the last requested non-combat track (eg, at the end of combat)
- void restoreMusic();
+ virtual void restoreMusic() = 0;
//! Save the current track state - used when the menu is opened
- void saveTrackState();
+ virtual void saveTrackState() = 0;
//! Bring back the track state from before it was put on hold
- void restoreTrackState();
+ virtual void restoreTrackState() = 0;
INTRINSIC(I_playMusic);
INTRINSIC(I_musicStop);
-
- //! Get the state of tracks (wanted, requested, queued)
- void getTrackState(TrackState &trackState) const;
-
- void setTrackState(const TrackState &state);
-
- void run() override;
-
- bool loadData(Common::ReadStream *rs, uint32 version);
};
} // End of namespace Ultima8
diff --git a/engines/ultima/ultima8/audio/remorse_music_process.cpp b/engines/ultima/ultima8/audio/remorse_music_process.cpp
new file mode 100644
index 0000000000..f20f1abf45
--- /dev/null
+++ b/engines/ultima/ultima8/audio/remorse_music_process.cpp
@@ -0,0 +1,164 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ultima/ultima8/misc/pent_include.h"
+#include "ultima/ultima8/ultima8.h"
+#include "ultima/ultima8/audio/remorse_music_process.h"
+#include "ultima/ultima8/games/game_data.h"
+#include "ultima/ultima8/audio/music_flex.h"
+#include "ultima/ultima8/audio/midi_player.h"
+#include "ultima/ultima8/audio/audio_mixer.h"
+#include "ultima/ultima8/filesys/file_system.h"
+#include "audio/mods/mod_xm_s3m.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+static const int MAX_TRACK = 20;
+static const char *TRACK_FILE_NAMES[] = {
+ nullptr,
+ "cred",
+ "M01",
+ "M02",
+ "M03",
+ "M04",
+ "M05",
+ "M06",
+ "M07",
+ "M08",
+ "M09",
+ "M10",
+ "M11",
+ "M12",
+ "M13",
+ "M14",
+ "M15",
+ "M16A",
+ "M16B",
+ "M16C",
+ "menu"
+};
+
+// p_dynamic_cast stuff
+DEFINE_RUNTIME_CLASSTYPE_CODE(RemorseMusicProcess, MusicProcess)
+
+RemorseMusicProcess::RemorseMusicProcess() : MusicProcess(), _currentTrack(0), _savedTrack(0), _playingStream(nullptr) {
+}
+
+RemorseMusicProcess::~RemorseMusicProcess() {
+ if (_playingStream) {
+ delete _playingStream;
+ }
+}
+
+void RemorseMusicProcess::playMusic(int track) {
+ playMusic_internal(track);
+}
+
+void RemorseMusicProcess::playCombatMusic(int track) {
+ playMusic_internal(track);
+}
+
+void RemorseMusicProcess::queueMusic(int track) {
+ playMusic_internal(track);
+}
+
+void RemorseMusicProcess::unqueueMusic() {
+
+}
+
+void RemorseMusicProcess::restoreMusic() {
+
+}
+
+void RemorseMusicProcess::saveTrackState() {
+ assert(!_savedTrack);
+ _savedTrack = _currentTrack;
+}
+
+void RemorseMusicProcess::restoreTrackState() {
+ _currentTrack = _savedTrack;
+ playMusic_internal(_currentTrack);
+}
+
+void RemorseMusicProcess::playMusic_internal(int track) {
+ if (track < 0 || track > MAX_TRACK) {
+ playMusic_internal(0);
+ return;
+ }
+
+ if (track == _currentTrack)
+ return;
+
+ Audio::Mixer *mixer = Ultima8Engine::get_instance()->_mixer;
+ assert(mixer);
+
+ mixer->stopHandle(_soundHandle);
+ _soundHandle = Audio::SoundHandle();
+ delete _playingStream;
+
+ if (track > 0) {
+ // TODO: It's a bit ugly having this here. Should be in GameData.
+ const Std::string fname = Std::string::format("@game/sound/%s.amf", TRACK_FILE_NAMES[track]);
+ FileSystem *filesystem = FileSystem::get_instance();
+ assert(filesystem);
+ Common::SeekableReadStream *rs = filesystem->ReadFile(fname);
+ if (!rs) {
+ error("Couldn't load AMF file: %s", fname.c_str());
+ return;
+ }
+
+ Audio::AudioStream *_playingStream = Audio::makeModXmS3mStream(rs, DisposeAfterUse::YES);
+ if (!_playingStream) {
+ error("Couldn't create stream from AMF file: %s", fname.c_str());
+ return;
+ }
+ mixer->playStream(Audio::Mixer::kMusicSoundType, &_soundHandle, _playingStream);
+ }
+}
+
+void RemorseMusicProcess::run() {
+ if (!_playingStream || !_playingStream->endOfStream()) {
+ // nothing to do
+ return;
+ }
+ // hit end of stream, play it again.
+ // TODO: Probably this doesn't perfectly loop, should do something a bit nicer..
+ playMusic_internal(_currentTrack);
+}
+
+void RemorseMusicProcess::saveData(Common::WriteStream *ws) {
+ Process::saveData(ws);
+
+ ws->writeUint32LE(static_cast<uint32>(_currentTrack));
+}
+
+bool RemorseMusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
+ if (!Process::loadData(rs, version)) return false;
+
+ _currentTrack = static_cast<int32>(rs->readUint32LE());
+
+ return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/audio/remorse_music_process.h b/engines/ultima/ultima8/audio/remorse_music_process.h
new file mode 100644
index 0000000000..1394f3b32c
--- /dev/null
+++ b/engines/ultima/ultima8/audio/remorse_music_process.h
@@ -0,0 +1,94 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ULTIMA8_AUDIO_REMORSEMUSICPROCESS_H
+#define ULTIMA8_AUDIO_REMORSEMUSICPROCESS_H
+
+#include "ultima/ultima8/audio/music_process.h"
+#include "ultima/ultima8/kernel/process.h"
+#include "ultima/ultima8/usecode/intrinsics.h"
+#include "ultima/ultima8/misc/p_dynamic_cast.h"
+#include "audio/audiostream.h"
+#include "audio/mixer.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+class Debugger;
+class MidiPlayer;
+
+class RemorseMusicProcess : public MusicProcess {
+ friend class Debugger;
+
+protected:
+ void saveData(Common::WriteStream *ws) override;
+
+ //! Play a music track
+ //! \param track The track number to play. Pass 0 to stop music
+ void playMusic_internal(int track);
+
+private:
+ int _currentTrack; //! Currently playing track (don't save)
+
+ //! Is the current music "combat" music
+ bool _combatMusicActive;
+
+ int _savedTrack;
+
+ Audio::SoundHandle _soundHandle;
+ Audio::AudioStream *_playingStream;
+
+public:
+ RemorseMusicProcess();
+ ~RemorseMusicProcess() override;
+
+ // p_dynamic_cast stuff
+ ENABLE_RUNTIME_CLASSTYPE()
+
+ //! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
+ void playMusic(int track);
+ //! Play some combat music - the last played track will be remembered
+ void playCombatMusic(int track);
+ //! Queue a track to start once the current one finishes
+ void queueMusic(int track);
+ //! Clear any queued track (does not affect currently playing track)
+ void unqueueMusic();
+ //! Restore the last requested non-combat track (eg, at the end of combat)
+ void restoreMusic();
+
+ //! Save the current track state - used when the menu is opened
+ void saveTrackState();
+ //! Bring back the track state from before it was put on hold
+ void restoreTrackState();
+
+ INTRINSIC(I_playMusic);
+ INTRINSIC(I_musicStop);
+
+ void run() override;
+
+ bool loadData(Common::ReadStream *rs, uint32 version);
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/audio/u8_music_process.cpp b/engines/ultima/ultima8/audio/u8_music_process.cpp
new file mode 100644
index 0000000000..2fae0267f9
--- /dev/null
+++ b/engines/ultima/ultima8/audio/u8_music_process.cpp
@@ -0,0 +1,286 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "ultima/ultima8/misc/pent_include.h"
+#include "ultima/ultima8/audio/u8_music_process.h"
+#include "ultima/ultima8/games/game_data.h"
+#include "ultima/ultima8/audio/music_flex.h"
+#include "ultima/ultima8/audio/midi_player.h"
+#include "ultima/ultima8/audio/audio_mixer.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+// p_dynamic_cast stuff
+DEFINE_RUNTIME_CLASSTYPE_CODE(U8MusicProcess, MusicProcess)
+
+U8MusicProcess::U8MusicProcess() : _midiPlayer(nullptr), _state(PLAYBACK_NORMAL),
+ _currentTrack(0), _combatMusicActive(false),
+ _savedTrackState(nullptr) {
+ Std::memset(_songBranches, (byte)-1, 128 * sizeof(int));
+}
+
+U8MusicProcess::U8MusicProcess(MidiPlayer *player) : _midiPlayer(player),
+ _state(PLAYBACK_NORMAL), _currentTrack(0), _combatMusicActive(false),
+ _savedTrackState(nullptr) {
+ Std::memset(_songBranches, (byte)-1, 128 * sizeof(int));
+
+ _theMusicProcess = this;
+ _type = 1; // persistent
+ setRunPaused();
+}
+
+U8MusicProcess::~U8MusicProcess() {
+ if (_savedTrackState)
+ delete _savedTrackState;
+ if (_midiPlayer)
+ _midiPlayer->stop();
+ _theMusicProcess = nullptr;
+}
+
+void U8MusicProcess::playMusic(int track) {
+ _trackState._lastRequest = track;
+
+ if (_combatMusicActive)
+ return;
+
+ if (_trackState._queued) {
+ _trackState._queued = track;
+ return;
+ }
+
+ playMusic_internal(track);
+}
+
+void U8MusicProcess::playCombatMusic(int track) {
+ _combatMusicActive = (track != 0);
+ playMusic_internal(track);
+}
+
+void U8MusicProcess::queueMusic(int track) {
+ if (_trackState._wanted != track) {
+ _trackState._queued = track;
+ }
+}
+
+void U8MusicProcess::unqueueMusic() {
+ _trackState._queued = 0;
+}
+
+void U8MusicProcess::restoreMusic() {
+ _trackState._queued = 0;
+ _combatMusicActive = false;
+ playMusic_internal(_trackState._lastRequest);
+}
+
+void U8MusicProcess::getTrackState(TrackState &trackState) const {
+ trackState = _trackState;
+}
+
+void U8MusicProcess::setTrackState(const TrackState &trackState) {
+ _trackState = trackState;
+ _state = PLAYBACK_PLAY_WANTED;
+}
+
+void U8MusicProcess::saveTrackState() {
+ assert(!_savedTrackState);
+ _savedTrackState = new TrackState(_trackState);
+}
+
+void U8MusicProcess::restoreTrackState() {
+ if (_savedTrackState == nullptr)
+ return;
+
+ _trackState = *_savedTrackState;
+ _state = PLAYBACK_PLAY_WANTED;
+ delete _savedTrackState;
+ _savedTrackState = nullptr;
+}
+
+void U8MusicProcess::playMusic_internal(int track) {
+ if (track < 0 || track >= 128) {
+ playMusic_internal(0);
+ return;
+ }
+
+ MusicFlex *musicflex = GameData::get_instance()->getMusic();
+
+ // No current track if not playing
+ if (_midiPlayer && !_midiPlayer->isPlaying())
+ _trackState._wanted = _currentTrack = 0;
+
+ // It's already playing and we are not transitioning
+ if (_currentTrack == track && _state == PLAYBACK_NORMAL) {
+ return;
+ } else if (_currentTrack == 0 || _state != PLAYBACK_NORMAL || !_midiPlayer) {
+ _trackState._wanted = track;
+ _state = PLAYBACK_PLAY_WANTED;
+
+ } else {
+ // We want to do a transition
+ const MusicFlex::SongInfo *info = musicflex->getSongInfo(_currentTrack);
+
+ uint32 measure = _midiPlayer->getSequenceCallbackData(0);
+
+ // No transition info, or invalid measure, so fast change
+ if (!info || (measure >= (uint32)info->_numMeasures) ||
+ !info->_transitions[track] || !info->_transitions[track][measure]) {
+ _currentTrack = 0;
+ if (track == 0) {
+ _trackState._wanted = 0;
+ _state = PLAYBACK_PLAY_WANTED;
+ } else {
+ playMusic_internal(track);
+ }
+ return;
+ }
+
+ // Get transition info
+ int trans = info->_transitions[track][measure];
+ bool speed_hack = false;
+
+ if (trans < 0) {
+ trans = (-trans) - 1;
+ speed_hack = true;
+ } else {
+ _midiPlayer->stop();
+ trans = trans - 1;
+ }
+
+ // Now get the transition midi
+ int xmidi_index = _midiPlayer->isFMSynth() ? 260 : 258;
+ MusicFlex::XMidiData *xmidi = musicflex->getXMidi(xmidi_index);
+
+ warning("Doing a MIDI transition! trans: %d xmidi: %d speedhack: %d", trans, xmidi_index, speed_hack);
+
+ if (xmidi && xmidi->_data) {
+ _midiPlayer->play(xmidi->_data, xmidi->_size, 1, trans, speed_hack);
+ } else {
+ _midiPlayer->stop();
+ }
+
+ _trackState._wanted = track;
+ _state = PLAYBACK_TRANSITION;
+ }
+}
+
+void U8MusicProcess::run() {
+ switch (_state) {
+ case PLAYBACK_NORMAL:
+ if (_midiPlayer && !_midiPlayer->isPlaying() && _trackState._queued) {
+ _trackState._wanted = _trackState._queued;
+ _state = PLAYBACK_PLAY_WANTED;
+ _trackState._queued = 0;
+ }
+
+ break;
+
+ case PLAYBACK_TRANSITION:
+ if (!_midiPlayer) {
+ _state = PLAYBACK_PLAY_WANTED;
+ } else {
+ _state = PLAYBACK_PLAY_WANTED;
+ _midiPlayer->stop();
+ }
+ break;
+
+ case PLAYBACK_PLAY_WANTED: {
+ if (_midiPlayer)
+ _midiPlayer->stop();
+
+ MusicFlex::XMidiData *xmidi = nullptr;
+
+ if (_trackState._wanted) {
+ int xmidi_index = _trackState._wanted;
+ if (_midiPlayer && _midiPlayer->isFMSynth())
+ xmidi_index += 128;
+
+ xmidi = GameData::get_instance()->getMusic()->getXMidi(xmidi_index);
+ }
+
+ if (xmidi && xmidi->_data) {
+#ifdef TODO
+ // TODO: support branches in tracks.
+ // Not clear how to do this with the scummvm xmidi parser..
+ if (song_branches[wanted_track] != -1)
+ {
+ XMidiEvent *event = list->findBranchEvent(song_branches[wanted_track]);
+ if (!event) song_branches[wanted_track] = 0;
+ }
+#endif
+
+ if (_midiPlayer) {
+ // if there's a track queued, only play this one once
+ bool repeat = (_trackState._queued == 0);
+ _midiPlayer->play(xmidi->_data, xmidi->_size, 0, 0, false);
+ _midiPlayer->setLooping(repeat);
+ }
+
+ _currentTrack = _trackState._wanted;
+ _songBranches[_trackState._wanted]++;
+ } else {
+ _currentTrack = _trackState._wanted = 0;
+ }
+ _state = PLAYBACK_NORMAL;
+ }
+ break;
+ }
+}
+
+void U8MusicProcess::saveData(Common::WriteStream *ws) {
+ MusicProcess::saveData(ws);
+
+ // When saving the game we want to remember the track state
+ // from before the menu was opened
+ const TrackState *stateToSave = _savedTrackState;
+ if (stateToSave == nullptr)
+ stateToSave = &_trackState;
+
+ ws->writeUint32LE(static_cast<uint32>(stateToSave->_wanted));
+ ws->writeUint32LE(static_cast<uint32>(stateToSave->_lastRequest));
+ ws->writeUint32LE(static_cast<uint32>(stateToSave->_queued));
+}
+
+bool U8MusicProcess::loadData(Common::ReadStream *rs, uint32 version) {
+ if (!MusicProcess::loadData(rs, version)) return false;
+
+ _trackState._wanted = static_cast<int32>(rs->readUint32LE());
+
+ if (version >= 4) {
+ _trackState._lastRequest = static_cast<int32>(rs->readUint32LE());
+ _trackState._queued = static_cast<int32>(rs->readUint32LE());
+ } else {
+ _trackState._lastRequest = _trackState._wanted;
+ _trackState._queued = 0;
+ }
+
+ _state = PLAYBACK_PLAY_WANTED;
+
+ _theMusicProcess = this;
+
+ _midiPlayer = AudioMixer::get_instance()->getMidiPlayer();
+
+ return true;
+}
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
diff --git a/engines/ultima/ultima8/audio/u8_music_process.h b/engines/ultima/ultima8/audio/u8_music_process.h
new file mode 100644
index 0000000000..94e437e708
--- /dev/null
+++ b/engines/ultima/ultima8/audio/u8_music_process.h
@@ -0,0 +1,125 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef ULTIMA8_AUDIO_U8MUSICPROCESS_H
+#define ULTIMA8_AUDIO_U8MUSICPROCESS_H
+
+#include "ultima/ultima8/audio/music_process.h"
+#include "ultima/ultima8/kernel/process.h"
+#include "ultima/ultima8/usecode/intrinsics.h"
+#include "ultima/ultima8/misc/p_dynamic_cast.h"
+#include "audio/mididrv.h"
+
+namespace Ultima {
+namespace Ultima8 {
+
+class Debugger;
+class MidiPlayer;
+
+class U8MusicProcess : public MusicProcess {
+ friend class Debugger;
+
+ enum PlaybackStates {
+ PLAYBACK_NORMAL = 1,
+ PLAYBACK_TRANSITION = 2,
+ PLAYBACK_PLAY_WANTED = 3
+ };
+
+public:
+ //! The saveable part of track state
+ struct TrackState {
+ //! Track we want to play
+ int _wanted;
+ //! Last requested track that was not a temporary (ie, combat) track
+ int _lastRequest;
+ //! Track queued to start after current
+ int _queued;
+
+ TrackState() : _wanted(0), _lastRequest(0), _queued(0) { }
+ TrackState(int wanted, int lastRequest, int queued) :
+ _wanted(wanted), _lastRequest(lastRequest), _queued(queued) { }
+ };
+
+private:
+ void saveData(Common::WriteStream *ws) override;
+
+ //! Play a music track
+ //! \param track The track number to play. Pass 0 to stop music
+ void playMusic_internal(int track);
+
+ MidiPlayer *_midiPlayer;
+ PlaybackStates _state;
+ int _songBranches[128];
+
+ int _currentTrack; //! Currently playing track (don't save)
+
+ TrackState _trackState;
+
+ //! The track state temporarily saved when using the menu etc
+ TrackState *_savedTrackState;
+
+ //! Is the current music "combat" music
+ bool _combatMusicActive;
+
+public:
+ U8MusicProcess();
+ U8MusicProcess(MidiPlayer *player); // Note that this does NOT delete the driver
+ ~U8MusicProcess() override;
+
+ // p_dynamic_cast stuff
+ ENABLE_RUNTIME_CLASSTYPE()
+
+ //! Get the current instance of the Music Processes
+ static MusicProcess *get_instance() {
+ return _theMusicProcess;
+ }
+
+ //! Play some background music. Does not change the current track if combat music is active. If another track is currently queued, just queues this track for play.
+ void playMusic(int track);
+ //! Play some combat music - the last played track will be remembered
+ void playCombatMusic(int track);
+ //! Queue a track to start once the current one finishes
+ void queueMusic(int track);
+ //! Clear any queued track (does not affect currently playing track)
+ void unqueueMusic();
+ //! Restore the last requested non-combat track (eg, at the end of combat)
+ void restoreMusic();
+
+ //! Save the current track state - used when the menu is opened
+ void saveTrackState();
+ //! Bring back the track state from before it was put on hold
+ void restoreTrackState();
+
+ //! Get the state of tracks (wanted, requested, queued)
+ void getTrackState(TrackState &trackState) const;
+
+ void setTrackState(const TrackState &state);
+
+ void run() override;
+
+ bool loadData(Common::ReadStream *rs, uint32 version);
+};
+
+} // End of namespace Ultima8
+} // End of namespace Ultima
+
+#endif
diff --git a/engines/ultima/ultima8/games/game_data.cpp b/engines/ultima/ultima8/games/game_data.cpp
index fe942de632..929aced5e1 100644
--- a/engines/ultima/ultima8/games/game_data.cpp
+++ b/engines/ultima/ultima8/games/game_data.cpp
@@ -24,12 +24,12 @@
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/games/game_data.h"
#include "ultima/ultima8/filesys/file_system.h"
+#include "ultima/ultima8/filesys/raw_archive.h"
#include "ultima/ultima8/filesys/idata_source.h"
#include "ultima/ultima8/usecode/usecode_flex.h"
#include "ultima/ultima8/graphics/main_shape_archive.h"
#include "ultima/ultima8/graphics/fonts/font_shape_archive.h"
#include "ultima/ultima8/graphics/gump_shape_archive.h"
-#include "ultima/ultima8/filesys/raw_archive.h"
#include "ultima/ultima8/world/map_glob.h"
#include "ultima/ultima8/graphics/palette_manager.h"
#include "ultima/ultima8/graphics/shape.h"
@@ -598,20 +598,8 @@ void GameData::loadRemorseData() {
delete gumpageds;
#endif
- Common::SeekableReadStream *dummyrs = filesystem->ReadFile("@data/empty.flx");
- _music = nullptr; //new MusicFlex(dummyds);
- delete dummyrs;
-#if 0
- Common::SeekableReadStream *mf = filesystem->ReadFile("@game/sound/music.flx");
- if (!mf)
- error("Unable to load sound/music.flx");
-
- _music = new MusicFlex(mf);
-#endif
-
- dummyrs = filesystem->ReadFile("@data/empty.flx");
- _soundFlex = new SoundFlex(dummyrs);
- delete dummyrs;
+ // Note: No MusicFlex for Remorse, as the music is all in different AMF files.
+ // The remorse_music_process will load them.
Common::SeekableReadStream *sndflx = filesystem->ReadFile("@game/sound/sound.flx");
if (!sndflx)
diff --git a/engines/ultima/ultima8/games/remorse_game.cpp b/engines/ultima/ultima8/games/remorse_game.cpp
index 4f95c977e3..40ca8ebbe4 100644
--- a/engines/ultima/ultima8/games/remorse_game.cpp
+++ b/engines/ultima/ultima8/games/remorse_game.cpp
@@ -152,7 +152,7 @@ static ProcId playMovie(const char *movieID, bool fade) {
}
ProcId RemorseGame::playIntroMovie(bool fade) {
- return playMovie("T01", fade);
+ return playMovie("T02", fade);
// TODO: also play T02
}
diff --git a/engines/ultima/ultima8/gumps/remorse_menu_gump.cpp b/engines/ultima/ultima8/gumps/remorse_menu_gump.cpp
index cf47671f61..1df91b152a 100644
--- a/engines/ultima/ultima8/gumps/remorse_menu_gump.cpp
+++ b/engines/ultima/ultima8/gumps/remorse_menu_gump.cpp
@@ -66,8 +66,8 @@ RemorseMenuGump::RemorseMenuGump(bool nameEntryMode_)
MusicProcess *musicprocess = MusicProcess::get_instance();
if (musicprocess) {
musicprocess->saveTrackState();
- // Stop any playing music.
- musicprocess->playCombatMusic(0);
+ // Play the menu music
+ musicprocess->playMusic(20);
}
MetaEngine::setGameMenuActive(true);
}
diff --git a/engines/ultima/ultima8/ultima8.cpp b/engines/ultima/ultima8/ultima8.cpp
index 4aef9e7494..248a9d72a4 100644
--- a/engines/ultima/ultima8/ultima8.cpp
+++ b/engines/ultima/ultima8/ultima8.cpp
@@ -104,7 +104,8 @@
#include "ultima/ultima8/gumps/shape_viewer_gump.h"
#include "ultima/ultima8/audio/audio_mixer.h"
#include "ultima/ultima8/graphics/xform_blend.h"
-#include "ultima/ultima8/audio/music_process.h"
+#include "ultima/ultima8/audio/u8_music_process.h"
+#include "ultima/ultima8/audio/remorse_music_process.h"
#include "ultima/ultima8/audio/audio_process.h"
#include "ultima/ultima8/misc/util.h"
#include "ultima/ultima8/audio/midi_player.h"
@@ -233,8 +234,10 @@ void Ultima8Engine::startup() {
ProcessLoader<SpriteProcess>::load);
_kernel->addProcessLoader("CameraProcess",
ProcessLoader<CameraProcess>::load);
- _kernel->addProcessLoader("MusicProcess",
- ProcessLoader<MusicProcess>::load);
+ _kernel->addProcessLoader("MusicProcess", // parent class name for save game backwards-compatibility.
+ ProcessLoader<U8MusicProcess>::load);
+ _kernel->addProcessLoader("RemorseMusicProcess",
+ ProcessLoader<RemorseMusicProcess>::load);
_kernel->addProcessLoader("AudioProcess",
ProcessLoader<AudioProcess>::load);
_kernel->addProcessLoader("EggHatcherProcess",
More information about the Scummvm-git-logs
mailing list