[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