[Scummvm-git-logs] scummvm master -> 0af83d3faabdc012031498d40df246068fd8de26
athrxx
athrxx at scummvm.org
Sat Jun 20 21:27:34 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:
0af83d3faa KYRA/MIDI: Fix minor MT-32/GM issues (#2333)
Commit: 0af83d3faabdc012031498d40df246068fd8de26
https://github.com/scummvm/scummvm/commit/0af83d3faabdc012031498d40df246068fd8de26
Author: NMIError (60350957+NMIError at users.noreply.github.com)
Date: 2020-06-20T23:27:30+02:00
Commit Message:
KYRA/MIDI: Fix minor MT-32/GM issues (#2333)
* KYRA: Added GM initialization for Lands of Lore
The original interpreter of Lands of Lore uses two executables, which
both do initialization of MIDI devices. The main executable runs a
sysex file for GM devices; the intro executable does not.
ScummVM only does MIDI initialization once and it performs this like
the intro executable. I've added the GM initialization of the main
executable. Note that the initialization file consists mostly of
sysexes which alter the display of the SC-55, so it's not particulary
useful. Not many games did this though, so I thought it would be a
fun feature to include.
I also noticed that the check which distinguishes between the two
demo versions of LoL did not work properly; the useAltShapeHeader
flag was true for both versions. I've changed it to check for the
existence of a PAK file which was only included with one of the two
demos.
* MIDI: Delay parser after handling SysEx events
This changes the way delays between SysEx events in MIDI data are handled from
delaying the backend to delaying the MidiParser.
SysEx events require a delay between events to ensure correct processing by the
MIDI device. This is usually implemented by calling OSystem::delayMillis.
Some games use some form of MIDI sequence filled with SysEx messages to
initialize the MIDI device. This is handled by the MidiParser. Using the
delayMillis method causes the following MIDI events to be "bunched up" and be
executed in a shorter timespan than intended.
I've altered this by making the MidiParser stop parsing when a SysEx event is
encountered and not enough time has passed since the last SysEx. After enough
time has passed, the next SysEx is sent and parsing resumes. To facilitate
this, I've introduced an alternate sysExNoDelay fuction on the MidiDiver. This
does not execute the delay itself, but instead returns the required delay time,
so the parser can handle this instead.
I've currently only implemented this method for the Miles MT-32/GM driver. For
other driver implementations, this will call the regular sysEx method and
return a 0 delay time, so there will be no change in behavior.
This restores a sound effect at the end of the Legend of Kyrandia MT-32 MIDI
initialization. Before, the Note On and Note Off events would be transmitted
instantly after each other, causing the notes not to play.
* MIDI: Add instrument bank fallback
Some games rely on a feature of the Roland SC-55 v1.xx that corrects invalid
instrument banks. This feature was removed in later versions of the device and
not implemented in most other GM devices, causing some MIDI data (sound effects
mostly) to play incorrectly.
Specifically, this applies to Lands of Lore. For example, in the intro, the
sound effect of the ring sparkling uses an incorrect instrument bank. Depending
on how the MIDI device handles this, the sound will play correctly, with the
wrong instrument, or not at all.
This commit emulates the SC-55 functionality that corrects the invalid bank
numbers. I've implemented this (partially) on the MidiDriver, so that it can be
re-used for other games (Xeen 4 and 5 also have this issue with some sound
effects).
* KYRA: Start MIDI playback after selecting track
The MIDI parser would automatically start playback after loading MIDI data, but
KYRA engine games use MIDI files with multiple tracks. Because of this it was
necessary to immediately stop playback after loading MIDI data, and then select
the track that should be played for correct playback. The parser now has a
feature to disable automatically starting playback, so I've implemented this
for KYRA.
* KYRA: Improve stopping MIDI playback
In two places All Notes Off events were sent on all channels to stop MIDI
playback of the background music. However, this will also cut off any MIDI
sound effects which are playing. Where needed I've replaced this with simply
stopping playback of the background music; the parser will turn off any active
notes.
I've also made sure playback is stopped before freeing the music data
memory to prevent any issues.
I've added sending All Notes Off events when the game quits, just in case any
hanging notes are not ended by the parsers (should not really be a problem
though).
* MIDI/KYRA: Add pausing playback to MIDI parser
* KYRA: Fix invalid track selection
If a game would attempt to play an invalid track, the parser would start
playing the previously selected track. Fixed this so the parser will not start
playing.
Changed paths:
audio/mididrv.cpp
audio/mididrv.h
audio/midiparser.cpp
audio/midiparser.h
audio/midiparser_xmidi.cpp
audio/miles.h
audio/miles_midi.cpp
engines/kyra/sound/sound_pc_midi.cpp
diff --git a/audio/mididrv.cpp b/audio/mididrv.cpp
index 39b876b9c2..f47c8d2fd9 100644
--- a/audio/mididrv.cpp
+++ b/audio/mididrv.cpp
@@ -631,6 +631,80 @@ void MidiDriver::sendGMReset() {
g_system->delayMillis(100);
}
+byte MidiDriver::correctInstrumentBank(byte outputChannel, byte patchId) {
+ if (_gsBank[outputChannel] == 0 || patchId >= 120 || _gsBank[outputChannel] >= 64)
+ // Usually, no bank select has been sent and no correction is
+ // necessary.
+ // No correction is performed on banks 64-127 or on the SFX
+ // instruments (120-127).
+ return 0xFF;
+
+ // Determine the intended bank. This emulates the behavior of the
+ // Roland SC-55 v1.2x. Instruments have 2, 1 or 0 sub-capital tones.
+ // Depending on the selected bank and the selected instrument, the
+ // bank will "fall back" to a sub-capital tone or to the capital
+ // tone (bank 0).
+ byte correctedBank = 0xFF;
+
+ switch (patchId) {
+ case 25: // Steel-String Guitar / 12-string Guitar / Mandolin
+ // This instrument has 2 sub-capital tones. Bank selects 17-63
+ // are corrected to the second sub-capital tone at 16.
+ if (_gsBank[outputChannel] >= 16) {
+ correctedBank = 16;
+ break;
+ }
+ // Corrections for values below 16 are handled below.
+
+ // fall through
+ case 4: // Electric Piano 1 / Detuned Electric Piano 1
+ case 5: // Electric Piano 2 / Detuned Electric Piano 2
+ case 6: // Harpsichord / Coupled Harpsichord
+ case 14: // Tubular-bell / Church Bell
+ case 16: // Organ 1 / Detuned Organ 1
+ case 17: // Organ 2 / Detuned Organ 2
+ case 19: // Church Organ 1 / Church Organ 2
+ case 21: // Accordion Fr / Accordion It
+ case 24: // Nylon-string Guitar / Ukelele
+ case 26: // Jazz Guitar / Hawaiian Guitar
+ case 27: // Clean Guitar / Chorus Guitar
+ case 28: // Muted Guitar / Funk Guitar
+ case 30: // Distortion Guitar / Feedback Guitar
+ case 31: // Guitar Harmonics / Guitar Feedback
+ case 38: // Synth Bass 1 / Synth Bass 3
+ case 39: // Synth Bass 2 / Synth Bass 4
+ case 48: // Strings / Orchestra
+ case 50: // Synth Strings 1 / Synth Strings 3
+ case 61: // Brass 1 / Brass 2
+ case 62: // Synth Brass 1 / Synth Brass 3
+ case 63: // Synth Brass 2 / Synth Brass 4
+ case 80: // Square Wave / Sine Wave
+ case 107: // Koto / Taisho Koto
+ case 115: // Woodblock / Castanets
+ case 116: // Taiko / Concert BD
+ case 117: // Melodic Tom 1 / Melodic Tom 2
+ case 118: // Synth Drum / 808 Tom
+ // These instruments have one sub-capital tone. Bank selects 9-63
+ // are corrected to the sub-capital tone at 8.
+ if (_gsBank[outputChannel] >= 8) {
+ correctedBank = 8;
+ break;
+ }
+ // Corrections for values below 8 are handled below.
+
+ // fall through
+ default:
+ // The other instruments only have a capital tone. Bank selects
+ // 1-63 are corrected to the capital tone.
+ if (_gsBank[outputChannel] >= 0)
+ correctedBank = 0;
+ break;
+ }
+
+ // Return the corrected bank, or 0xFF if no correction is needed.
+ return _gsBank[outputChannel] != correctedBank ? correctedBank : 0xFF;
+}
+
void MidiDriver_BASE::midiDumpInit() {
g_system->displayMessageOnOSD(_("Starting MIDI dump"));
_midiDumpCache.clear();
diff --git a/audio/mididrv.h b/audio/mididrv.h
index b8d78d6359..d974757436 100644
--- a/audio/mididrv.h
+++ b/audio/mididrv.h
@@ -125,7 +125,7 @@ public:
void send(int8 source, byte status, byte firstOp, byte secondOp);
/**
- * Transmit a sysEx to the midi device.
+ * Transmit a SysEx to the MIDI device.
*
* The given msg MUST NOT contain the usual SysEx frame, i.e.
* do NOT include the leading 0xF0 and the trailing 0xF7.
@@ -136,6 +136,20 @@ public:
*/
virtual void sysEx(const byte *msg, uint16 length) { }
+ /**
+ * Transmit a SysEx to the MIDI device and return the necessary
+ * delay until the next SysEx event in milliseconds.
+ *
+ * This can be used to implement an alternate delay method than the
+ * OSystem::delayMillis function used by most sysEx implementations.
+ * Note that not every driver needs a delay, or supports this method.
+ * In this case, 0 is returned and the driver itself will do a delay
+ * if necessary.
+ *
+ * For information on the SysEx data requirements, see the sysEx method.
+ */
+ virtual uint16 sysExNoDelay(const byte *msg, uint16 length) { sysEx(msg, length); return 0; }
+
// TODO: Document this.
virtual void metaEvent(byte type, byte *data, uint16 length) { }
@@ -233,6 +247,8 @@ protected:
bool _reversePanning;
// True if GS percussion channel volume should be scaled to match MT-32 volume.
bool _scaleGSPercussionVolumeToMT32;
+ // The currently selected GS instrument bank / variation for each channel.
+ byte _gsBank[16];
private:
// If detectDevice() detects MT32 and we have a preferred MT32 device
@@ -347,6 +363,18 @@ public:
// Does this driver accept soundFont data?
virtual bool acceptsSoundFontData() { return false; }
+
+protected:
+ /**
+ * Checks if the currently selected GS bank / instrument variation
+ * on the specified channel is valid for the specified patch.
+ * If this is not the case, the correct bank will be returned which
+ * can be set by sending a bank select message. If no correction is
+ * needed, 0xFF will be returned.
+ * This emulates the fallback functionality of the Roland SC-55 v1.2x,
+ * on which some games rely to correct wrong bank selects.
+ */
+ byte correctInstrumentBank(byte outputChannel, byte patchId);
};
class MidiChannel {
diff --git a/audio/midiparser.cpp b/audio/midiparser.cpp
index 89c564d279..15de9cab5d 100644
--- a/audio/midiparser.cpp
+++ b/audio/midiparser.cpp
@@ -38,6 +38,7 @@ _timerRate(0x4A0000),
_ppqn(96),
_tempo(500000),
_psecPerTick(5208), // 500000 / 96
+_sysExDelay(0),
_autoLoop(false),
_smartJump(false),
_centerPitchWheelOnUnload(false),
@@ -48,7 +49,8 @@ _numTracks(0),
_activeTrack(255),
_abortParse(false),
_jumpingToTick(false),
-_doParse(true) {
+_doParse(true),
+_pause(false) {
memset(_activeNotes, 0, sizeof(_activeNotes));
memset(_tracks, 0, sizeof(_tracks));
_nextEvent.start = NULL;
@@ -181,7 +183,11 @@ void MidiParser::onTimer() {
uint32 endTime;
uint32 eventTime;
- if (!_position._playPos || !_driver || !_doParse)
+ // The SysEx delay can be decreased whenever time passes,
+ // even if the parser does not parse events.
+ _sysExDelay -= (_sysExDelay > _timerRate) ? _timerRate : _sysExDelay;
+
+ if (!_position._playPos || !_driver || !_doParse || _pause)
return;
_abortParse = false;
@@ -252,10 +258,20 @@ bool MidiParser::processEvent(const EventInfo &info, bool fireEvents) {
// SysEx event
// Check for trailing 0xF7 -- if present, remove it.
if (fireEvents) {
+ if (_sysExDelay > 0)
+ // Don't process this event if the delay from
+ // the previous SysEx hasn't passed yet.
+ return false;
+
+ uint16 delay;
if (info.ext.data[info.length-1] == 0xF7)
- _driver->sysEx(info.ext.data, (uint16)info.length-1);
+ delay = _driver->sysExNoDelay(info.ext.data, (uint16)info.length-1);
else
- _driver->sysEx(info.ext.data, (uint16)info.length);
+ delay = _driver->sysExNoDelay(info.ext.data, (uint16)info.length);
+
+ // Set the delay in microseconds so the next
+ // SysEx event will be delayed if necessary.
+ _sysExDelay = delay * 1000;
}
} else if (info.event == 0xFF) {
// META event
@@ -352,6 +368,7 @@ bool MidiParser::setTrack(int track) {
allNotesOff();
resetTracking();
+ _pause = false;
memset(_activeNotes, 0, sizeof(_activeNotes));
if (_disableAutoStartPlayback)
_doParse = false;
@@ -364,10 +381,11 @@ bool MidiParser::setTrack(int track) {
void MidiParser::stopPlaying() {
allNotesOff();
resetTracking();
+ _pause = false;
}
bool MidiParser::startPlaying() {
- if (_activeTrack >= _numTracks)
+ if (_activeTrack >= _numTracks || _pause)
return false;
if (!_position._playPos) {
_position._playPos = _tracks[_activeTrack];
@@ -377,6 +395,17 @@ bool MidiParser::startPlaying() {
return true;
}
+void MidiParser::pausePlaying() {
+ if (isPlaying() && !_pause) {
+ _pause = true;
+ allNotesOff();
+ }
+}
+
+void MidiParser::resumePlaying() {
+ _pause = false;
+}
+
void MidiParser::hangAllActiveNotes() {
// Search for note off events until we have
// accounted for every active note.
@@ -414,7 +443,7 @@ void MidiParser::hangAllActiveNotes() {
}
bool MidiParser::jumpToTick(uint32 tick, bool fireEvents, bool stopNotes, bool dontSendNoteOn) {
- if (_activeTrack >= _numTracks)
+ if (_activeTrack >= _numTracks || _pause)
return false;
assert(!_jumpingToTick); // This function is not re-entrant
@@ -486,6 +515,7 @@ void MidiParser::unloadMusic() {
_numTracks = 0;
_activeTrack = 255;
_abortParse = true;
+ _pause = false;
if (_centerPitchWheelOnUnload) {
// Center the pitch wheels in preparation for the next piece of
diff --git a/audio/midiparser.h b/audio/midiparser.h
index 11f3a5efdb..94ef5ad366 100644
--- a/audio/midiparser.h
+++ b/audio/midiparser.h
@@ -276,6 +276,7 @@ protected:
uint32 _ppqn; ///< Pulses Per Quarter Note. (We refer to "pulses" as "ticks".)
uint32 _tempo; ///< Microseconds per quarter note.
uint32 _psecPerTick; ///< Microseconds per tick (_tempo / _ppqn).
+ uint32 _sysExDelay; ///< Number of microseconds until the next SysEx event can be sent.
bool _autoLoop; ///< For lightweight clients that don't provide their own flow control.
bool _smartJump; ///< Support smart expiration of hanging notes when jumping
bool _centerPitchWheelOnUnload; ///< Center the pitch wheels when unloading a song
@@ -293,6 +294,7 @@ protected:
bool _abortParse; ///< If a jump or other operation interrupts parsing, flag to abort.
bool _jumpingToTick; ///< True if currently inside jumpToTick
bool _doParse; ///< True if the parser should be parsing; false if it should be active
+ bool _pause; ///< True if the parser has paused parsing
protected:
static uint32 readVLQ(byte * &data);
@@ -419,6 +421,24 @@ public:
* Stops playback. This resets the current playback position.
*/
void stopPlaying();
+ /**
+ * Pauses playback and stops all active notes. Use resumePlaying to
+ * continue playback at the current track position; startPlaying will
+ * do nothing if the parser is paused.
+ * stopPlaying, unloadMusic, loadMusic and setTrack will unpause the
+ * parser. jumpToTick and jumpToIndex do nothing while the parser is
+ * paused.
+ * If the parser is not playing or already paused, this function does
+ * nothing. Note that isPlaying will continue to return true while
+ * playback is paused.
+ * Not every parser implementation might support pausing properly.
+ */
+ void pausePlaying();
+ /**
+ * Resumes playback at the current track position.
+ * If the parser is not paused, this function does nothing.
+ */
+ void resumePlaying();
bool setTrack(int track);
bool jumpToTick(uint32 tick, bool fireEvents = false, bool stopNotes = true, bool dontSendNoteOn = false);
diff --git a/audio/midiparser_xmidi.cpp b/audio/midiparser_xmidi.cpp
index 865582cc69..2be4472941 100644
--- a/audio/midiparser_xmidi.cpp
+++ b/audio/midiparser_xmidi.cpp
@@ -140,7 +140,7 @@ bool MidiParser_XMIDI::hasJumpIndex(uint8 index) {
}
bool MidiParser_XMIDI::jumpToIndex(uint8 index, bool stopNotes) {
- if (_activeTrack >= _numTracks)
+ if (_activeTrack >= _numTracks || _pause)
return false;
if (index >= MAXIMUM_TRACK_BRANCHES || _trackBranches[_activeTrack][index] == 0) {
diff --git a/audio/miles.h b/audio/miles.h
index dbc4b835df..25ad2ad9a8 100644
--- a/audio/miles.h
+++ b/audio/miles.h
@@ -39,6 +39,8 @@ namespace Audio {
#define MILES_CONTROLLER_PROTECT_TIMBRE 113
#define MILES_CONTROLLER_LOCK_CHANNEL 110
#define MILES_CONTROLLER_PROTECT_CHANNEL 111
+#define MILES_CONTROLLER_BANK_SELECT_MSB 0
+#define MILES_CONTROLLER_BANK_SELECT_LSB 32
#define MILES_CONTROLLER_MODULATION 1
#define MILES_CONTROLLER_VOLUME 7
#define MILES_CONTROLLER_EXPRESSION 11
@@ -125,6 +127,7 @@ public:
void send(uint32 b) override;
void send(int8 source, uint32 b) override;
void sysEx(const byte *msg, uint16 length) override;
+ uint16 sysExNoDelay(const byte *msg, uint16 length) override;
void metaEvent(int8 source, byte type, byte *data, uint16 length) override;
/**
@@ -326,7 +329,7 @@ private:
* @param controlData The new program value will be set on this MidiChannelControlData
* @param sendMessage True if the message should be sent out to the device
*/
- void programChange(byte outputChannel, byte patchId, MidiChannelControlData &controlData, bool sendMessage);
+ void programChange(byte outputChannel, byte patchId, uint8 source, MidiChannelControlData &controlData, bool sendMessage);
void stopNotesOnChannel(uint8 outputChannelNumber);
diff --git a/audio/miles_midi.cpp b/audio/miles_midi.cpp
index ad31098ae7..6b6cb79af3 100644
--- a/audio/miles_midi.cpp
+++ b/audio/miles_midi.cpp
@@ -75,6 +75,7 @@ MidiDriver_Miles_Midi::MidiDriver_Miles_Midi(MusicType midiType, MilesMT32Instru
break;
}
+ memset(_gsBank, 0, sizeof(_gsBank));
memset(_patchesBank, 0, sizeof(_patchesBank));
_instrumentTablePtr = instrumentTablePtr;
@@ -208,21 +209,28 @@ void MidiDriver_Miles_Midi::initMidiDevice() {
}
void MidiDriver_Miles_Midi::sysEx(const byte *msg, uint16 length) {
+ uint16 delay = sysExNoDelay(msg, length);
+
+ if (delay > 0)
+ g_system->delayMillis(delay);
+}
+
+uint16 MidiDriver_Miles_Midi::sysExNoDelay(const byte *msg, uint16 length) {
if (!_nativeMT32 && length >= 3 && msg[0] == 0x41 && msg[2] == 0x16)
// MT-32 SysExes have no effect on GM devices.
- return;
+ return 0;
// Send SysEx
_driver->sysEx(msg, length);
// Wait the time it takes to send the SysEx data
- uint32 delay = (length + 2) * 1000 / 3125;
+ uint16 delay = (length + 2) * 1000 / 3125;
// Plus an additional delay for the MT-32 rev00
if (_nativeMT32)
delay += 40;
- g_system->delayMillis(delay);
+ return delay;
}
void MidiDriver_Miles_Midi::MT32SysEx(const uint32 targetAddress, const byte *dataPtr) {
@@ -376,7 +384,7 @@ void MidiDriver_Miles_Midi::send(int8 source, uint32 b) {
controlChange(outputChannel, op1, op2, source, controlData, sendMessage);
break;
case 0xc0: // Program Change
- programChange(outputChannel, op1, controlData, sendMessage);
+ programChange(outputChannel, op1, source, controlData, sendMessage);
break;
case 0xf0: // SysEx
warning("MILES-MIDI: SysEx: %x", b);
@@ -462,7 +470,10 @@ void MidiDriver_Miles_Midi::controlChange(byte outputChannel, byte controllerNum
}
// XMIDI MT-32 SysEx controllers
- if (_nativeMT32 && (controllerNumber >= MILES_CONTROLLER_SYSEX_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_SYSEX_RANGE_END)) {
+ if (_midiType == MT_MT32 && (controllerNumber >= MILES_CONTROLLER_SYSEX_RANGE_BEGIN) && (controllerNumber <= MILES_CONTROLLER_SYSEX_RANGE_END)) {
+ if (!_nativeMT32)
+ return;
+
// send SysEx
byte sysExQueueNr = 0;
@@ -533,6 +544,10 @@ void MidiDriver_Miles_Midi::controlChange(byte outputChannel, byte controllerNum
// Standard MIDI controllers
switch (controllerNumber) {
+ case MILES_CONTROLLER_BANK_SELECT_MSB:
+ // Keep track of the current bank for each channel
+ _gsBank[outputChannel] = controllerValue;
+ break;
case MILES_CONTROLLER_MODULATION:
controlData.modulation = controllerValue;
break;
@@ -715,7 +730,7 @@ void MidiDriver_Miles_Midi::unlockChannel(uint8 outputChannel) {
if (channel.currentData.currentPatchBank != channel.unlockData.currentPatchBank)
controlChange(outputChannel, MILES_CONTROLLER_SELECT_PATCH_BANK, channel.unlockData.currentPatchBank, channel.currentData.source, channel.currentData, true);
if (channel.unlockData.program != 0xFF && (channel.currentData.program != channel.unlockData.program || channel.currentData.currentPatchBank != channel.unlockData.currentPatchBank))
- programChange(outputChannel, channel.unlockData.program, channel.currentData, true);
+ programChange(outputChannel, channel.unlockData.program, channel.currentData.source, channel.currentData, true);
if (channel.currentData.pitchWheel != channel.unlockData.pitchWheel)
send(channel.currentData.source, 0xE0 | outputChannel, channel.unlockData.pitchWheel & 0x7F, (channel.unlockData.pitchWheel >> 7) & 0x7F);
}
@@ -745,7 +760,7 @@ void MidiDriver_Miles_Midi::allNotesOff() {
}
}
-void MidiDriver_Miles_Midi::programChange(byte outputChannel, byte patchId, MidiChannelControlData &controlData, bool sendMessage) {
+void MidiDriver_Miles_Midi::programChange(byte outputChannel, byte patchId, uint8 source, MidiChannelControlData &controlData, bool sendMessage) {
// remember patch id for the current MIDI-channel
controlData.program = patchId;
@@ -787,8 +802,15 @@ void MidiDriver_Miles_Midi::programChange(byte outputChannel, byte patchId, Midi
if (outputChannel == MILES_RHYTHM_CHANNEL) {
// Correct possible wrong GS drumkit number
patchId = _gsDrumkitFallbackMap[patchId];
- }
- else if (_nativeMT32) {
+ } else if (!_nativeMT32) {
+ // Correct possible wrong bank / instrument variation
+ byte correctedBank = correctInstrumentBank(outputChannel, patchId);
+ if (correctedBank != 0xFF) {
+ // Send out a bank select for the corrected bank number
+ controlChange(outputChannel, MILES_CONTROLLER_BANK_SELECT_MSB, correctedBank, source, controlData, sendMessage);
+ controlChange(outputChannel, MILES_CONTROLLER_BANK_SELECT_LSB, 0, source, controlData, sendMessage);
+ }
+ } else {
// GM on an MT-32: map the patch to the MT-32 equivalent
patchId = _gmToMt32[patchId];
}
diff --git a/engines/kyra/sound/sound_pc_midi.cpp b/engines/kyra/sound/sound_pc_midi.cpp
index 3f7182a631..c6d4f2cc90 100644
--- a/engines/kyra/sound/sound_pc_midi.cpp
+++ b/engines/kyra/sound/sound_pc_midi.cpp
@@ -43,10 +43,12 @@ SoundMidiPC::SoundMidiPC(KyraEngine_v1 *vm, Audio::Mixer *mixer, MidiDriver *dri
_music = MidiParser::createParser_XMIDI(MidiParser::defaultXMidiCallback, NULL, NULL, NULL, 0);
assert(_music);
_music->property(MidiParser::mpDisableAllNotesOffMidiEvents, true);
+ _music->property(MidiParser::mpDisableAutoStartPlayback, true);
for (int i = 0; i < 3; ++i) {
_sfx[i] = MidiParser::createParser_XMIDI(MidiParser::defaultXMidiCallback, NULL, NULL, NULL, i + 1);
assert(_sfx[i]);
_sfx[i]->property(MidiParser::mpDisableAllNotesOffMidiEvents, true);
+ _sfx[i]->property(MidiParser::mpDisableAutoStartPlayback, true);
}
_musicVolume = _sfxVolume = 0;
@@ -87,6 +89,7 @@ SoundMidiPC::~SoundMidiPC() {
delete _music;
for (int i = 0; i < 3; ++i)
delete _sfx[i];
+ _output->allNotesOff();
delete _output; // This automatically frees _driver (!)
@@ -121,9 +124,10 @@ bool SoundMidiPC::init() {
_output->setTimerCallback(this, SoundMidiPC::onTimer);
+ // Load MT-32 and GM initialization files
+ const char* midiFile = 0;
+ const char* pakFile = 0;
if (_nativeMT32 && _type == kMidiMT32) {
- const char *midiFile = 0;
- const char *pakFile = 0;
if (_vm->game() == GI_KYRA1) {
midiFile = "INTRO";
} else if (_vm->game() == GI_KYRA2) {
@@ -133,7 +137,7 @@ bool SoundMidiPC::init() {
midiFile = "LOREINTR";
if (_vm->gameFlags().isDemo) {
- if (_vm->gameFlags().useAltShapeHeader) {
+ if (_vm->resource()->exists("INTROVOC.PAK")) {
// Intro demo
pakFile = "INTROVOC.PAK";
} else {
@@ -148,27 +152,36 @@ bool SoundMidiPC::init() {
pakFile = "INTROVOC.PAK";
}
}
+ } else if (_type == kMidiGM && _vm->game() == GI_LOL) {
+ if (_vm->gameFlags().isDemo && _vm->resource()->exists("INTROVOC.PAK")) {
+ // Intro demo
+ midiFile = "LOREINTR";
+ pakFile = "INTROVOC.PAK";
+ } else {
+ midiFile = "LOLSYSEX";
+ pakFile = "GENERAL.PAK";
+ }
+ }
- if (!midiFile)
- return true;
-
- if (pakFile)
- _vm->resource()->loadPakFile(pakFile);
+ if (!midiFile)
+ return true;
- loadSoundFile(midiFile);
- playTrack(0);
+ if (pakFile)
+ _vm->resource()->loadPakFile(pakFile);
- Common::Event event;
- while (isPlaying() && !_vm->shouldQuit()) {
- _vm->_system->updateScreen();
- _vm->_eventMan->pollEvent(event);
- _vm->_system->delayMillis(10);
- }
+ loadSoundFile(midiFile);
+ playTrack(0);
- if (pakFile)
- _vm->resource()->unloadPakFile(pakFile);
+ Common::Event event;
+ while (isPlaying() && !_vm->shouldQuit()) {
+ _vm->_system->updateScreen();
+ _vm->_eventMan->pollEvent(event);
+ _vm->_system->delayMillis(10);
}
+ if (pakFile)
+ _vm->resource()->unloadPakFile(pakFile);
+
return true;
}
@@ -227,10 +240,10 @@ void SoundMidiPC::loadSoundFile(Common::String file) {
if (!_vm->resource()->exists(file.c_str()))
return;
- // When loading a new file we stop all notes
- // still running on our own, just to prevent
- // glitches
- _output->allNotesOff();
+ haltTrack();
+ if (_vm->game() == GI_KYRA1) {
+ stopAllSoundEffects();
+ }
delete[] _musicFile;
uint32 fileSize = 0;
@@ -238,14 +251,12 @@ void SoundMidiPC::loadSoundFile(Common::String file) {
_mFileName = file;
_music->loadMusic(_musicFile, fileSize);
- _music->stopPlaying();
// Since KYRA1 uses the same file for SFX and Music
// we setup sfx to play from music file as well
if (_vm->game() == GI_KYRA1) {
for (int i = 0; i < 3; ++i) {
_sfx[i]->loadMusic(_musicFile, fileSize);
- _sfx[i]->stopPlaying();
}
}
}
@@ -265,6 +276,8 @@ void SoundMidiPC::loadSfxFile(Common::String file) {
if (!_vm->resource()->exists(file.c_str()))
return;
+ stopAllSoundEffects();
+
delete[] _sfxFile;
uint32 fileSize = 0;
@@ -283,17 +296,13 @@ void SoundMidiPC::playTrack(uint8 track) {
haltTrack();
- // The following two lines are meant as a fix for bug #6314.
- // It is on purpose that they are outside the mutex lock.
- _output->allNotesOff();
- //_vm->delay(250);
-
Common::StackLock lock(_mutex);
_fadeMusicOut = false;
_output->setSourceVolume(0, _musicVolume);
- _music->setTrack(track);
+ if (_music->setTrack(track))
+ _music->startPlaying();
}
void SoundMidiPC::haltTrack() {
@@ -316,7 +325,8 @@ void SoundMidiPC::playSoundEffect(uint8 track, uint8) {
Common::StackLock lock(_mutex);
for (int i = 0; i < 3; ++i) {
if (!_sfx[i]->isPlaying()) {
- _sfx[i]->setTrack(track);
+ if (_sfx[i]->setTrack(track))
+ _sfx[i]->startPlaying();
return;
}
}
@@ -342,15 +352,15 @@ void SoundMidiPC::pause(bool paused) {
Common::StackLock lock(_mutex);
if (paused) {
- _music->setMidiDriver(0);
+ _music->pausePlaying();
for (int i = 0; i < 3; i++)
- _sfx[i]->setMidiDriver(0);
+ _sfx[i]->pausePlaying();
if (_output)
_output->allNotesOff();
} else {
- _music->setMidiDriver(_output);
+ _music->resumePlaying();
for (int i = 0; i < 3; ++i)
- _sfx[i]->setMidiDriver(_output);
+ _sfx[i]->resumePlaying();
// Possible TODO (IMHO unnecessary): restore notes and/or update _fadeStartTime
}
}
@@ -367,14 +377,8 @@ void SoundMidiPC::onTimer(void *data) {
int volume = (byte)((musicFadeTime - (midi->_vm->_system->getMillis() - midi->_fadeStartTime)) * midi->_musicVolume / musicFadeTime);
midi->_output->setSourceVolume(0, volume);
} else {
- midi->_music->stopPlaying();
-
- for (int i = 0; i < 3; ++i) {
- midi->_sfx[i]->stopPlaying();
- }
-
- for (int i = 0; i < 4; ++i)
- midi->_output->deinitSource(i);
+ midi->haltTrack();
+ midi->stopAllSoundEffects();
midi->_fadeMusicOut = false;
More information about the Scummvm-git-logs
mailing list