AGOS: Fix Simon 2 intro first scene MT-32 music

The first scene of the Simon The Sorcerer 2 intro uses 3 music tracks. The last
2 are missing from the MT-32 MIDI data set. The original interpreter just stops
playing music after the first track and does not restart until the next scene.

This commit fixes this problem by using the GM versions of these 2 tracks
instead and mapping the GM instruments to MT-32 ones.
This commit is contained in:
Coen Rampen 2022-02-18 23:16:22 +01:00
parent b55b1da128
commit a72704764b
7 changed files with 70 additions and 8 deletions

View File

@ -754,9 +754,9 @@ void AGOSEngine_Simon2::setupGame() {
_tableMemSize = 100000;
// Check whether to use MT-32 MIDI tracks in Simon the Sorcerer 2
if (getGameType() == GType_SIMON2 && getPlatform() == Common::kPlatformDOS && _midi->usesMT32Data())
_musicIndexBase = (1128 + 612) / 4;
_musicIndexBase = MUSIC_INDEX_BASE_SIMON2_MT32;
else
_musicIndexBase = 1128 / 4;
_musicIndexBase = MUSIC_INDEX_BASE_SIMON2_GM;
_soundIndexBase = 1660 / 4;
_frameCount = 1;
_vgaBaseDelay = 1;

View File

@ -202,6 +202,11 @@ protected:
// List of Simon 1 DOS floppy SFX which use rhythm notes.
static const byte SIMON1_RHYTHM_SFX[];
// Music index base for Simon 2 GM data.
static const uint16 MUSIC_INDEX_BASE_SIMON2_GM = 1128 / 4;
// Music index base for Simon 2 MT-32 data.
static const uint16 MUSIC_INDEX_BASE_SIMON2_MT32 = (1128 + 612) / 4;
protected:
friend class Debugger;
@ -1289,7 +1294,11 @@ protected:
void windowScroll(WindowBlock *window);
virtual void windowDrawChar(WindowBlock *window, uint x, uint y, byte chr);
void loadMusic(uint16 track);
// Loads the MIDI data for the specified track. The forceSimon2Gm parameter
// forces loading the MIDI data from the GM data set and activates GM to
// MT-32 instrument remapping. This is useful only for a specific
// workaround (see AGOSEngine_Simon2::playMusic for more details).
void loadMusic(uint16 track, bool forceSimon2Gm = false);
void playModule(uint16 music);
virtual void playMusic(uint16 music, uint16 track);
void stopMusic();
@ -1943,6 +1952,10 @@ protected:
void clearVideoWindow(uint16 windowNum, uint16 color) override;
void playSpeech(uint16 speechId, uint16 vgaSpriteId) override;
// This overload plays the music track specified in the second parameter.
// The first parameter is ignored; music data must be loaded using the
// loadMusic method before calling this method.
void playMusic(uint16 music, uint16 track) override;
Common::String genSaveName(int slot) const override;
};

View File

