aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm
diff options
context:
space:
mode:
authorJamieson Christian2006-03-12 18:06:22 +0000
committerJamieson Christian2006-03-12 18:06:22 +0000
commit4d5a3e4c45b19a96e55ff8a3c67ff7ab9734fd3b (patch)
tree23d4c7e78d45aaea3d95b2e59c83bf067904cc69 /engines/scumm
parent20095d8e93085419385efbe82d6607201d1d38c6 (diff)
downloadscummvm-rg350-4d5a3e4c45b19a96e55ff8a3c67ff7ab9734fd3b.tar.gz
scummvm-rg350-4d5a3e4c45b19a96e55ff8a3c67ff7ab9734fd3b.tar.bz2
scummvm-rg350-4d5a3e4c45b19a96e55ff8a3c67ff7ab9734fd3b.zip
IMuse SysEx processing now handled by client-specified callbacks. This removes all game-specific references from the Player class. Bloodshed Dev-C++ project file updated.
svn-id: r21241
Diffstat (limited to 'engines/scumm')
-rw-r--r--engines/scumm/imuse/imuse.cpp16
-rw-r--r--engines/scumm/imuse/imuse.h4
-rw-r--r--engines/scumm/imuse/imuse_internal.h27
-rw-r--r--engines/scumm/imuse/imuse_player.cpp181
-rw-r--r--engines/scumm/imuse/sysex.h39
-rw-r--r--engines/scumm/imuse/sysex_samnmax.cpp78
-rw-r--r--engines/scumm/imuse/sysex_scumm.cpp200
-rw-r--r--engines/scumm/scumm.cpp4
8 files changed, 369 insertions, 180 deletions
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index fd96a77221..b39e7da177 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -46,9 +46,10 @@ IMuseInternal::IMuseInternal() :
_native_mt32(false),
_enable_gs(false),
_sc55(false),
-_midi_adlib(0),
-_midi_native(0),
-_base_sounds(0),
+_midi_adlib(NULL),
+_midi_native(NULL),
+_base_sounds(NULL),
+_sysex(NULL),
_paused(false),
_initialized(false),
_tempoFactor(0),
@@ -451,6 +452,15 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
return 0;
}
+void IMuseInternal::addSysexHandler (byte mfgID, sysexfunc handler) {
+ // TODO: Eventually support multiple sysEx handlers and pay
+ // attention to the client-supplied manufacturer ID.
+ Common::StackLock lock(_mutex, "IMuseInternal::property()");
+ _sysex = handler;
+}
+
+
+
////////////////////////////////////////
//
// MusicEngine interface methods
diff --git a/engines/scumm/imuse/imuse.h b/engines/scumm/imuse/imuse.h
index 4f25d4b897..a82d82cca7 100644
--- a/engines/scumm/imuse/imuse.h
+++ b/engines/scumm/imuse/imuse.h
@@ -34,9 +34,12 @@ class OSystem;
namespace Scumm {
class IMuseInternal;
+class Player;
class ScummEngine;
class Serializer;
+typedef void (*sysexfunc) (Player *, const byte *, uint16);
+
/**
* iMuse implementation interface.
* MusicEngine derivative for state-tracked, interactive,
@@ -66,6 +69,7 @@ public:
virtual int clear_queue() = 0;
virtual void setBase(byte **base) = 0;
virtual uint32 property(int prop, uint32 value) = 0;
+ virtual void addSysexHandler (byte mfgID, sysexfunc handler) = 0;
public:
// MusicEngine base class methods.
diff --git a/engines/scumm/imuse/imuse_internal.h b/engines/scumm/imuse/imuse_internal.h
index e82da1ad5b..e19e72cd23 100644
--- a/engines/scumm/imuse/imuse_internal.h
+++ b/engines/scumm/imuse/imuse_internal.h
@@ -154,6 +154,16 @@ struct CommandQueue {
//////////////////////////////////////////////////
class Player : public MidiDriver {
+/*
+ * External SysEx handler functions shall each be defined in
+ * a separate file. This header file shall be included at the
+ * top of the file immediately following this special #define:
+ * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
+ */
+#ifdef SYSEX_CALLBACK_FUNCTION
+ friend void SYSEX_CALLBACK_FUNCTION (Player *, const byte *, uint16);
+#endif
+
protected:
// Moved from IMuseInternal.
// This is only used by one player at a time.
@@ -367,6 +377,16 @@ class IMuseInternal : public IMuse {
friend class Player;
friend struct Part;
+/*
+ * External SysEx handler functions shall each be defined in
+ * a separate file. This header file shall be included at the
+ * top of the file immediately following this special #define:
+ * #define SYSEX_CALLBACK_FUNCTION nameOfHandlerFunction
+ */
+#ifdef SYSEX_CALLBACK_FUNCTION
+ friend void SYSEX_CALLBACK_FUNCTION (Player *, const byte *, uint16);
+#endif
+
protected:
bool _native_mt32;
bool _enable_gs;
@@ -379,6 +399,12 @@ protected:
uint32 _game_id;
byte **_base_sounds;
+ // Plug-in SysEx handling. Right now this only supports one
+ // custom SysEx handler for the hardcoded IMUSE_SYSEX_ID
+ // manufacturer code. TODO: Expand this to support multiple
+ // SysEx handlers for client-specified manufacturer codes.
+ sysexfunc _sysex;
+
OSystem *_system;
Common::Mutex _mutex;
@@ -488,6 +514,7 @@ public:
int32 doCommand (int numargs, int args[]);
void setBase(byte **base);
uint32 property(int prop, uint32 value);
+ virtual void addSysexHandler (byte mfgID, sysexfunc handler);
public:
// MusicEngine interface
diff --git a/engines/scumm/imuse/imuse_player.cpp b/engines/scumm/imuse/imuse_player.cpp
index fddba2fcc9..ff12e4d94a 100644
--- a/engines/scumm/imuse/imuse_player.cpp
+++ b/engines/scumm/imuse/imuse_player.cpp
@@ -58,10 +58,10 @@ uint16 Player::_active_notes[128];
//////////////////////////////////////////////////
Player::Player() :
- _midi(0),
- _parser(0),
+ _midi(NULL),
+ _parser(NULL),
_passThrough(0),
- _parts(0),
+ _parts(NULL),
_active(false),
_scanning(false),
_id(0),
@@ -393,180 +393,7 @@ void Player::sysEx(const byte *p, uint16 len) {
debugC(DEBUG_IMUSE, "[%02d] SysEx:%s", _id, buf);
}
- switch (code = *p++) {
- case 0:
- if (_se->_game_id != GID_SAMNMAX) {
- // There are 17 bytes of useful information beyond
- // what we've read so far. All we know about them is
- // as follows:
- // BYTE 00: Channel #
- // BYTE 02: BIT 01(0x01): Part on?(1 = yes)
- // BIT 02(0x02): Reverb? (1 = yes) [bug #1088045]
- // BYTE 04: Priority adjustment [guessing]
- // BYTE 05: Volume(upper 4 bits) [guessing]
- // BYTE 06: Volume(lower 4 bits) [guessing]
- // BYTE 07: Pan(upper 4 bits) [bug #1088045]
- // BYTE 08: Pan(lower 4 bits) [bug #1088045]
- // BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
- // BYTE 13: Pitchbend range(upper 4 bits) [bug #1088045]
- // BYTE 14: Pitchbend range(lower 4 bits) [bug #1088045]
- // BYTE 15: Program(upper 4 bits)
- // BYTE 16: Program(lower 4 bits)
- part = getPart(p[0] & 0x0F);
- if (part) {
- part->set_onoff(p[2] & 0x01);
- part->effectLevel ((p[2] & 0x02) ? 127 : 0);
- part->set_pri(p[4]);
- part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
- part->set_pan((p[7] & 0x0F) << 4 | (p[8] & 0x0F));
- part->_percussion = _isMIDI ? ((p[9] & 0x08) > 0) : false;
- part->pitchBendFactor ((p[13] & 0x0F) << 4 | (p[14] & 0x0F));
- if (part->_percussion) {
- if (part->_mc) {
- part->off();
- _se->reallocateMidiChannels(_midi);
- }
- } else {
- // Even in cases where a program does not seem to be specified,
- // i.e. bytes 15 and 16 are 0, we send a program change because
- // 0 is a valid program number. MI2 tests show that in such
- // cases, a regular program change message always seems to follow
- // anyway.
- if (_isMIDI)
- part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), _isMT32);
- part->sendAll();
- }
- }
- } else {
- // Sam & Max: Trigger Event
- // Triggers are set by doCommand(ImSetTrigger).
- // When a SysEx marker is encountered whose sound
- // ID and marker ID match what was set by ImSetTrigger,
- // something magical is supposed to happen....
- for (a = 0; a < ARRAYSIZE(_se->_snm_triggers); ++a) {
- if (_se->_snm_triggers[a].sound == _id &&
- _se->_snm_triggers[a].id == *p)
- {
- _se->_snm_triggers[a].sound = _se->_snm_triggers[a].id = 0;
- _se->doCommand(8, _se->_snm_triggers[a].command);
- break;
- }
- }
- } // end if
- break;
-
- case 1:
- if (_se->_game_id != GID_SAMNMAX) {
- // Shut down a part. [Bug 1088045, comments]
- part = getPart (p[0]);
- if (part != NULL) part->uninit();
- } else {
- // Sam & Max: maybe_jump.
- if (_scanning)
- break;
- maybe_jump(p[0], p[1] - 1, (READ_BE_UINT16(p + 2) - 1) * 4 + p[4], ((p[5] * TICKS_PER_BEAT) >> 2) + p[6]);
- }
- break;
-
- case 2: // Start of song. Ignore for now.
- break;
-
- case 16: // Adlib instrument definition(Part)
- a = *p++ & 0x0F;
- ++p; // Skip hardware type
- part = getPart(a);
- if (part) {
- if (len == 63) {
- decode_sysex_bytes(p, buf, len - 3);
- part->set_instrument((byte *)buf);
- } else {
- // SPK tracks have len == 49 here, and are not supported
- part->programChange(254); // Must be invalid, but not 255 (which is reserved)
- }
- }
- break;
-
- case 17: // Adlib instrument definition(Global)
- p += 2; // Skip hardware type and... whatever came right before it
- a = *p++;
- decode_sysex_bytes(p, buf, len - 4);
- _se->setGlobalAdlibInstrument(a, buf);
- break;
-
- case 33: // Parameter adjust
- a = *p++ & 0x0F;
- ++p; // Skip hardware type
- decode_sysex_bytes(p, buf, len - 3);
- part = getPart(a);
- if (part)
- part->set_param(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2));
- break;
-
- case 48: // Hook - jump
- if (_scanning)
- break;
- decode_sysex_bytes(p + 1, buf, len - 2);
- maybe_jump(buf[0], READ_BE_UINT16(buf + 1), READ_BE_UINT16(buf + 3), READ_BE_UINT16(buf + 5));
- break;
-
- case 49: // Hook - global transpose
- decode_sysex_bytes(p + 1, buf, len - 2);
- maybe_set_transpose(buf);
- break;
-
- case 50: // Hook - part on/off
- buf[0] = *p++ & 0x0F;
- decode_sysex_bytes(p, buf + 1, len - 2);
- maybe_part_onoff(buf);
- break;
-
- case 51: // Hook - set volume
- buf[0] = *p++ & 0x0F;
- decode_sysex_bytes(p, buf + 1, len - 2);
- maybe_set_volume(buf);
- break;
-
- case 52: // Hook - set program
- buf[0] = *p++ & 0x0F;
- decode_sysex_bytes(p, buf + 1, len - 2);
- maybe_set_program(buf);
- break;
-
- case 53: // Hook - set transpose
- buf[0] = *p++ & 0x0F;
- decode_sysex_bytes(p, buf + 1, len - 2);
- maybe_set_transpose_part(buf);
- break;
-
- case 64: // Marker
- p++;
- len -= 2;
- while (len--) {
- _se->handle_marker(_id, *p++);
- }
- break;
-
- case 80: // Loop
- decode_sysex_bytes(p + 1, buf, len - 2);
- setLoop(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2),
- READ_BE_UINT16(buf + 4), READ_BE_UINT16(buf + 6),
- READ_BE_UINT16(buf + 8));
- break;
-
- case 81: // End loop
- clearLoop();
- break;
-
- case 96: // Set instrument
- part = getPart(p[0] & 0x0F);
- b = (p[1] & 0x0F) << 12 |(p[2] & 0x0F) << 8 |(p[4] & 0x0F) << 4 |(p[4] & 0x0F);
- if (part)
- part->set_instrument(b);
- break;
-
- default:
- error("Unknown SysEx command %d", (int)code);
- }
+ if (_se->_sysex) (*_se->_sysex) (this, p, len);
}
void Player::decode_sysex_bytes(const byte *src, byte *dst, int len) {
diff --git a/engines/scumm/imuse/sysex.h b/engines/scumm/imuse/sysex.h
new file mode 100644
index 0000000000..7e33295b62
--- /dev/null
+++ b/engines/scumm/imuse/sysex.h
@@ -0,0 +1,39 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#ifndef DEFINED_SYSEX_H
+#define DEFINED_SYSEX_H
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+namespace Scumm {
+
+class Player;
+
+extern void sysexHandler_Scumm (Player *, const byte *, uint16);
+extern void sysexHandler_SamNMax (Player *, const byte *, uint16);
+
+} // End of namespace Scumm
+
+#endif
+
diff --git a/engines/scumm/imuse/sysex_samnmax.cpp b/engines/scumm/imuse/sysex_samnmax.cpp
new file mode 100644
index 0000000000..837fc65afa
--- /dev/null
+++ b/engines/scumm/imuse/sysex_samnmax.cpp
@@ -0,0 +1,78 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+/*
+ * SysEx command handlers must have full access to the
+ * internal IMuse implementation classes. Before including
+ * the relevant header file, two things must happen:
+ * 1. A function declaration must be made.
+ * 2. The following #define must be established:
+ * #define SYSEX_CALLBACK_FUNCTION functionName
+ */
+#define SYSEX_CALLBACK_FUNCTION sysexHandler_SamNMax
+#include "scumm/imuse/imuse_internal.h"
+
+namespace Scumm {
+
+extern void sysexHandler_Scumm (Player *, const byte *, uint16);
+
+void sysexHandler_SamNMax (Player *player, const byte *msg, uint16 len) {
+ Part *part;
+ byte a;
+
+ IMuseInternal *se = player->_se;
+ const byte *p = msg;
+
+ switch (*p++) {
+ case 0:
+ // Trigger Event
+ // Triggers are set by doCommand(ImSetTrigger).
+ // When a SysEx marker is encountered whose sound
+ // ID and marker ID match what was set by ImSetTrigger,
+ // something magical is supposed to happen....
+ for (a = 0; a < ARRAYSIZE(se->_snm_triggers); ++a) {
+ if (se->_snm_triggers[a].sound == player->_id &&
+ se->_snm_triggers[a].id == *p)
+ {
+ se->_snm_triggers[a].sound = se->_snm_triggers[a].id = 0;
+ se->doCommand(8, se->_snm_triggers[a].command);
+ break;
+ }
+ }
+ break;
+
+ case 1:
+ // maybe_jump.
+ if (player->_scanning)
+ break;
+ player->maybe_jump(p[0], p[1] - 1, (READ_BE_UINT16(p + 2) - 1) * 4 + p[4], ((p[5] * TICKS_PER_BEAT) >> 2) + p[6]);
+ break;
+
+ default:
+ sysexHandler_Scumm (player, msg, len);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/imuse/sysex_scumm.cpp b/engines/scumm/imuse/sysex_scumm.cpp
new file mode 100644
index 0000000000..2b11878522
--- /dev/null
+++ b/engines/scumm/imuse/sysex_scumm.cpp
@@ -0,0 +1,200 @@
+/* ScummVM - Scumm Interpreter
+ * Copyright (C) 2001 Ludvig Strigeus
+ * Copyright (C) 2001-2006 The ScummVM project
+ *
+ * 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.
+ *
+ * $URL$
+ * $Id$
+ */
+
+#include "common/stdafx.h"
+#include "common/util.h"
+
+/*
+ * SysEx command handlers must have full access to the
+ * internal IMuse implementation classes. Before including
+ * the relevant header file, two things must happen:
+ * 1. A function declaration must be made.
+ * 2. The following #define must be established:
+ * #define SYSEX_CALLBACK_FUNCTION functionName
+ */
+#define SYSEX_CALLBACK_FUNCTION sysexHandler_Scumm
+#include "scumm/imuse/imuse_internal.h"
+
+namespace Scumm {
+
+void sysexHandler_Scumm (Player *player, const byte *msg, uint16 len) {
+ Part *part;
+ byte a;
+ byte buf[128];
+
+ IMuseInternal *se = player->_se;
+ const byte *p = msg;
+
+ switch (byte code = *p++) {
+ case 0:
+ // Allocate new part.
+ // There are 17 bytes of useful information here.
+ // Here is what we know about them so far:
+ // BYTE 00: Channel #
+ // BYTE 02: BIT 01(0x01): Part on?(1 = yes)
+ // BIT 02(0x02): Reverb? (1 = yes) [bug #1088045]
+ // BYTE 04: Priority adjustment [guessing]
+ // BYTE 05: Volume(upper 4 bits) [guessing]
+ // BYTE 06: Volume(lower 4 bits) [guessing]
+ // BYTE 07: Pan(upper 4 bits) [bug #1088045]
+ // BYTE 08: Pan(lower 4 bits) [bug #1088045]
+ // BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
+ // BYTE 13: Pitchbend range(upper 4 bits) [bug #1088045]
+ // BYTE 14: Pitchbend range(lower 4 bits) [bug #1088045]
+ // BYTE 15: Program(upper 4 bits)
+ // BYTE 16: Program(lower 4 bits)
+ part = player->getPart(p[0] & 0x0F);
+ if (part) {
+ part->set_onoff(p[2] & 0x01);
+ part->effectLevel ((p[2] & 0x02) ? 127 : 0);
+ part->set_pri(p[4]);
+ part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
+ part->set_pan((p[7] & 0x0F) << 4 | (p[8] & 0x0F));
+ part->_percussion = player->_isMIDI ? ((p[9] & 0x08) > 0) : false;
+ part->pitchBendFactor ((p[13] & 0x0F) << 4 | (p[14] & 0x0F));
+ if (part->_percussion) {
+ if (part->_mc) {
+ part->off();
+ se->reallocateMidiChannels(player->_midi);
+ }
+ } else {
+ // Even in cases where a program does not seem to be specified,
+ // i.e. bytes 15 and 16 are 0, we send a program change because
+ // 0 is a valid program number. MI2 tests show that in such
+ // cases, a regular program change message always seems to follow
+ // anyway.
+ if (player->_isMIDI)
+ part->_instrument.program((p[15] & 0x0F) << 4 |(p[16] & 0x0F), player->_isMT32);
+ part->sendAll();
+ }
+ }
+ break;
+
+ case 1:
+ // Shut down a part. [Bug 1088045, comments]
+ part = player->getPart (p[0]);
+ if (part != NULL) part->uninit();
+ break;
+
+ case 2: // Start of song. Ignore for now.
+ break;
+
+ case 16: // Adlib instrument definition(Part)
+ a = *p++ & 0x0F;
+ ++p; // Skip hardware type
+ part = player->getPart(a);
+ if (part) {
+ if (len == 63) {
+ player->decode_sysex_bytes(p, buf, len - 3);
+ part->set_instrument((byte *)buf);
+ } else {
+ // SPK tracks have len == 49 here, and are not supported
+ part->programChange(254); // Must be invalid, but not 255 (which is reserved)
+ }
+ }
+ break;
+
+ case 17: // Adlib instrument definition(Global)
+ p += 2; // Skip hardware type and... whatever came right before it
+ a = *p++;
+ player->decode_sysex_bytes(p, buf, len - 4);
+ se->setGlobalAdlibInstrument(a, buf);
+ break;
+
+ case 33: // Parameter adjust
+ a = *p++ & 0x0F;
+ ++p; // Skip hardware type
+ player->decode_sysex_bytes(p, buf, len - 3);
+ part = player->getPart(a);
+ if (part)
+ part->set_param(READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2));
+ break;
+
+ case 48: // Hook - jump
+ if (player->_scanning)
+ break;
+ player->decode_sysex_bytes(p + 1, buf, len - 2);
+ player->maybe_jump(buf[0], READ_BE_UINT16(buf + 1), READ_BE_UINT16(buf + 3), READ_BE_UINT16(buf + 5));
+ break;
+
+ case 49: // Hook - global transpose
+ player->decode_sysex_bytes(p + 1, buf, len - 2);
+ player->maybe_set_transpose(buf);
+ break;
+
+ case 50: // Hook - part on/off
+ buf[0] = *p++ & 0x0F;
+ player->decode_sysex_bytes(p, buf + 1, len - 2);
+ player->maybe_part_onoff(buf);
+ break;
+
+ case 51: // Hook - set volume
+ buf[0] = *p++ & 0x0F;
+ player->decode_sysex_bytes(p, buf + 1, len - 2);
+ player->maybe_set_volume(buf);
+ break;
+
+ case 52: // Hook - set program
+ buf[0] = *p++ & 0x0F;
+ player->decode_sysex_bytes(p, buf + 1, len - 2);
+ player->maybe_set_program(buf);
+ break;
+
+ case 53: // Hook - set transpose
+ buf[0] = *p++ & 0x0F;
+ player->decode_sysex_bytes(p, buf + 1, len - 2);
+ player->maybe_set_transpose_part(buf);
+ break;
+
+ case 64: // Marker
+ p++;
+ len -= 2;
+ while (len--) {
+ se->handle_marker(player->_id, *p++);
+ }
+ break;
+
+ case 80: // Loop
+ player->decode_sysex_bytes(p + 1, buf, len - 2);
+ player->setLoop
+ (READ_BE_UINT16(buf), READ_BE_UINT16(buf + 2),
+ READ_BE_UINT16(buf + 4), READ_BE_UINT16(buf + 6),
+ READ_BE_UINT16(buf + 8));
+ break;
+
+ case 81: // End loop
+ player->clearLoop();
+ break;
+
+ case 96: // Set instrument
+ part = player->getPart(p[0] & 0x0F);
+ a = (p[1] & 0x0F) << 12 |(p[2] & 0x0F) << 8 |(p[4] & 0x0F) << 4 |(p[4] & 0x0F);
+ if (part)
+ part->set_instrument(a);
+ break;
+
+ default:
+ error("Unknown SysEx command %d", (int)code);
+ }
+}
+
+} // End of namespace Scumm
diff --git a/engines/scumm/scumm.cpp b/engines/scumm/scumm.cpp
index 3bfefb1410..58b9c97dd7 100644
--- a/engines/scumm/scumm.cpp
+++ b/engines/scumm/scumm.cpp
@@ -56,6 +56,7 @@
#include "scumm/he/resource_he.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
+#include "scumm/imuse/sysex.h"
#include "scumm/he/sprite_he.h"
#include "scumm/util.h"
#include "scumm/verbs.h"
@@ -1703,6 +1704,9 @@ void ScummEngine::setupMusic(int midi) {
_musicEngine = _imuse = IMuse::create(_system, nativeMidiDriver, adlibMidiDriver);
if (_imuse) {
+ _imuse->addSysexHandler
+ (/*IMUSE_SYSEX_ID*/ 0x7D,
+ (_game.id == GID_SAMNMAX) ? sysexHandler_SamNMax : sysexHandler_Scumm);
_imuse->property(IMuse::PROP_GAME_ID, _game.id);
if (ConfMan.hasKey("tempo"))
_imuse->property(IMuse::PROP_TEMPO_BASE, ConfMan.getInt("tempo"));