@ -54,6 +54,20 @@ namespace AGOS {
// and just provide a factory function.
extern MidiParser *MidiParser_createS1D();
// This instrument remapping has been constructed by checking how the GM
// instruments correspond to MT-32 instruments in other tracks (f.e. track 10-3
// is similar to track 11).
byte MidiPlayer::SIMON2_TRACK10_GM_MT32_INSTRUMENT_REMAPPING[] {
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x65, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x33, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x1D, 0x00,
0x00, 0x00, 0x00, 0x00, 0x54, 0x00, 0x56, 0x53, 0x4B, 0x00, 0x4B, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
MidiPlayer::MidiPlayer(AGOSEngine *vm) {
// Since initialize() is called every time the music changes,
// this is where we'll initialize stuff that must persist
@ -905,6 +919,11 @@ void MidiPlayer::setLoop(bool loop) {
_parserMusic->property(MidiParser::mpAutoLoop, loop);
}
void MidiPlayer::setSimon2Remapping(bool remap) {
if (_driverMsMusic)
_driverMsMusic->setInstrumentRemapping(remap ? SIMON2_TRACK10_GM_MT32_INSTRUMENT_REMAPPING : nullptr);
}
void MidiPlayer::queueTrack(int track, bool loop) {
Common::StackLock lock(_mutex);

View File

@ -62,6 +62,10 @@ struct MusicInfo {
class MidiPlayer : public MidiDriver_BASE {
protected:
// Instrument map specifically for remapping the instruments of the GM
// version of Simon 2 track 10 subtracks 2 and 3 to MT-32.
static byte SIMON2_TRACK10_GM_MT32_INSTRUMENT_REMAPPING[];
AGOSEngine *_vm;
Common::Mutex _mutex;
@ -138,6 +142,9 @@ public:
bool usesMT32Data() const;
bool hasAdLibSfx() const;
void setLoop(bool loop);
// Activates or deactivates remapping GM to MT-32 instruments for
// Simon 2 track 10.
void setSimon2Remapping(bool remap);
void startTrack(int track);
void queueTrack(int track, bool loop);
bool isPlaying(bool checkQueued = false);

View File

@ -131,12 +131,18 @@ void AGOSEngine::skipSpeech() {
}
}
void AGOSEngine::loadMusic(uint16 music) {
void AGOSEngine::loadMusic(uint16 music, bool forceSimon2Gm) {
stopMusic();
_gameFile->seek(_gameOffsetsPtr[_musicIndexBase + music - 1], SEEK_SET);
uint16 indexBase = forceSimon2Gm ? MUSIC_INDEX_BASE_SIMON2_GM : _musicIndexBase;
_gameFile->seek(_gameOffsetsPtr[indexBase + music - 1], SEEK_SET);
_midi->loadMusic(_gameFile);
// Activate Simon 2 GM to MT-32 remapping if we force GM, otherwise
// deactivate it (in case it was previously activated).
_midi->setSimon2Remapping(forceSimon2Gm);
_lastMusicPlayed = music;
_nextMusicToPlay = -1;
}
@ -228,6 +234,23 @@ void AGOSEngine::playModule(uint16 music) {
_mixer->playStream(Audio::Mixer::kMusicSoundType, &_modHandle, audioStream);
}
void AGOSEngine_Simon2::playMusic(uint16 music, uint16 track) {
if (_lastMusicPlayed == 10 && getPlatform() == Common::kPlatformDOS && _midi->usesMT32Data()) {
// WORKAROUND Simon 2 track 10 (played during the first intro scene)
// consist of 3 subtracks. Subtracks 2 and 3 are missing from the MT-32
// MIDI data. The original interpreter just stops playing after track 1
// and does not restart until the next scene.
// We fix this by loading the GM version of track 10 and remapping the
// instruments to MT-32.
// Reload track 10 and force GM for all subtracks but the first (this
// also activates the instrument remapping).
loadMusic(10, track > 0);
}
_midi->play(track);
}
void AGOSEngine_Simon1::playMusic(uint16 music, uint16 track) {
stopMusic();

View File

@ -347,7 +347,7 @@ void AGOSEngine_Simon2::os2_playTune() {
if (_lastMusicPlayed != music)
_nextMusicToPlay = music;
else
_midi->play(track);
playMusic(0, track);
}
void AGOSEngine_Simon2::os2_screenTextPObj() {

View File

@ -146,7 +146,7 @@ void AGOSEngine::vc69_playSeq() {
// as a means of stopping what music is currently
// playing.
_midi->setLoop(loop != 0);
_midi->play(track);
playMusic(0, track);
}
void AGOSEngine::vc70_joinSeq() {
@ -195,7 +195,7 @@ void AGOSEngine::vc72_segue() {
stopMusic();
} else {
_midi->setLoop(loop != 0);
_midi->play(track);
playMusic(0, track);
}
}