[Scummvm-cvs-logs] SF.net SVN: scummvm: [20801] scummvm/trunk
fingolfin at users.sourceforge.net
fingolfin at users.sourceforge.net
Mon Feb 20 12:59:01 CET 2006
Revision: 20801
Author: fingolfin
Date: 2006-02-20 12:57:26 -0800 (Mon, 20 Feb 2006)
ViewCVS: http://svn.sourceforge.net/scummvm?rev=20801&view=rev
Log Message:
-----------
Moved iMUSE code to the new directory engines/scumm/imuse/
Modified Paths:
--------------
scummvm/trunk/TODO
scummvm/trunk/engines/scumm/akos.cpp
scummvm/trunk/engines/scumm/debugger.cpp
scummvm/trunk/engines/scumm/dialogs.cpp
scummvm/trunk/engines/scumm/he/script_v60he.cpp
scummvm/trunk/engines/scumm/he/sound_he.cpp
scummvm/trunk/engines/scumm/input.cpp
scummvm/trunk/engines/scumm/insane/insane.cpp
scummvm/trunk/engines/scumm/module.mk
scummvm/trunk/engines/scumm/resource.cpp
scummvm/trunk/engines/scumm/saveload.cpp
scummvm/trunk/engines/scumm/script_v6.cpp
scummvm/trunk/engines/scumm/scumm.cpp
scummvm/trunk/engines/scumm/smush/smush_mixer.cpp
scummvm/trunk/engines/scumm/smush/smush_player.cpp
scummvm/trunk/engines/scumm/sound.cpp
Added Paths:
-----------
scummvm/trunk/engines/scumm/imuse/
scummvm/trunk/engines/scumm/imuse/imuse.cpp
scummvm/trunk/engines/scumm/imuse/imuse.h
scummvm/trunk/engines/scumm/imuse/imuse_internal.h
scummvm/trunk/engines/scumm/imuse/imuse_player.cpp
scummvm/trunk/engines/scumm/imuse/instrument.cpp
scummvm/trunk/engines/scumm/imuse/instrument.h
Removed Paths:
-------------
scummvm/trunk/engines/scumm/imuse.cpp
scummvm/trunk/engines/scumm/imuse.h
scummvm/trunk/engines/scumm/imuse_internal.h
scummvm/trunk/engines/scumm/imuse_player.cpp
scummvm/trunk/engines/scumm/instrument.cpp
scummvm/trunk/engines/scumm/instrument.h
Modified: scummvm/trunk/TODO
===================================================================
--- scummvm/trunk/TODO 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/TODO 2006-02-20 20:57:26 UTC (rev 20801)
@@ -115,7 +115,6 @@
- consider moving the MIDI stuff from sound/ to sound/midi/
- move fmopl code to softsynth dir
- maybe common/system.h / system.cpp should go to backends/, too ?
- - move the iMuse source files in scumm/ to a new dir scumm/imuse/
* The following things should be put into namespaces:
- AudioStream and subclasses into Audio
- MIDI related classes either to Audio, or a new "MIDI" namespace
Modified: scummvm/trunk/engines/scumm/akos.cpp
===================================================================
--- scummvm/trunk/engines/scumm/akos.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/akos.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -26,7 +26,7 @@
#include "scumm/actor.h"
#include "scumm/akos.h"
#include "scumm/bomp.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/intern.h"
#include "scumm/he/intern_he.h"
Modified: scummvm/trunk/engines/scumm/debugger.cpp
===================================================================
--- scummvm/trunk/engines/scumm/debugger.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/debugger.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -30,7 +30,7 @@
#include "scumm/actor.h"
#include "scumm/boxes.h"
#include "scumm/debugger.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/object.h"
#include "scumm/player_v2.h"
#include "scumm/scumm.h"
Modified: scummvm/trunk/engines/scumm/dialogs.cpp
===================================================================
--- scummvm/trunk/engines/scumm/dialogs.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/dialogs.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -35,7 +35,7 @@
#include "scumm/dialogs.h"
#include "scumm/sound.h"
#include "scumm/scumm.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/player_v2.h"
#include "scumm/verbs.h"
Modified: scummvm/trunk/engines/scumm/he/script_v60he.cpp
===================================================================
--- scummvm/trunk/engines/scumm/he/script_v60he.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/he/script_v60he.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -26,7 +26,7 @@
#include "scumm/actor.h"
#include "scumm/charset.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
#include "scumm/resource.h"
Modified: scummvm/trunk/engines/scumm/he/sound_he.cpp
===================================================================
--- scummvm/trunk/engines/scumm/he/sound_he.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/he/sound_he.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -23,7 +23,7 @@
#include "common/stdafx.h"
#include "scumm/actor.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/util.h"
Property changes on: scummvm/trunk/engines/scumm/imuse
___________________________________________________________________
Name: svn:ignore
+ .deps
*.o
lib*.a
Copied: scummvm/trunk/engines/scumm/imuse/imuse.cpp (from rev 20795, scummvm/trunk/engines/scumm/imuse.cpp)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse.cpp (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/imuse.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,2043 @@
+/* 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 "base/version.h"
+
+#include "common/util.h"
+#include "common/system.h"
+
+#include "scumm/imuse/imuse.h"
+#include "scumm/imuse/imuse_internal.h"
+#include "scumm/imuse/instrument.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+#include "scumm/util.h"
+
+#include "sound/mididrv.h"
+
+
+namespace Scumm {
+
+////////////////////////////////////////
+//
+// IMuseInternal implementation
+//
+////////////////////////////////////////
+
+IMuseInternal::IMuseInternal() :
+_native_mt32(false),
+_enable_gs(false),
+_sc55(false),
+_midi_adlib(0),
+_midi_native(0),
+_base_sounds(0),
+_paused(false),
+_initialized(false),
+_tempoFactor(0),
+_player_limit(ARRAYSIZE(_players)),
+_recycle_players(false),
+_direct_passthrough(false),
+_queue_end(0),
+_queue_pos(0),
+_queue_sound(0),
+_queue_adding(0),
+_queue_marker(0),
+_queue_cleared(0),
+_master_volume(0),
+_music_volume(0),
+_trigger_count(0),
+_snm_trigger_index(0) {
+ memset(_channel_volume,0,sizeof(_channel_volume));
+ memset(_channel_volume_eff,0,sizeof(_channel_volume_eff));
+ memset(_volchan_table,0,sizeof(_volchan_table));
+}
+
+byte *IMuseInternal::findStartOfSound(int sound) {
+ byte *ptr = NULL;
+ int32 size, pos;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL) {
+ debug(1, "IMuseInternal::findStartOfSound(): Sound %d doesn't exist!", sound);
+ return NULL;
+ }
+
+ // Check for old-style headers first, like 'RO'
+ if (ptr[4] == 'R' && ptr[5] == 'O'&& ptr[6] != 'L')
+ return ptr + 4;
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return ptr + 8;
+
+ ptr += 8;
+ size = READ_BE_UINT32(ptr);
+ ptr += 4;
+
+ // Okay, we're looking for one of those things: either
+ // an 'MThd' tag (for SMF), or a 'FORM' tag (for XMIDI).
+ size = 48; // Arbitrary; we should find our tag within the first 48 bytes of the resource
+ pos = 0;
+ while (pos < size) {
+ if (!memcmp(ptr + pos, "MThd", 4) || !memcmp(ptr + pos, "FORM", 4))
+ return ptr + pos;
+ ++pos; // We could probably iterate more intelligently
+ }
+
+ debug(3, "IMuseInternal::findStartOfSound(): Failed to align on sound %d!", sound);
+ return 0;
+}
+
+bool IMuseInternal::isMT32(int sound) {
+ byte *ptr = NULL;
+ uint32 tag;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL)
+ return false;
+
+ tag = *(((uint32 *)ptr) + 1);
+ switch (tag) {
+ case MKID('ADL '):
+ case MKID('ASFX'): // Special AD class for old Adlib sound effects
+ case MKID('SPK '):
+ return false;
+
+ case MKID('AMI '):
+ case MKID('ROL '):
+ return true;
+
+ case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
+ return true;
+
+ case MKID('GMD '):
+ case MKID('MIDI'): // Occurs in Sam & Max
+ return false;
+ }
+
+ // Old style 'RO' has equivalent properties to 'ROL'
+ if (ptr[4] == 'R' && ptr[5] == 'O')
+ return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return false;
+
+ error("Unknown music type: '%s'", tag2str(tag));
+
+ return false;
+}
+
+bool IMuseInternal::isMIDI(int sound) {
+ byte *ptr = NULL;
+ uint32 tag;
+
+ if (_base_sounds)
+ ptr = _base_sounds[sound];
+
+ if (ptr == NULL)
+ return false;
+
+ tag = *(((uint32 *)ptr) + 1);
+ switch (tag) {
+ case MKID('ADL '):
+ case MKID('ASFX'): // Special AD class for old Adlib sound effects
+ case MKID('SPK '):
+ return false;
+
+ case MKID('AMI '):
+ case MKID('ROL '):
+ return true;
+
+ case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
+ return true;
+
+ case MKID('GMD '):
+ case MKID('MIDI'): // Occurs in Sam & Max
+ return true;
+ }
+
+ // Old style 'RO' has equivalent properties to 'ROL'
+ if (ptr[4] == 'R' && ptr[5] == 'O')
+ return true;
+ // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
+ // FIXME: Right now we're pretending it's GM.
+ if (ptr[8] == 'S' && ptr[9] == 'O')
+ return true;
+
+ error("Unknown music type: '%s'", tag2str(tag));
+
+ return false;
+}
+
+MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
+ MidiDriver *driver = NULL;
+
+ if (isMIDI(sound)) {
+ if (_midi_native) {
+ driver = _midi_native;
+ } else {
+ // Route it through Adlib anyway.
+ driver = _midi_adlib;
+ }
+ } else {
+ driver = _midi_adlib;
+ }
+ return driver;
+}
+
+bool IMuseInternal::startSound(int sound) {
+ Player *player;
+ void *ptr;
+
+ // Do not start a sound if it is already set to start on an ImTrigger
+ // event. This fixes carnival music problems where a sound has been set
+ // to trigger at the right time, but then is started up immediately
+ // anyway, only to be restarted later when the trigger occurs.
+ //
+ // However, we have to make sure the sound with the trigger is actually
+ // playing, otherwise the music may stop when Sam and Max are thrown
+ // out of Bumpusville, because entering the mansion sets up a trigger
+ // for a sound that isn't necessarily playing. This is somewhat related
+ // to bug #780918.
+
+ int i;
+ ImTrigger *trigger = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trigger) {
+ if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound && getSoundStatus(trigger->sound))
+ return false;
+ }
+
+ ptr = findStartOfSound(sound);
+ if (!ptr) {
+ debug(2, "IMuseInternal::startSound(): Couldn't find sound %d!", sound);
+ return false;
+ }
+
+ // Check which MIDI driver this track should use.
+ // If it's NULL, it ain't something we can play.
+ MidiDriver *driver = getBestMidiDriver(sound);
+ if (!driver)
+ return false;
+
+ // If the requested sound is already playing, start it over
+ // from scratch. This was originally a hack to prevent Sam & Max
+ // iMuse messiness while upgrading the iMuse engine, but it
+ // is apparently necessary to deal with fade-and-restart
+ // race conditions that were observed in MI2. Reference
+ // Bug #590511 and Patch #607175 (which was reversed to fix
+ // an FOA regression: Bug #622606).
+ player = findActivePlayer(sound);
+ if (!player)
+ player = allocate_player(128);
+ if (!player)
+ return false;
+
+ // HACK: This is to work around a problem at the Dino Bungie Memorial.
+ // There are three pieces of music involved here:
+ //
+ // 80 - Main theme (looping)
+ // 81 - Music when entering Rex's and Wally's room (not looping)
+ // 82 - Music when listening to Rex or Wally
+ //
+ // When entering, tune 81 starts, tune 80 is faded down (not out) and
+ // a trigger is set in tune 81 to fade tune 80 back up.
+ //
+ // When listening to Rex or Wally, tune 82 is started, tune 81 is faded
+ // out and tune 80 is faded down even further.
+ //
+ // However, when tune 81 is faded out its trigger will cause tune 80 to
+ // fade back up, resulting in two tunes being played simultaneously at
+ // full blast. It's no use trying to keep tune 81 playing at volume 0.
+ // It doesn't loop, so eventually it will terminate on its own.
+ //
+ // I don't know how the original interpreter handled this - or even if
+ // it handled it at all - but it looks like sloppy scripting to me. Our
+ // workaround is to clear the trigger if the player listens to Rex or
+ // Wally before tune 81 has finished on its own.
+
+ if (g_scumm->_game.id == GID_SAMNMAX && sound == 82 && getSoundStatus(81, false))
+ ImClearTrigger(81, 1);
+
+ player->clear();
+ return player->startSound(sound, driver, _direct_passthrough);
+}
+
+
+Player *IMuseInternal::allocate_player(byte priority) {
+ Player *player = _players, *best = NULL;
+ int i;
+ byte bestpri = 255;
+
+ for (i = _player_limit; i != 0; i--, player++) {
+ if (!player->isActive())
+ return player;
+ if (player->getPriority() < bestpri) {
+ best = player;
+ bestpri = player->getPriority();
+ }
+ }
+
+ if (bestpri < priority || _recycle_players)
+ return best;
+
+ debug(1, "Denying player request");
+ return NULL;
+}
+
+void IMuseInternal::init_players() {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ player->_se = this;
+ player->clear(); // Used to just set _active to false
+ }
+}
+
+void IMuseInternal::init_parts() {
+ Part *part;
+ int i;
+
+ for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
+ part->init();
+ part->_se = this;
+ part->_slot = i;
+ }
+}
+
+int IMuseInternal::stopSound(int sound) {
+ int r = -1;
+ Player *player = findActivePlayer(sound);
+ if (player) {
+ player->clear();
+ r = 0;
+ }
+ return r;
+}
+
+int IMuseInternal::stopAllSounds() {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive())
+ player->clear();
+ }
+ return 0;
+}
+
+void IMuseInternal::on_timer(MidiDriver *midi) {
+ if (_paused || !_initialized)
+ return;
+
+ if (midi == _midi_native || !_midi_native)
+ handleDeferredCommands(midi);
+ sequencer_timers(midi);
+}
+
+int IMuseInternal::getMusicTimer() const {
+ int best_time = 0;
+ const Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive()) {
+ int timer = player->getMusicTimer();
+ if (timer > best_time)
+ best_time = timer;
+ }
+ }
+ return best_time;
+}
+
+void IMuseInternal::sequencer_timers(MidiDriver *midi) {
+ Player *player = _players;
+ int i;
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && player->getMidiDriver() == midi) {
+ player->onTimer();
+ }
+ }
+}
+
+void IMuseInternal::handle_marker(uint id, byte data) {
+ uint16 *p = 0;
+ uint pos;
+
+ if (_queue_adding && _queue_sound == id && data == _queue_marker)
+ return;
+
+ // Fix for bug #733401, revised for bug #761637:
+ // It would seem that sometimes a marker is in the queue
+ // but not at the head position. In the case of our bug,
+ // this seems to be the result of commands in the queue
+ // for songs that are no longer playing. So we skip
+ // ahead to the appropriate marker, effectively chomping
+ // anything in the queue before it. This fixes the FOA
+ // end credits music, but needs to be tested for inappopriate
+ // behavior elsewhere.
+ pos = _queue_end;
+ while (pos != _queue_pos) {
+ p = _cmd_queue[pos].array;
+ if (p[0] == TRIGGER_ID && p[1] == id && p[2] == data)
+ break;
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ }
+
+ if (pos == _queue_pos)
+ return;
+
+ if (pos != _queue_end)
+ debug(0, "Skipping entries in iMuse command queue to reach marker");
+
+ _trigger_count--;
+ _queue_cleared = false;
+ do {
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ if (_queue_pos == pos)
+ break;
+ p = _cmd_queue[pos].array;
+ if (*p++ != COMMAND_ID)
+ break;
+ _queue_end = pos;
+
+ doCommand(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
+
+ if (_queue_cleared)
+ return;
+ pos = _queue_end;
+ } while (1);
+
+ _queue_end = pos;
+}
+
+int IMuseInternal::get_channel_volume(uint a) {
+ if (a < 8)
+ return _channel_volume_eff[a];
+ return (_master_volume * _music_volume / 255) / 2;
+}
+
+Part *IMuseInternal::allocate_part(byte pri, MidiDriver *midi) {
+ Part *part, *best = NULL;
+ int i;
+
+ for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
+ if (!part->_player)
+ return part;
+ if (pri >= part->_pri_eff) {
+ pri = part->_pri_eff;
+ best = part;
+ }
+ }
+
+ if (best) {
+ best->uninit();
+ reallocateMidiChannels(midi);
+ } else {
+ debug(1, "Denying part request");
+ }
+ return best;
+}
+
+int IMuseInternal::getSoundStatus(int sound, bool ignoreFadeouts) const {
+ int i;
+ const Player *player = _players;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && (!ignoreFadeouts || !player->isFadingOut())) {
+ if (sound == -1)
+ return player->getID();
+ else if (player->getID() == (uint16)sound)
+ return 1;
+ }
+ }
+ return (sound == -1) ? 0 : get_queue_sound_status(sound);
+}
+
+int IMuseInternal::get_queue_sound_status(int sound) const {
+ const uint16 *a;
+ int i, j;
+
+ j = _queue_pos;
+ i = _queue_end;
+
+ while (i != j) {
+ a = _cmd_queue[i].array;
+ if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
+ return 2;
+ i = (i + 1) % ARRAYSIZE(_cmd_queue);
+ }
+
+ for (i = 0; i < ARRAYSIZE (_deferredCommands); ++i) {
+ if (_deferredCommands[i].time_left && _deferredCommands[i].a == 8 &&
+ _deferredCommands[i].b == sound) {
+ return 2;
+ }
+ }
+
+ return 0;
+}
+
+int IMuseInternal::set_volchan(int sound, int volchan) {
+ int r;
+ int i;
+ int num;
+ Player *player, *best, *sameid;
+
+ r = get_volchan_entry(volchan);
+ if (r == -1)
+ return -1;
+
+ if (r >= 8) {
+ player = findActivePlayer(sound);
+ if (player && player->_vol_chan != (uint16)volchan) {
+ player->_vol_chan = volchan;
+ player->setVolume(player->getVolume());
+ return 0;
+ }
+ return -1;
+ } else {
+ best = NULL;
+ num = 0;
+ sameid = NULL;
+ for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
+ if (player->isActive()) {
+ if (player->_vol_chan == (uint16)volchan) {
+ num++;
+ if (!best || player->getPriority() <= best->getPriority())
+ best = player;
+ } else if (player->getID() == (uint16)sound) {
+ sameid = player;
+ }
+ }
+ }
+ if (sameid == NULL)
+ return -1;
+ if (num >= r)
+ best->clear();
+ player->_vol_chan = volchan;
+ player->setVolume(player->getVolume());
+ return 0;
+ }
+}
+
+int IMuseInternal::clear_queue() {
+ _queue_adding = false;
+ _queue_cleared = true;
+ _queue_pos = 0;
+ _queue_end = 0;
+ _trigger_count = 0;
+ return 0;
+}
+
+int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g) {
+ uint16 *p;
+ uint i;
+
+ i = _queue_pos;
+
+ if (i == _queue_end)
+ return -1;
+
+ if (a == -1) {
+ _queue_adding = false;
+ _trigger_count++;
+ return 0;
+ }
+
+ p = _cmd_queue[_queue_pos].array;
+ p[0] = COMMAND_ID;
+ p[1] = a;
+ p[2] = b;
+ p[3] = c;
+ p[4] = d;
+ p[5] = e;
+ p[6] = f;
+ p[7] = g;
+
+ i = (i + 1) % ARRAYSIZE(_cmd_queue);
+
+ if (_queue_end != i) {
+ _queue_pos = i;
+ return 0;
+ } else {
+ _queue_pos = (i - 1) % ARRAYSIZE(_cmd_queue);
+ return -1;
+ }
+}
+
+int IMuseInternal::query_queue(int param) {
+ switch (param) {
+ case 0: // Get trigger count
+ return _trigger_count;
+ case 1: // Get trigger type
+ if (_queue_end == _queue_pos)
+ return -1;
+ return _cmd_queue[_queue_end].array[1];
+ case 2: // Get trigger sound
+ if (_queue_end == _queue_pos)
+ return 0xFF;
+ return _cmd_queue[_queue_end].array[2];
+ default:
+ return -1;
+ }
+}
+
+int IMuseInternal::setMusicVolume(uint vol) {
+ if (vol > 255)
+ vol = 255;
+ if (_music_volume == vol)
+ return 0;
+ _music_volume = vol;
+ vol = _master_volume * _music_volume / 255;
+ for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
+ _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
+ }
+ if (!_paused)
+ update_volumes();
+ return 0;
+}
+
+int IMuseInternal::setImuseMasterVolume(uint vol) {
+ if (vol > 255)
+ vol = 255;
+ if (_master_volume == vol)
+ return 0;
+ _master_volume = vol;
+ vol = _master_volume * _music_volume / 255;
+ for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
+ _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
+ }
+ if (!_paused)
+ update_volumes();
+ return 0;
+}
+
+int IMuseInternal::terminate1() {
+ _initialized = false;
+ stopAllSounds();
+ return 0;
+}
+
+// This is the stuff that has to be done
+// outside the monitor's mutex, otherwise
+// a deadlock occurs.
+int IMuseInternal::terminate2() {
+ if (_midi_adlib) {
+ _midi_adlib->close();
+ delete _midi_adlib;
+ _midi_adlib = 0;
+ }
+
+ if (_midi_native) {
+ _midi_native->close();
+ delete _midi_native;
+ _midi_native = 0;
+ }
+
+ return 0;
+}
+
+int IMuseInternal::enqueue_trigger(int sound, int marker) {
+ uint16 *p;
+ uint pos;
+
+ pos = _queue_pos;
+
+ p = _cmd_queue[pos].array;
+ p[0] = TRIGGER_ID;
+ p[1] = sound;
+ p[2] = marker;
+
+ pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
+ if (_queue_end == pos) {
+ _queue_pos = (pos - 1) % ARRAYSIZE(_cmd_queue);
+ return -1;
+ }
+
+ _queue_pos = pos;
+ _queue_adding = true;
+ _queue_sound = sound;
+ _queue_marker = marker;
+ return 0;
+}
+
+int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
+ int args[8];
+ args[0] = a;
+ args[1] = b;
+ args[2] = c;
+ args[3] = d;
+ args[4] = e;
+ args[5] = f;
+ args[6] = g;
+ args[7] = h;
+ return doCommand(8, args);
+}
+
+int32 IMuseInternal::doCommand(int numargs, int a[]) {
+ int i;
+
+ if (numargs < 1)
+ return -1;
+ byte cmd = a[0] & 0xFF;
+ byte param = a[0] >> 8;
+ Player *player = NULL;
+
+ if (!_initialized && (cmd || param))
+ return -1;
+
+#ifdef IMUSE_DEBUG
+ {
+ char string[128];
+ sprintf(string, "doCommand - %d (%d/%d)", a[0], (int)param, (int)cmd);
+ for (i = 1; i < numargs; ++i)
+ sprintf(string + strlen(string), ", %d", a[i]);
+ debug(0, string);
+ }
+#endif
+
+ if (param == 0) {
+ switch (cmd) {
+ case 6:
+ if (a[1] > 127)
+ return -1;
+ else {
+ debug(0, "IMuse doCommand(6) - setImuseMasterVolume (%d)", a[1]);
+ return setImuseMasterVolume((a[1] << 1) | (a[1] ? 0 : 1)); // Convert from 0-127 to 0-255
+ }
+ case 7:
+ debug(0, "IMuse doCommand(7) - getMasterVolume (%d)", a[1]);
+ return _master_volume / 2; // Convert from 0-255 to 0-127
+ case 8:
+ return startSound(a[1]) ? 0 : -1;
+ case 9:
+ return stopSound(a[1]);
+ case 10: // FIXME: Sam and Max - Not sure if this is correct
+ return stopAllSounds();
+ case 11:
+ return stopAllSounds();
+ case 12:
+ // Sam & Max: Player-scope commands
+ player = findActivePlayer(a[1]);
+ if (!player)
+ return -1;
+
+ switch (a[3]) {
+ case 6:
+ // Set player volume.
+ return player->setVolume(a[4]);
+ default:
+ error("IMuseInternal::doCommand(12) unsupported sub-command %d", a[3]);
+ }
+ return -1;
+ case 13:
+ return getSoundStatus(a[1]);
+ case 14:
+ // Sam and Max: Parameter fade
+ player = findActivePlayer(a[1]);
+ if (player)
+ return player->addParameterFader(a[3], a[4], a[5]);
+ return -1;
+
+ case 15:
+ // Sam & Max: Set hook for a "maybe" jump
+ player = findActivePlayer(a[1]);
+ if (player) {
+ player->setHook(0, a[3], 0);
+ return 0;
+ }
+ return -1;
+ case 16:
+ debug(0, "IMuse doCommand(16) - set_volchan (%d, %d)", a[1], a[2]);
+ return set_volchan(a[1], a[2]);
+ case 17:
+ if (g_scumm->_game.id != GID_SAMNMAX) {
+ debug(0, "IMuse doCommand(17) - set_channel_volume (%d, %d)", a[1], a[2]);
+ return set_channel_volume(a[1], a[2]);
+ } else {
+ if (a[4]) {
+ int b[16];
+ memset(b, 0, sizeof(b));
+ for (i = 0; i < numargs; ++i)
+ b[i] = a[i];
+ return ImSetTrigger(b[1], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11]);
+ } else {
+ return ImClearTrigger(a[1], a[3]);
+ }
+ }
+ case 18:
+ if (g_scumm->_game.id != GID_SAMNMAX) {
+ return set_volchan_entry(a[1], a[2]);
+ } else {
+ // Sam & Max: ImCheckTrigger.
+ // According to Mike's notes to Ender,
+ // this function returns the number of triggers
+ // associated with a particular player ID and
+ // trigger ID.
+ a[0] = 0;
+ for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
+ if (_snm_triggers[i].sound == a[1] && _snm_triggers[i].id &&
+ (a[3] == -1 || _snm_triggers[i].id == a[3]))
+ {
+ ++a[0];
+ }
+ }
+ return a[0];
+ }
+ case 19:
+ // Sam & Max: ImClearTrigger
+ // This should clear a trigger that's been set up
+ // with ImSetTrigger(cmd == 17). Seems to work....
+ return ImClearTrigger(a[1], a[3]);
+ case 20:
+ // Sam & Max: Deferred Command
+ addDeferredCommand(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ return 0;
+ case 2:
+ case 3:
+ return 0;
+ default:
+ error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ }
+ } else if (param == 1) {
+ if ((1 << cmd) & 0x783FFF) {
+ player = findActivePlayer(a[1]);
+ if (!player)
+ return -1;
+ if ((1 << cmd) & (1 << 11 | 1 << 22)) {
+ assert(a[2] >= 0 && a[2] <= 15);
+ player = (Player *)player->getPart(a[2]);
+ if (!player)
+ return -1;
+ }
+ }
+
+ switch (cmd) {
+ case 0:
+ if (g_scumm->_game.id == GID_SAMNMAX) {
+ if (a[3] == 1) // Measure number
+ return ((player->getBeatIndex() - 1) >> 2) + 1;
+ else if (a[3] == 2) // Beat number
+ return player->getBeatIndex();
+ return -1;
+ } else {
+ return player->getParam(a[2], a[3]);
+ }
+ case 1:
+ if (g_scumm->_game.id == GID_SAMNMAX) {
+ // FIXME: Could someone verify this?
+ //
+ // This jump instruction is known to be used in
+ // the following cases:
+ //
+ // 1) Going anywhere on the USA map
+ // 2) Winning the Wak-A-Rat game
+ // 3) Losing or quitting the Wak-A-Rat game
+ // 4) Conroy hitting Max with a golf club
+ //
+ // For all these cases the position parameters
+ // are always the same: 2, 1, 0, 0.
+ //
+ // 5) When leaving the bigfoot party. The
+ // position parameters are: 3, 4, 300, 0
+ // 6) At Frog Rock, when the UFO appears. The
+ // position parameters are: 10, 4, 400, 1
+ //
+ // The last two cases used to be buggy, so I
+ // have made a change to how the last two
+ // position parameters are handled. I still do
+ // not know if it's correct, but it sounds
+ // good to me at least.
+
+ debug(0, "doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d)", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ player->jump(a[3] - 1, (a[4] - 1) * 4 + a[5], a[6] + ((a[7] * player->getTicksPerBeat()) >> 2));
+ } else
+ player->setPriority(a[2]);
+ return 0;
+ case 2:
+ return player->setVolume(a[2]);
+ case 3:
+ player->setPan(a[2]);
+ return 0;
+ case 4:
+ return player->setTranspose(a[2], a[3]);
+ case 5:
+ player->setDetune(a[2]);
+ return 0;
+ case 6:
+ player->setSpeed(a[2]);
+ return 0;
+ case 7:
+ return player->jump(a[2], a[3], a[4]) ? 0 : -1;
+ case 8:
+ return player->scan(a[2], a[3], a[4]);
+ case 9:
+ return player->setLoop(a[2], a[3], a[4], a[5], a[6]) ? 0 : -1;
+ case 10:
+ player->clearLoop();
+ return 0;
+ case 11:
+ ((Part *)player)->set_onoff(a[3] != 0);
+ return 0;
+ case 12:
+ return player->setHook(a[2], a[3], a[4]);
+ case 13:
+ return player->addParameterFader(ParameterFader::pfVolume, a[2], a[3]);
+ case 14:
+ return enqueue_trigger(a[1], a[2]);
+ case 15:
+ return enqueue_command(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ case 16:
+ return clear_queue();
+ case 19:
+ return player->getParam(a[2], a[3]);
+ case 20:
+ return player->setHook(a[2], a[3], a[4]);
+ case 21:
+ return -1;
+ case 22:
+ ((Part *)player)->volume(a[3]);
+ return 0;
+ case 23:
+ return query_queue(a[1]);
+ case 24:
+ return 0;
+ default:
+ error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
+ return -1;
+ }
+ }
+
+ return -1;
+}
+
+int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h) {
+ // Sam & Max: ImSetTrigger.
+ // Sets a trigger for a particular player and
+ // marker ID, along with doCommand parameters
+ // to invoke at the marker. The marker is
+ // represented by MIDI SysEx block 00 xx(F7)
+ // where "xx" is the marker ID.
+ uint16 oldest_trigger = 0;
+ ImTrigger *oldest_ptr = NULL;
+
+ int i;
+ ImTrigger *trig = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
+ if (!trig->id)
+ break;
+ // We used to only compare 'id' and 'sound' here, but at least
+ // at the Dino Bungie Memorial that causes the music to stop
+ // after getting the T-Rex tooth. See bug #888161.
+ if (trig->id == id && trig->sound == sound && trig->command[0] == a)
+ break;
+
+ uint16 diff;
+ if (trig->expire <= _snm_trigger_index)
+ diff = _snm_trigger_index - trig->expire;
+ else
+ diff = 0x10000 - trig->expire + _snm_trigger_index;
+
+ if (!oldest_ptr || oldest_trigger < diff) {
+ oldest_ptr = trig;
+ oldest_trigger = diff;
+ }
+ }
+
+ // If we didn't find a trigger, see if we can expire one.
+ if (!i) {
+ if (!oldest_ptr)
+ return -1;
+ trig = oldest_ptr;
+ }
+
+ trig->id = id;
+ trig->sound = sound;
+ trig->expire = (++_snm_trigger_index & 0xFFFF);
+ trig->command[0] = a;
+ trig->command[1] = b;
+ trig->command[2] = c;
+ trig->command[3] = d;
+ trig->command[4] = e;
+ trig->command[5] = f;
+ trig->command[6] = g;
+ trig->command[7] = h;
+
+ // If the command is to start a sound, stop that sound if it's already playing.
+ // This fixes some carnival music problems.
+ // NOTE: We ONLY do this if the sound that will trigger the command is actually
+ // playing. Otherwise, there's a problem when exiting and re-entering the
+ // Bumpusville mansion. Ref Bug #780918.
+ if (trig->command[0] == 8 && getSoundStatus(trig->command[1]) && getSoundStatus(sound))
+ stopSound(trig->command[1]);
+ return 0;
+}
+
+int32 IMuseInternal::ImClearTrigger(int sound, int id) {
+ int count = 0;
+ int i;
+ ImTrigger *trig = _snm_triggers;
+ for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
+ if ((sound == -1 || trig->sound == sound) && trig->id && (id == -1 || trig->id == id)) {
+ trig->sound = trig->id = 0;
+ ++count;
+ }
+ }
+ return (count > 0) ? 0 : -1;
+}
+
+int32 IMuseInternal::ImFireAllTriggers(int sound) {
+ if (!sound)
+ return 0;
+ int count = 0;
+ int i;
+ for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
+ if (_snm_triggers[i].sound == sound) {
+ _snm_triggers[i].sound = _snm_triggers[i].id = 0;
+ doCommand(8, _snm_triggers[i].command);
+ ++count;
+ }
+ }
+ return (count > 0) ? 0 : -1;
+}
+
+int IMuseInternal::set_channel_volume(uint chan, uint vol)
+{
+ if (chan >= 8 || vol > 127)
+ return -1;
+
+ _channel_volume[chan] = vol;
+ _channel_volume_eff[chan] = _master_volume * _music_volume * vol / 255 / 255;
+ update_volumes();
+ return 0;
+}
+
+void IMuseInternal::update_volumes() {
+ Player *player;
+ int i;
+
+ for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
+ if (player->isActive())
+ player->setVolume(player->getVolume());
+ }
+}
+
+int IMuseInternal::set_volchan_entry(uint a, uint b) {
+ if (a >= 8)
+ return -1;
+ _volchan_table[a] = b;
+ return 0;
+}
+
+int HookDatas::query_param(int param, byte chan) {
+ switch (param) {
+ case 18:
+ return _jump[0];
+ case 19:
+ return _transpose;
+ case 20:
+ return _part_onoff[chan];
+ case 21:
+ return _part_volume[chan];
+ case 22:
+ return _part_program[chan];
+ case 23:
+ return _part_transpose[chan];
+ default:
+ return -1;
+ }
+}
+
+int HookDatas::set(byte cls, byte value, byte chan) {
+ switch (cls) {
+ case 0:
+ if (value != _jump[0]) {
+ _jump[1] = _jump[0];
+ _jump[0] = value;
+ }
+ break;
+ case 1:
+ _transpose = value;
+ break;
+ case 2:
+ if (chan < 16)
+ _part_onoff[chan] = value;
+ else if (chan == 16)
+ memset(_part_onoff, value, 16);
+ break;
+ case 3:
+ if (chan < 16)
+ _part_volume[chan] = value;
+ else if (chan == 16)
+ memset(_part_volume, value, 16);
+ break;
+ case 4:
+ if (chan < 16)
+ _part_program[chan] = value;
+ else if (chan == 16)
+ memset(_part_program, value, 16);
+ break;
+ case 5:
+ if (chan < 16)
+ _part_transpose[chan] = value;
+ else if (chan == 16)
+ memset(_part_transpose, value, 16);
+ break;
+ default:
+ return -1;
+ }
+ return 0;
+}
+
+Player *IMuseInternal::findActivePlayer(int id) {
+ int i;
+ Player *player = _players;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive() && player->getID() == (uint16)id)
+ return player;
+ }
+ return NULL;
+}
+
+int IMuseInternal::get_volchan_entry(uint a) {
+ if (a < 8)
+ return _volchan_table[a];
+ return -1;
+}
+
+uint32 IMuseInternal::property(int prop, uint32 value) {
+ switch (prop) {
+ case IMuse::PROP_TEMPO_BASE:
+ // This is a specified as a percentage of normal
+ // music speed. The number must be an integer
+ // ranging from 50 to 200(for 50% to 200% normal speed).
+ if (value >= 50 && value <= 200)
+ _tempoFactor = value;
+ break;
+
+ case IMuse::PROP_NATIVE_MT32:
+ _native_mt32 = (value > 0);
+ Instrument::nativeMT32(_native_mt32);
+ if (_midi_native && _native_mt32)
+ initMT32(_midi_native);
+ break;
+
+ case IMuse::PROP_GS:
+ _enable_gs = (value > 0);
+
+ // If True Roland MT-32 is not selected, run in GM or GS mode.
+ // If it is selected, change the Roland GS synth to MT-32 mode.
+ if (_midi_native && !_native_mt32)
+ initGM(_midi_native);
+ else if (_midi_native && _native_mt32 && _enable_gs) {
+ _sc55 = true;
+ initGM(_midi_native);
+ }
+ break;
+
+ case IMuse::PROP_LIMIT_PLAYERS:
+ if (value > 0 && value <= ARRAYSIZE(_players))
+ _player_limit = (int)value;
+ break;
+
+ case IMuse::PROP_RECYCLE_PLAYERS:
+ _recycle_players = (value != 0);
+ break;
+
+ case IMuse::PROP_DIRECT_PASSTHROUGH:
+ _direct_passthrough = (value != 0);
+ break;
+ }
+
+ return 0;
+}
+
+void IMuseInternal::setBase(byte **base) {
+ _base_sounds = base;
+}
+
+IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
+ IMuseInternal *i = new IMuseInternal;
+ i->initialize(syst, nativeMidiDriver, adlibMidiDriver);
+ return i;
+}
+
+int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver *adlib_midi) {
+ int i;
+
+ _midi_native = native_midi;
+ _midi_adlib = adlib_midi;
+ if (native_midi != NULL)
+ initMidiDriver(native_midi);
+ if (adlib_midi != NULL)
+ initMidiDriver(adlib_midi);
+
+ if (!_tempoFactor)
+ _tempoFactor = 100;
+ _master_volume = 255;
+
+ for (i = 0; i != 8; i++)
+ _channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
+
+ init_players();
+ init_queue();
+ init_parts();
+
+ _initialized = true;
+
+ return 0;
+}
+
+void IMuseInternal::initMidiDriver(MidiDriver *midi) {
+ // Open MIDI driver
+ int result = midi->open();
+ if (result)
+ error("IMuse initialization - %s", MidiDriver::getErrorName(result));
+
+ // Connect to the driver's timer
+ midi->setTimerCallback(midi, &IMuseInternal::midiTimerCallback);
+}
+
+void IMuseInternal::initMT32(MidiDriver *midi) {
+ byte buffer[52];
+ char info[256] = "ScummVM ";
+ int len;
+
+ // Reset the MT-32
+ memcpy(&buffer[0], "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
+ midi->sysEx(buffer, 9);
+ g_system->delayMillis(100);
+
+ // Compute version string (truncated to 20 chars max.)
+ strcat(info, gScummVMVersion);
+ len = strlen(info);
+ if (len > 20)
+ len = 20;
+
+ // Display a welcome message on MT-32 displays.
+ memcpy(&buffer[4], "\x20\x00\x00", 3);
+ memcpy(&buffer[7], " ", 20);
+ memcpy(buffer + 7 +(20 - len) / 2, info, len);
+ byte checksum = 0;
+ for (int i = 4; i < 27; ++i)
+ checksum -= buffer[i];
+ buffer[27] = checksum & 0x7F;
+ midi->sysEx(buffer, 28);
+ g_system->delayMillis(500);
+
+ // Setup master tune, reverb mode, reverb time, reverb level,
+ // channel mapping, partial reserve and master volume
+ memcpy(&buffer[4], "\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 27);
+ midi->sysEx(buffer, 31);
+ g_system->delayMillis(250);
+
+ // Map percussion to notes 24 - 34 without reverb
+ memcpy(&buffer[4], "\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 48);
+ midi->sysEx(buffer, 52);
+ g_system->delayMillis(250);
+}
+
+void IMuseInternal::initGM(MidiDriver *midi) {
+ byte buffer[11];
+ int i;
+
+ // General MIDI System On message
+ // Resets all GM devices to default settings
+ memcpy(&buffer[0], "\xF0\x7E\x7F\x09\x01\xF7", 6);
+ midi->sysEx(buffer, 6);
+ debug(2, "GM SysEx: GM System On");
+ g_system->delayMillis(200);
+
+ if (_enable_gs) {
+
+ // All GS devices recognize the GS Reset command,
+ // even with Roland's ID. It is impractical to
+ // support other manufacturers' devices for
+ // further GS settings, as there are limitless
+ // numbers of them out there that would each
+ // require individual SysEx commands with unique IDs.
+
+ // Roland GS SysEx ID
+ memcpy(&buffer[0], "\xF0\x41\x10\x42\x12", 5);
+
+ // GS Reset
+ memcpy(&buffer[5], "\x40\x00\x7F\x00\x41\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: GS Reset");
+ g_system->delayMillis(200);
+
+ if (_sc55) {
+ // This mode is for GS devices that support an MT-32-compatible
+ // Map, such as the Roland Sound Canvas line of modules. It
+ // will allow them to work with True MT-32 mode, but will
+ // obviously still ignore MT-32 SysEx (and thus custom
+ // instruments).
+
+ // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
+ for (i = 0; i < 16; ++i) {
+ midi->send(( 127 << 16) | (0 << 8) | (0xB0 | i));
+ midi->send(( 1 << 16) | (32 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (0 << 8) | (0xC0 | i));
+ }
+ debug(2, "GS Program Change: CM-64/32L Map Selected");
+
+ // Set Percussion Channel to SC-55 Map (CC#32, 01H), then
+ // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
+ midi->getPercussionChannel()->controlChange(0, 0);
+ midi->getPercussionChannel()->controlChange(32, 1);
+ midi->send(127 << 8 | 0xC0 | 9);
+ debug(2, "GS Program Change: Drum Map is CM-64/32L");
+
+ }
+
+ // Set Master Chorus to 0. The MT-32 has no chorus capability.
+ memcpy(&buffer[5], "\x40\x01\x3A\x00\x05\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Master Chorus Level is 0");
+
+ // Set Channels 1-16 Reverb to 64, which is the
+ // equivalent of MT-32 default Reverb Level 5
+ for (i = 0; i < 16; ++i)
+ midi->send(( 64 << 16) | (91 << 8) | (0xB0 | i));
+ debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
+
+ // Set Channels 1-16 Pitch Bend Sensitivity to
+ // 12 semitones; then lock the RPN by setting null.
+ for (i = 0; i < 16; ++i) {
+ midi->send(( 0 << 16) | (100 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (101 << 8) | (0xB0 | i));
+ midi->send(( 12 << 16) | (6 << 8) | (0xB0 | i));
+ midi->send(( 0 << 16) | (38 << 8) | (0xB0 | i));
+ midi->send(( 127 << 16) | (100 << 8) | (0xB0 | i));
+ midi->send(( 127 << 16) | (101 << 8) | (0xB0 | i));
+ }
+ debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
+
+ // Set channels 1-16 Mod. LFO1 Pitch Depth to 4
+ memcpy(&buffer[5], "\x40\x20\x04\x04\x18\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x21\x04\x04\x17\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x22\x04\x04\x16\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x23\x04\x04\x15\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x24\x04\x04\x14\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x25\x04\x04\x13\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x26\x04\x04\x12\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x27\x04\x04\x11\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x28\x04\x04\x10\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x29\x04\x04\x0F\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2A\x04\x04\x0E\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2B\x04\x04\x0D\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2C\x04\x04\x0C\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2D\x04\x04\x0B\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2E\x04\x04\x0A\xF7", 6);
+ midi->sysEx(buffer, 11);
+ memcpy(&buffer[5], "\x40\x2F\x04\x04\x09\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
+
+ // Set Percussion Channel Expression to 80
+ midi->getPercussionChannel()->controlChange(11, 80);
+ debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80");
+
+ // Turn off Percussion Channel Rx. Expression so that
+ // Expression cannot be modified. I don't know why, but
+ // Roland does it this way.
+ memcpy(&buffer[5], "\x40\x10\x0E\x00\x22\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
+
+ // Change Reverb Character to 0. I don't think this
+ // sounds most like MT-32, but apparently Roland does.
+ memcpy(&buffer[5], "\x40\x01\x31\x00\x0E\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Character is 0");
+
+ // Change Reverb Pre-LF to 4, which is similar to
+ // what MT-32 reverb does.
+ memcpy(&buffer[5], "\x40\x01\x32\x04\x09\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Pre-LF is 4");
+
+ // Change Reverb Time to 106; the decay on Hall 2
+ // Reverb is too fast compared to the MT-32's
+ memcpy(&buffer[5], "\x40\x01\x34\x6A\x21\xF7", 6);
+ midi->sysEx(buffer, 11);
+ debug(2, "GS SysEx: Reverb Time is 106");
+ }
+}
+
+void IMuseInternal::init_queue() {
+ _queue_adding = false;
+ _queue_pos = 0;
+ _queue_end = 0;
+ _trigger_count = 0;
+}
+
+void IMuseInternal::pause(bool paused) {
+ if (_paused == paused)
+ return;
+ int vol = _music_volume;
+ if (paused)
+ _music_volume = 0;
+ update_volumes();
+ _music_volume = vol;
+
+ // Fix for Bug #817871. The MT-32 apparently fails
+ // sometimes to respond to a channel volume message
+ // (or only uses it for subsequent note events).
+ // The result is hanging notes on pause. Reportedly
+ // happens in the original distro, too. To fix that,
+ // just send AllNotesOff to the channels.
+ if (_midi_native && _native_mt32) {
+ for (int i = 0; i < 16; ++i)
+ _midi_native->send(123 << 8 | 0xB0 | i);
+ }
+
+ _paused = paused;
+}
+
+void IMuseInternal::handleDeferredCommands(MidiDriver *midi) {
+ uint32 advance = midi->getBaseTempo();
+
+ DeferredCommand *ptr = &_deferredCommands[0];
+ int i;
+ for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
+ if (!ptr->time_left)
+ continue;
+ if (ptr->time_left <= advance) {
+ doCommand(ptr->a, ptr->b, ptr->c, ptr->d, ptr->e, ptr->f, 0, 0);
+ ptr->time_left = advance;
+ }
+ ptr->time_left -= advance;
+ }
+}
+
+// "time" is interpreted as hundredths of a second.
+// FIXME: Is that correct?
+// We convert it to microseconds before prceeding
+void IMuseInternal::addDeferredCommand(int time, int a, int b, int c, int d, int e, int f) {
+ DeferredCommand *ptr = &_deferredCommands[0];
+ int i;
+ for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
+ if (!ptr->time_left)
+ break;
+ }
+
+ if (i) {
+ ptr->time_left = time * 10000;
+ ptr->a = a;
+ ptr->b = b;
+ ptr->c = c;
+ ptr->d = d;
+ ptr->e = e;
+ ptr->f = f;
+ }
+}
+
+////////////////////////////////////////////////////////////
+//
+// IMuseInternal load/save implementation
+//
+////////////////////////////////////////////////////////////
+
+int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) {
+ const SaveLoadEntry mainEntries[] = {
+ MKLINE(IMuseInternal, _queue_end, sleUint8, VER(8)),
+ MKLINE(IMuseInternal, _queue_pos, sleUint8, VER(8)),
+ MKLINE(IMuseInternal, _queue_sound, sleUint16, VER(8)),
+ MKLINE(IMuseInternal, _queue_adding, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _queue_marker, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _queue_cleared, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _master_volume, sleByte, VER(8)),
+ MKLINE(IMuseInternal, _trigger_count, sleUint16, VER(8)),
+ MKLINE(IMuseInternal, _snm_trigger_index, sleUint16, VER(54)),
+ MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8, VER(8)),
+ MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry cmdQueueEntries[] = {
+ MKARRAY(CommandQueue, array[0], sleUint16, 8, VER(23)),
+ MKEND()
+ };
+
+ // VolumeFader is obsolete.
+ const SaveLoadEntry volumeFaderEntries[] = {
+ MK_OBSOLETE(VolumeFader, player, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, active, sleUint8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, curvol, sleUint8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo_max, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, num_steps, sleUint16, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_hi, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, direction, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo, sleInt8, VER(8), VER(16)),
+ MK_OBSOLETE(VolumeFader, speed_lo_counter, sleUint16, VER(8), VER(16)),
+ MKEND()
+ };
+
+ const SaveLoadEntry snmTriggerEntries[] = {
+ MKLINE(ImTrigger, sound, sleInt16, VER(54)),
+ MKLINE(ImTrigger, id, sleByte, VER(54)),
+ MKLINE(ImTrigger, expire, sleUint16, VER(54)),
+ MKARRAY(ImTrigger, command[0], sleUint16, 8, VER(54)),
+ MKEND()
+ };
+
+ int i;
+
+ ser->saveLoadEntries(this, mainEntries);
+ ser->saveLoadArrayOf(_cmd_queue, ARRAYSIZE(_cmd_queue), sizeof(_cmd_queue[0]), cmdQueueEntries);
+ ser->saveLoadArrayOf(_snm_triggers, ARRAYSIZE(_snm_triggers), sizeof(_snm_triggers[0]), snmTriggerEntries);
+
+ // The players
+ for (i = 0; i < ARRAYSIZE(_players); ++i)
+ _players[i].saveLoadWithSerializer(ser);
+
+ // The parts
+ for (i = 0; i < ARRAYSIZE(_parts); ++i)
+ _parts[i].saveLoadWithSerializer(ser);
+
+ { // Load/save the instrument definitions, which were revamped with V11.
+ Part *part = &_parts[0];
+ if (ser->getVersion() >= VER(11)) {
+ for (i = ARRAYSIZE(_parts); i; --i, ++part) {
+ part->_instrument.saveOrLoad(ser);
+ }
+ } else {
+ for (i = ARRAYSIZE(_parts); i; --i, ++part)
+ part->_instrument.clear();
+ }
+ }
+
+ // VolumeFader has been replaced with the more generic ParameterFader.
+ // FIXME: replace this loop by something like
+ // if (loading && version <= 16) ser->skip(XXX bytes);
+ for (i = 0; i < 8; ++i)
+ ser->saveLoadEntries(0, volumeFaderEntries);
+
+ if (ser->isLoading()) {
+ // Load all sounds that we need
+ fix_players_after_load(scumm);
+ fix_parts_after_load();
+ setImuseMasterVolume(_master_volume);
+
+ if (_midi_native)
+ reallocateMidiChannels(_midi_native);
+ if (_midi_adlib)
+ reallocateMidiChannels(_midi_adlib);
+ }
+
+ return 0;
+}
+
+void IMuseInternal::fix_parts_after_load() {
+ Part *part;
+ int i;
+
+ for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
+ if (part->_player)
+ part->fix_after_load();
+ }
+}
+
+// Only call this routine from the main thread,
+// since it uses getResourceAddress
+void IMuseInternal::fix_players_after_load(ScummEngine *scumm) {
+ Player *player = _players;
+ int i;
+
+ for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
+ if (player->isActive()) {
+ scumm->getResourceAddress(rtSound, player->getID());
+ player->fixAfterLoad();
+ }
+ }
+}
+
+Part::Part() {
+ _slot = 0;
+ _next = 0;
+ _prev = 0;
+ _mc = 0;
+ _player = 0;
+ _pitchbend = 0;
+ _pitchbend_factor = 0;
+ _transpose = 0;
+ _transpose_eff = 0;
+ _vol = 0;
+ _vol_eff = 0;
+ _detune = 0;
+ _detune_eff = 0;
+ _pan = 0;
+ _pan_eff = 0;
+ _on = false;
+ _modwheel = 0;
+ _pedal = false;
+ _pri = 0;
+ _pri_eff = 0;
+ _chan = 0;
+ _effect_level = 0;
+ _chorus = 0;
+ _percussion = 0;
+ _bank = 0;
+ _unassigned_instrument = false;
+}
+
+void Part::saveLoadWithSerializer(Serializer *ser) {
+ const SaveLoadEntry partEntries[] = {
+ MKLINE(Part, _pitchbend, sleInt16, VER(8)),
+ MKLINE(Part, _pitchbend_factor, sleUint8, VER(8)),
+ MKLINE(Part, _transpose, sleInt8, VER(8)),
+ MKLINE(Part, _vol, sleUint8, VER(8)),
+ MKLINE(Part, _detune, sleInt8, VER(8)),
+ MKLINE(Part, _pan, sleInt8, VER(8)),
+ MKLINE(Part, _on, sleUint8, VER(8)),
+ MKLINE(Part, _modwheel, sleUint8, VER(8)),
+ MKLINE(Part, _pedal, sleUint8, VER(8)),
+ MK_OBSOLETE(Part, _program, sleUint8, VER(8), VER(16)),
+ MKLINE(Part, _pri, sleUint8, VER(8)),
+ MKLINE(Part, _chan, sleUint8, VER(8)),
+ MKLINE(Part, _effect_level, sleUint8, VER(8)),
+ MKLINE(Part, _chorus, sleUint8, VER(8)),
+ MKLINE(Part, _percussion, sleUint8, VER(8)),
+ MKLINE(Part, _bank, sleUint8, VER(8)),
+ MKEND()
+ };
+
+ int num;
+ if (ser->isSaving()) {
+ num = (_next ? (_next - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+
+ num = (_prev ? (_prev - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+
+ num = (_player ? (_player - _se->_players + 1) : 0);
+ ser->saveUint16(num);
+ } else {
+ num = ser->loadUint16();
+ _next = (num ? &_se->_parts[num - 1] : 0);
+
+ num = ser->loadUint16();
+ _prev = (num ? &_se->_parts[num - 1] : 0);
+
+ num = ser->loadUint16();
+ _player = (num ? &_se->_players[num - 1] : 0);
+ }
+ ser->saveLoadEntries(this, partEntries);
+}
+
+void Part::set_detune(int8 detune) {
+ _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::pitchBend(int16 value) {
+ _pitchbend = value;
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::volume(byte value) {
+ _vol_eff = ((_vol = value) + 1) * _player->getEffectiveVolume() >> 7;
+ if (_mc)
+ _mc->volume(_vol_eff);
+}
+
+void Part::set_pri(int8 pri) {
+ _pri_eff = clamp((_pri = pri) + _player->getPriority(), 0, 255);
+ if (_mc)
+ _mc->priority(_pri_eff);
+}
+
+void Part::set_pan(int8 pan) {
+ _pan_eff = clamp((_pan = pan) + _player->getPan(), -64, 63);
+ if (_mc)
+ _mc->panPosition(_pan_eff + 0x40);
+}
+
+void Part::set_transpose(int8 transpose) {
+ _transpose_eff = transpose_clamp((_transpose = transpose) + _player->getTranspose(), -24, 24);
+ if (_mc)
+ sendPitchBend();
+}
+
+void Part::sustain(bool value) {
+ _pedal = value;
+ if (_mc)
+ _mc->sustain(value);
+}
+
+void Part::modulationWheel(byte value) {
+ _modwheel = value;
+ if (_mc)
+ _mc->modulationWheel(value);
+}
+
+void Part::chorusLevel(byte value) {
+ _chorus = value;
+ if (_mc)
+ _mc->chorusLevel(value);
+}
+
+void Part::effectLevel(byte value)
+{
+ _effect_level = value;
+ if (_mc)
+ _mc->effectLevel(value);
+}
+
+void Part::fix_after_load() {
+ set_transpose(_transpose);
+ volume(_vol);
+ set_detune(_detune);
+ set_pri(_pri);
+ set_pan(_pan);
+ sendAll();
+}
+
+void Part::pitchBendFactor(byte value) {
+ if (value > 12)
+ return;
+ pitchBend(0);
+ _pitchbend_factor = value;
+ if (_mc)
+ _mc->pitchBendFactor(value);
+}
+
+void Part::set_onoff(bool on) {
+ if (_on != on) {
+ _on = on;
+ if (!on)
+ off();
+ if (!_percussion)
+ _player->_se->reallocateMidiChannels(_player->getMidiDriver());
+ }
+}
+
+void Part::set_instrument(byte * data) {
+ _instrument.adlib(data);
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::load_global_instrument(byte slot) {
+ _player->_se->copyGlobalAdlibInstrument(slot, &_instrument);
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::noteOn(byte note, byte velocity) {
+ if (!_on)
+ return;
+
+ MidiChannel *mc = _mc;
+
+ // DEBUG
+ if (_unassigned_instrument && !_percussion) {
+ _unassigned_instrument = false;
+ if (!_instrument.isValid()) {
+ debug(0, "[%02d] No instrument specified", (int)_chan);
+ return;
+ }
+ }
+
+ if (mc && _instrument.isValid()) {
+ mc->noteOn(note, velocity);
+ } else if (_percussion) {
+ mc = _player->getMidiDriver()->getPercussionChannel();
+ if (!mc)
+ return;
+ static byte prev_vol_eff = 128;
+ if (_vol_eff != prev_vol_eff){
+ mc->volume(_vol_eff);
+ prev_vol_eff = _vol_eff;
+ }
+ if ((note < 35) && (!_player->_se->isNativeMT32()))
+ note = Instrument::_gmRhythmMap[note];
+
+ mc->noteOn(note, velocity);
+ }
+}
+
+void Part::noteOff(byte note) {
+ if (!_on)
+ return;
+
+ MidiChannel *mc = _mc;
+ if (mc) {
+ mc->noteOff(note);
+ } else if (_percussion) {
+ mc = _player->getMidiDriver()->getPercussionChannel();
+ if (mc)
+ mc->noteOff(note);
+ }
+}
+
+void Part::init() {
+ _player = NULL;
+ _next = NULL;
+ _prev = NULL;
+ _mc = NULL;
+}
+
+void Part::setup(Player *player) {
+ _player = player;
+
+ _percussion = (player->isMIDI() && _chan == 9); // true;
+ _on = true;
+ _pri_eff = player->getPriority();
+ _pri = 0;
+ _vol = 127;
+ _vol_eff = player->getEffectiveVolume();
+ _pan = clamp(player->getPan(), -64, 63);
+ _transpose_eff = player->getTranspose();
+ _transpose = 0;
+ _detune = 0;
+ _detune_eff = player->getDetune();
+ _pitchbend_factor = 2;
+ _pitchbend = 0;
+ _effect_level = 64;
+ _instrument.clear();
+ _unassigned_instrument = true;
+ _chorus = 0;
+ _modwheel = 0;
+ _bank = 0;
+ _pedal = false;
+ _mc = NULL;
+}
+
+void Part::uninit() {
+ if (!_player)
+ return;
+ off();
+ _player->removePart(this);
+ _player = NULL;
+}
+
+void Part::off() {
+ if (_mc) {
+ _mc->allNotesOff();
+ _mc->release();
+ _mc = NULL;
+ }
+}
+
+bool Part::clearToTransmit() {
+ if (_mc)
+ return true;
+ if (_instrument.isValid())
+ _player->_se->reallocateMidiChannels(_player->getMidiDriver());
+ return false;
+}
+
+void Part::sendAll() {
+ if (!clearToTransmit())
+ return;
+ _mc->pitchBendFactor(_pitchbend_factor);
+ sendPitchBend();
+ _mc->volume(_vol_eff);
+ _mc->sustain(_pedal);
+ _mc->modulationWheel(_modwheel);
+ _mc->panPosition(_pan_eff + 0x40);
+ _mc->effectLevel(_effect_level);
+ if (_instrument.isValid())
+ _instrument.send(_mc);
+ _mc->chorusLevel(_chorus);
+ _mc->priority(_pri_eff);
+}
+
+void Part::sendPitchBend() {
+ int16 bend = _pitchbend;
+ // RPN-based pitchbend range doesn't work for the MT32,
+ // so we'll do the scaling ourselves.
+ if (_player->_se->isNativeMT32())
+ bend = bend * _pitchbend_factor / 12;
+ _mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (_transpose_eff * 8192 / 12), -8192, 8191));
+}
+
+void Part::programChange(byte value) {
+ _bank = 0;
+ _instrument.program(value, _player->isMT32());
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::set_instrument(uint b) {
+ _bank = (byte)(b >> 8);
+ if (_bank)
+ error("Non-zero instrument bank selection. Please report this");
+ _instrument.program((byte)b, _player->isMT32());
+ if (clearToTransmit())
+ _instrument.send(_mc);
+}
+
+void Part::allNotesOff() {
+ if (!_mc)
+ return;
+ _mc->allNotesOff();
+}
+
+////////////////////////////////////////
+//
+// Some more IMuseInternal stuff
+//
+////////////////////////////////////////
+
+void IMuseInternal::midiTimerCallback(void *data) {
+ MidiDriver *driver = (MidiDriver *)data;
+ if (g_scumm->_imuse)
+ g_scumm->_imuse->on_timer(driver);
+}
+
+void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
+ Part *part, *hipart;
+ int i;
+ byte hipri, lopri;
+ Part *lopart;
+
+ while (true) {
+ hipri = 0;
+ hipart = NULL;
+ for (i = 32, part = _parts; i; i--, part++) {
+ if (part->_player && part->_player->getMidiDriver() == midi &&
+ !part->_percussion && part->_on &&
+ !part->_mc && part->_pri_eff >= hipri) {
+ hipri = part->_pri_eff;
+ hipart = part;
+ }
+ }
+
+ if (!hipart)
+ return;
+
+ if ((hipart->_mc = midi->allocateChannel()) == NULL) {
+ lopri = 255;
+ lopart = NULL;
+ for (i = 32, part = _parts; i; i--, part++) {
+ if (part->_mc && part->_mc->device() == midi && part->_pri_eff <= lopri) {
+ lopri = part->_pri_eff;
+ lopart = part;
+ }
+ }
+
+ if (lopart == NULL || lopri >= hipri)
+ return;
+ lopart->off();
+
+ if ((hipart->_mc = midi->allocateChannel()) == NULL)
+ return;
+ }
+ hipart->sendAll();
+ }
+}
+
+void IMuseInternal::setGlobalAdlibInstrument(byte slot, byte *data) {
+ if (slot < 32) {
+ _global_adlib_instruments[slot].adlib(data);
+ }
+}
+
+void IMuseInternal::copyGlobalAdlibInstrument(byte slot, Instrument *dest) {
+ if (slot >= 32)
+ return;
+ _global_adlib_instruments[slot].copy_to(dest);
+}
+
+////////////////////////////////////////////////////////////
+//
+// IMuse implementation
+//
+// IMuse actually serves as a concurency monitor front-end
+// to IMuseInternal and ensures that only one thread
+// accesses the object at a time. This is necessary to
+// prevent scripts and the MIDI parser from yanking objects
+// out from underneath each other.
+//
+////////////////////////////////////////////////////////////
+
+IMuse::IMuse(OSystem *system, IMuseInternal *target)
+ : _system(system), _target(target) {
+ _mutex = system->createMutex();
+}
+
+IMuse::~IMuse() {
+ if (_mutex)
+ _system->deleteMutex(_mutex);
+ if (_target)
+ delete _target;
+}
+
+inline void IMuse::in() const {
+ _system->lockMutex(_mutex);
+}
+inline void IMuse::out() const {
+ _system->unlockMutex(_mutex);
+}
+
+void IMuse::on_timer(MidiDriver *midi) { in(); _target->on_timer(midi); out(); }
+void IMuse::pause(bool paused) { in(); _target->pause(paused); out(); }
+int IMuse::save_or_load(Serializer *ser, ScummEngine *scumm) { in(); int ret = _target->save_or_load(ser, scumm); out(); return ret; }
+void IMuse::setMusicVolume(int vol) { in(); _target->setMusicVolume(vol); out(); }
+void IMuse::startSound(int sound) { in(); _target->startSound(sound); out(); }
+void IMuse::stopSound(int sound) { in(); _target->stopSound(sound); out(); }
+void IMuse::stopAllSounds() { in(); _target->stopAllSounds(); out(); }
+int IMuse::getSoundStatus(int sound) const { in(); int ret = _target->getSoundStatus(sound, true); out(); return ret; }
+bool IMuse::get_sound_active(int sound) const { in(); bool ret = _target->getSoundStatus(sound, false) ? 1 : 0; out(); return ret; }
+int IMuse::getMusicTimer() const { in(); int ret = _target->getMusicTimer(); out(); return ret; }
+int32 IMuse::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) { in(); int32 ret = _target->doCommand(a,b,c,d,e,f,g,h); out(); return ret; }
+int32 IMuse::doCommand(int numargs, int args[]) { in(); int32 ret = _target->doCommand(numargs, args); out(); return ret; }
+int IMuse::clear_queue() { in(); int ret = _target->clear_queue(); out(); return ret; }
+void IMuse::setBase(byte **base) { in(); _target->setBase(base); out(); }
+uint32 IMuse::property(int prop, uint32 value) { in(); uint32 ret = _target->property(prop, value); out(); return ret; }
+void IMuse::terminate() { in(); _target->terminate1(); out(); _target->terminate2(); }
+
+// The IMuse::create method provides a front-end factory
+// for creating IMuseInternal without exposing that class
+// to the client.
+IMuse *IMuse::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
+ IMuseInternal *engine = IMuseInternal::create(syst, nativeMidiDriver, adlibMidiDriver);
+ return new IMuse(syst, engine);
+}
+
+} // End of namespace Scumm
Copied: scummvm/trunk/engines/scumm/imuse/imuse.h (from rev 20787, scummvm/trunk/engines/scumm/imuse.h)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse.h (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/imuse.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,85 @@
+/* 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 IMUSE_H
+#define IMUSE_H
+
+#include "common/scummsys.h"
+#include "common/mutex.h"
+#include "scumm/music.h"
+
+class MidiDriver;
+class OSystem;
+
+namespace Scumm {
+
+class IMuseInternal;
+class ScummEngine;
+class Serializer;
+
+class IMuse : public MusicEngine {
+private:
+ OSystem *_system;
+ IMuseInternal *_target;
+ mutable Common::MutexRef _mutex;
+
+ IMuse(OSystem *system, IMuseInternal *target);
+ void in() const;
+ void out() const;
+
+public:
+ ~IMuse();
+
+ enum {
+ PROP_TEMPO_BASE,
+ PROP_NATIVE_MT32,
+ PROP_GS,
+ PROP_LIMIT_PLAYERS,
+ PROP_RECYCLE_PLAYERS,
+ PROP_DIRECT_PASSTHROUGH
+ };
+
+ void on_timer(MidiDriver *midi);
+ void pause(bool paused);
+ int save_or_load(Serializer *ser, ScummEngine *scumm);
+ void setMusicVolume(int vol);
+ void startSound(int sound);
+ void stopSound(int sound);
+ void stopAllSounds();
+ int getSoundStatus(int sound) const;
+ bool get_sound_active(int sound) const;
+ int getMusicTimer() const;
+ int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 doCommand(int numargs, int args[]);
+ int clear_queue();
+ void setBase(byte **base);
+ uint32 property(int prop, uint32 value);
+ void terminate();
+
+ // Factory methods
+ static IMuse *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+};
+
+} // End of namespace Scumm
+
+#endif
Copied: scummvm/trunk/engines/scumm/imuse/imuse_internal.h (from rev 20787, scummvm/trunk/engines/scumm/imuse_internal.h)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse_internal.h (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/imuse_internal.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,472 @@
+/* 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_IMUSE_INTERNAL
+#define DEFINED_IMUSE_INTERNAL
+
+#include "common/scummsys.h"
+#include "scumm/imuse/instrument.h"
+#include "scumm/saveload.h"
+#include "sound/mididrv.h"
+
+class MidiParser;
+class OSystem;
+
+namespace Scumm {
+
+// Unremark this statement to activate some of
+// the most common iMuse diagnostic messages.
+// #define IMUSE_DEBUG
+
+struct ParameterFader;
+struct DeferredCommand;
+struct ImTrigger;
+struct SustainingNotes;
+struct CommandQueue;
+struct IsNoteCmdData;
+class Player;
+struct Part;
+class IMuseInternal;
+
+// Some entities also referenced
+class ScummEngine;
+
+
+
+//////////////////////////////////////////////////
+//
+// Some constants
+//
+//////////////////////////////////////////////////
+
+#define TICKS_PER_BEAT 480
+
+#define TRIGGER_ID 0
+#define COMMAND_ID 1
+
+#define MDPG_TAG "MDpg"
+
+
+////////////////////////////////////////
+//
+// Helper functions
+//
+////////////////////////////////////////
+
+inline int clamp(int val, int min, int max) {
+ if (val < min)
+ return min;
+ if (val > max)
+ return max;
+ return val;
+}
+
+inline int transpose_clamp(int a, int b, int c) {
+ if (b > a)
+ a += (b - a + 11) / 12 * 12;
+ if (c < a)
+ a -= (a - c + 11) / 12 * 12;
+ return a;
+}
+
+
+
+//////////////////////////////////////////////////
+//
+// Entity declarations
+//
+//////////////////////////////////////////////////
+
+struct HookDatas {
+ byte _jump[2];
+ byte _transpose;
+ byte _part_onoff[16];
+ byte _part_volume[16];
+ byte _part_program[16];
+ byte _part_transpose[16];
+
+ int query_param(int param, byte chan);
+ int set(byte cls, byte value, byte chan);
+ HookDatas() { memset(this, 0, sizeof(HookDatas)); }
+};
+
+struct ParameterFader {
+ enum {
+ pfVolume = 1,
+ pfTranspose = 3,
+ pfSpeed = 4
+ };
+
+ int param;
+ int start;
+ int end;
+ uint32 total_time;
+ uint32 current_time;
+
+ ParameterFader() { param = 0; }
+ void init() { param = 0; }
+};
+
+struct DeferredCommand {
+ uint32 time_left;
+ int a, b, c, d, e, f;
+ DeferredCommand() { memset(this, 0, sizeof(DeferredCommand)); }
+};
+
+struct ImTrigger {
+ int sound;
+ byte id;
+ uint16 expire;
+ int command [8];
+ ImTrigger() { memset(this, 0, sizeof(ImTrigger)); }
+};
+
+struct CommandQueue {
+ uint16 array[8];
+ CommandQueue() { memset(this, 0, sizeof(CommandQueue)); }
+};
+
+class Player : public MidiDriver {
+protected:
+ // Moved from IMuseInternal.
+ // This is only used by one player at a time.
+ static uint16 _active_notes[128];
+
+protected:
+ MidiDriver *_midi;
+ MidiParser *_parser;
+ bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
+
+ Part *_parts;
+ bool _active;
+ bool _scanning;
+ int _id;
+ byte _priority;
+ byte _volume;
+ int8 _pan;
+ int8 _transpose;
+ int8 _detune;
+ byte _vol_eff;
+
+ uint _track_index;
+ uint _loop_to_beat;
+ uint _loop_from_beat;
+ uint _loop_counter;
+ uint _loop_to_tick;
+ uint _loop_from_tick;
+ byte _speed;
+ bool _abort;
+
+ // This does not get used by us! It is only
+ // here for save/load purposes, and gets
+ // passed on to the MidiParser during
+ // fixAfterLoad().
+ uint32 _music_tick;
+
+ HookDatas _hook;
+ ParameterFader _parameterFaders[4];
+
+ bool _isMT32;
+ bool _isMIDI;
+
+protected:
+ // Player part
+ void hook_clear();
+ void uninit_parts();
+ byte *parse_midi(byte *s);
+ void part_set_transpose(uint8 chan, byte relative, int8 b);
+ void parse_sysex(byte *p, uint len);
+ void maybe_jump(byte cmd, uint track, uint beat, uint tick);
+ void maybe_set_transpose(byte *data);
+ void maybe_part_onoff(byte *data);
+ void maybe_set_volume(byte *data);
+ void maybe_set_program(byte *data);
+ void maybe_set_transpose_part(byte *data);
+ void turn_off_pedals();
+ int query_part_param(int param, byte chan);
+ void turn_off_parts();
+ void play_active_notes();
+
+ void transitionParameters();
+
+ static void decode_sysex_bytes(const byte *src, byte *dst, int len);
+
+ // Sequencer part
+ int start_seq_sound(int sound, bool reset_vars = true);
+ int query_param(int param);
+
+public:
+ IMuseInternal *_se;
+ uint _vol_chan;
+
+public:
+ Player();
+ virtual ~Player();
+
+ int addParameterFader(int param, int target, int time);
+ void clear();
+ void clearLoop();
+ void fixAfterLoad();
+ Part * getActivePart(uint8 part);
+ uint getBeatIndex();
+ int8 getDetune() const { return _detune; }
+ byte getEffectiveVolume() const { return _vol_eff; }
+ int getID() const { return _id; }
+ MidiDriver *getMidiDriver() const { return _midi; }
+ int getParam(int param, byte chan);
+ int8 getPan() const { return _pan; }
+ Part * getPart(uint8 part);
+ byte getPriority() const { return _priority; }
+ uint getTicksPerBeat() const { return TICKS_PER_BEAT; }
+ int8 getTranspose() const { return _transpose; }
+ byte getVolume() const { return _volume; }
+ bool isActive() const { return _active; }
+ bool isFadingOut() const;
+ bool isMIDI() const { return _isMIDI; }
+ bool isMT32() const { return _isMT32; }
+ bool jump(uint track, uint beat, uint tick);
+ void onTimer();
+ void removePart(Part *part);
+ int scan(uint totrack, uint tobeat, uint totick);
+ void saveLoadWithSerializer(Serializer *ser);
+ int setHook(byte cls, byte value, byte chan) { return _hook.set(cls, value, chan); }
+ void setDetune(int detune);
+ bool setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
+ void setPan(int pan);
+ void setPriority(int pri);
+ void setSpeed(byte speed);
+ int setTranspose(byte relative, int b);
+ int setVolume(byte vol);
+ bool startSound(int sound, MidiDriver *midi, bool passThrough);
+ int getMusicTimer() const;
+
+public:
+ // MidiDriver interface
+ int open() { return 0; }
+ void close() { }
+ void send(uint32 b);
+ const char *getErrorName(int error_code) { return "Unknown"; }
+ void sysEx(byte *msg, uint16 length);
+ void metaEvent(byte type, byte *data, uint16 length);
+ void setTimerCallback(void *timer_param, void(*timer_proc)(void *)) { }
+ uint32 getBaseTempo();
+ MidiChannel *allocateChannel() { return 0; }
+ MidiChannel *getPercussionChannel() { return 0; }
+};
+
+struct Part : public Serializable {
+ IMuseInternal *_se;
+ int _slot;
+ Part *_next, *_prev;
+ MidiChannel *_mc;
+ Player *_player;
+ int16 _pitchbend;
+ byte _pitchbend_factor;
+ int8 _transpose, _transpose_eff;
+ byte _vol, _vol_eff;
+ int8 _detune, _detune_eff;
+ int8 _pan, _pan_eff;
+ bool _on;
+ byte _modwheel;
+ bool _pedal;
+ int8 _pri;
+ byte _pri_eff;
+ byte _chan;
+ byte _effect_level;
+ byte _chorus;
+ byte _percussion;
+ byte _bank;
+
+ // New abstract instrument definition
+ Instrument _instrument;
+ bool _unassigned_instrument; // For diagnostic reporting purposes only
+
+ // MidiChannel interface
+ // (We don't currently derive from MidiChannel,
+ // but if we ever do, this will make it easy.)
+ void noteOff(byte note);
+ void noteOn(byte note, byte velocity);
+ void programChange(byte value);
+ void pitchBend(int16 value);
+ void modulationWheel(byte value);
+ void volume(byte value);
+ void pitchBendFactor(byte value);
+ void sustain(bool value);
+ void effectLevel(byte value);
+ void chorusLevel(byte value);
+ void allNotesOff();
+
+ void set_param(byte param, int value) { }
+ void init();
+ void setup(Player *player);
+ void uninit();
+ void off();
+ void set_instrument(uint b);
+ void set_instrument(byte *data);
+ void load_global_instrument(byte b);
+
+ void set_transpose(int8 transpose);
+ void set_detune(int8 detune);
+ void set_pri(int8 pri);
+ void set_pan(int8 pan);
+
+ void set_onoff(bool on);
+ void fix_after_load();
+
+ void sendAll();
+ void sendPitchBend();
+ bool clearToTransmit();
+
+ Part();
+
+ void saveLoadWithSerializer(Serializer *ser);
+};
+
+// WARNING: This is the internal variant of the IMUSE class.
+// imuse.h contains a public version of the same class.
+// the public version, only contains a set of methods.
+class IMuseInternal {
+ friend class Player;
+ friend struct Part;
+
+protected:
+ bool _native_mt32;
+ bool _enable_gs;
+ bool _sc55;
+ MidiDriver *_midi_adlib;
+ MidiDriver *_midi_native;
+
+ byte **_base_sounds;
+
+protected:
+ bool _paused;
+ bool _initialized;
+
+ int _tempoFactor;
+
+ int _player_limit; // Limits how many simultaneous music tracks are played
+ bool _recycle_players; // Can we stop a player in order to start another one?
+ bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
+
+ uint _queue_end, _queue_pos, _queue_sound;
+ byte _queue_adding;
+
+ byte _queue_marker;
+ byte _queue_cleared;
+ byte _master_volume; // Master volume. 0-255
+ byte _music_volume; // Global music volume. 0-255
+
+ uint16 _trigger_count;
+ ImTrigger _snm_triggers[16]; // Sam & Max triggers
+ uint16 _snm_trigger_index;
+
+ uint16 _channel_volume[8];
+ uint16 _channel_volume_eff[8]; // No Save
+ uint16 _volchan_table[8];
+
+ Player _players[8];
+ Part _parts[32];
+
+ Instrument _global_adlib_instruments[32];
+ CommandQueue _cmd_queue[64];
+ DeferredCommand _deferredCommands[4];
+
+protected:
+ byte *findStartOfSound(int sound);
+ bool isMT32(int sound);
+ bool isMIDI(int sound);
+ int get_queue_sound_status(int sound) const;
+ void handle_marker(uint id, byte data);
+ int get_channel_volume(uint a);
+ void initMidiDriver(MidiDriver *midi);
+ void initGM(MidiDriver *midi);
+ void initMT32(MidiDriver *midi);
+ void init_players();
+ void init_parts();
+ void init_queue();
+
+ void sequencer_timers(MidiDriver *midi);
+
+ MidiDriver *getBestMidiDriver(int sound);
+ Player *allocate_player(byte priority);
+ Part *allocate_part(byte pri, MidiDriver *midi);
+
+ int32 ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 ImClearTrigger(int sound, int id);
+ int32 ImFireAllTriggers(int sound);
+
+ void addDeferredCommand(int time, int a, int b, int c, int d, int e, int f);
+ void handleDeferredCommands(MidiDriver *midi);
+
+ int enqueue_command(int a, int b, int c, int d, int e, int f, int g);
+ int enqueue_trigger(int sound, int marker);
+ int query_queue(int param);
+ Player *findActivePlayer(int id);
+
+ int get_volchan_entry(uint a);
+ int set_volchan_entry(uint a, uint b);
+ int set_channel_volume(uint chan, uint vol);
+ void update_volumes();
+ void reset_tick();
+
+ int set_volchan(int sound, int volchan);
+
+ void fix_parts_after_load();
+ void fix_players_after_load(ScummEngine *scumm);
+
+ static void midiTimerCallback(void *data);
+
+public:
+ IMuseInternal();
+
+ int initialize(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+ void reallocateMidiChannels(MidiDriver *midi);
+ void setGlobalAdlibInstrument(byte slot, byte *data);
+ void copyGlobalAdlibInstrument(byte slot, Instrument *dest);
+ bool isNativeMT32() { return _native_mt32; }
+
+ // IMuse interface
+
+ void on_timer(MidiDriver *midi);
+ void pause(bool paused);
+ int terminate1();
+ int terminate2();
+ int save_or_load(Serializer *ser, ScummEngine *scumm);
+ int setMusicVolume(uint vol);
+ int setImuseMasterVolume(uint vol);
+ bool startSound(int sound);
+ int stopSound(int sound);
+ int stopAllSounds();
+ int getSoundStatus(int sound, bool ignoreFadeouts = true) const;
+ int getMusicTimer() const;
+ int32 doCommand (int a, int b, int c, int d, int e, int f, int g, int h);
+ int32 doCommand (int numargs, int args[]);
+ int clear_queue();
+ void setBase(byte **base);
+ uint32 property(int prop, uint32 value);
+
+ static IMuseInternal *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
+};
+
+} // End of namespace Scumm
+
+#endif
Copied: scummvm/trunk/engines/scumm/imuse/imuse_player.cpp (from rev 20795, scummvm/trunk/engines/scumm/imuse_player.cpp)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/imuse_player.cpp (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/imuse_player.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,1241 @@
+/* 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"
+#include "base/engine.h"
+
+#include "scumm/imuse/imuse_internal.h"
+#include "scumm/saveload.h"
+#include "scumm/scumm.h"
+
+#include "sound/midiparser.h"
+
+namespace Scumm {
+
+////////////////////////////////////////
+//
+// Miscellaneous
+//
+////////////////////////////////////////
+
+#define IMUSE_SYSEX_ID 0x7D
+#define YM2612_SYSEX_ID 0x7C
+#define ROLAND_SYSEX_ID 0x41
+#define PERCUSSION_CHANNEL 9
+
+extern MidiParser *MidiParser_createRO();
+extern MidiParser *MidiParser_createEUP();
+
+uint16 Player::_active_notes[128];
+
+
+
+//////////////////////////////////////////////////
+//
+// IMuse Player implementation
+//
+//////////////////////////////////////////////////
+
+Player::Player() :
+ _midi(0),
+ _parser(0),
+ _passThrough(0),
+ _parts(0),
+ _active(false),
+ _scanning(false),
+ _id(0),
+ _priority(0),
+ _volume(0),
+ _pan(0),
+ _transpose(0),
+ _detune(0),
+ _vol_eff(0),
+ _track_index(0),
+ _loop_to_beat(0),
+ _loop_from_beat(0),
+ _loop_counter(0),
+ _loop_to_tick(0),
+ _loop_from_tick(0),
+ _speed(128),
+ _isMT32(false),
+ _isMIDI(false),
+ _se(0),
+ _vol_chan(0){
+}
+
+Player::~Player() {
+ if (_parser) {
+ delete _parser;
+ _parser = 0;
+ }
+}
+
+bool Player::startSound(int sound, MidiDriver *midi, bool passThrough) {
+ void *ptr;
+ int i;
+
+ // Not sure what the old code was doing,
+ // but we'll go ahead and do a similar check.
+ ptr = _se->findStartOfSound(sound);
+ if (!ptr) {
+ error("Player::startSound(): Couldn't find start of sound %d!", sound);
+ return false;
+ }
+
+ _isMT32 = _se->isMT32(sound);
+ _isMIDI = _se->isMIDI(sound);
+
+ _parts = NULL;
+ _active = true;
+ _midi = midi;
+ _id = sound;
+ _priority = 0x80;
+ _volume = 0x7F;
+ _vol_chan = 0xFFFF;
+ _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
+ _pan = 0;
+ _transpose = 0;
+ _detune = 0;
+ _passThrough = passThrough;
+
+ for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i)
+ _parameterFaders[i].init();
+ hook_clear();
+
+ if (start_seq_sound(sound) != 0) {
+ _active = false;
+ _midi = NULL;
+ return false;
+ }
+
+#ifdef IMUSE_DEBUG
+ debug(0, "Starting music %d", sound);
+#endif
+ return true;
+}
+
+int Player::getMusicTimer() const {
+ return _parser ? (_parser->getTick() * 2 / _parser->getPPQN()) : 0;
+}
+
+bool Player::isFadingOut() const {
+ int i;
+ for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) {
+ if (_parameterFaders[i].param == ParameterFader::pfVolume &&
+ _parameterFaders[i].end == 0) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void Player::clear() {
+ if (!_active)
+ return;
+
+#ifdef IMUSE_DEBUG
+ debug(0, "Stopping music %d", _id);
+#endif
+
+ if (_parser) {
+ _parser->unloadMusic();
+ delete _parser;
+ _parser = 0;
+ }
+ uninit_parts();
+ _se->ImFireAllTriggers(_id);
+ _active = false;
+ _midi = NULL;
+ _id = 0;
+}
+
+void Player::hook_clear() {
+ memset(&_hook, 0, sizeof(_hook));
+}
+
+int Player::start_seq_sound(int sound, bool reset_vars) {
+ byte *ptr;
+
+ if (reset_vars) {
+ _loop_to_beat = 1;
+ _loop_from_beat = 1;
+ _track_index = 0;
+ _loop_counter = 0;
+ _loop_to_tick = 0;
+ _loop_from_tick = 0;
+ }
+
+ ptr = _se->findStartOfSound(sound);
+ if (ptr == NULL)
+ return -1;
+ if (_parser)
+ delete _parser;
+
+ if (!memcmp(ptr, "RO", 2)) {
+ // Old style 'RO' resource
+ _parser = MidiParser_createRO();
+ } else if (!memcmp(ptr, "SO", 2)) {
+ // Euphony (FM-TOWNS) resource
+ _parser = MidiParser_createEUP();
+ } else if (!memcmp(ptr, "FORM", 4)) {
+ // Humongous Games XMIDI resource
+ _parser = MidiParser::createParser_XMIDI();
+ } else {
+ // SCUMM SMF resource
+ _parser = MidiParser::createParser_SMF();
+ }
+
+ _parser->setMidiDriver(this);
+ _parser->property(MidiParser::mpSmartJump, 1);
+ _parser->loadMusic(ptr, 0);
+ _parser->setTrack(_track_index);
+ setSpeed(reset_vars ? 128 : _speed);
+
+ return 0;
+}
+
+void Player::uninit_parts() {
+ if (_parts && _parts->_player != this)
+ error("asd");
+ while (_parts)
+ _parts->uninit();
+
+ // In case another player is waiting to allocate parts
+ if (_midi)
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::setSpeed(byte speed) {
+ _speed = speed;
+ if (_parser)
+ _parser->setTimerRate(((_midi->getBaseTempo() * speed) >> 7) * _se->_tempoFactor / 100);
+}
+
+void Player::send(uint32 b) {
+ if (_passThrough) {
+ _midi->send(b);
+ return;
+ }
+
+ byte cmd = (byte)(b & 0xF0);
+ byte chan = (byte)(b & 0x0F);
+ byte param1 = (byte)((b >> 8) & 0xFF);
+ byte param2 = (byte)((b >> 16) & 0xFF);
+ Part *part;
+
+ switch (cmd >> 4) {
+ case 0x8: // Key Off
+ if (!_scanning) {
+ if ((part = getPart(chan)) != 0)
+ part->noteOff(param1);
+ } else {
+ _active_notes[param1] &= ~(1 << chan);
+ }
+ break;
+
+ case 0x9: // Key On
+ if (!_scanning) {
+ if (_isMT32 && !_se->isNativeMT32())
+ param2 = (((param2 * 3) >> 2) + 32) & 0x7F;
+ if ((part = getPart(chan)) != 0)
+ part->noteOn(param1, param2);
+ } else {
+ _active_notes[param1] |= (1 << chan);
+ }
+ break;
+
+ case 0xB: // Control Change
+ part = (param1 == 123 ? getActivePart(chan) : getPart(chan));
+ if (!part)
+ break;
+
+ switch (param1) {
+ case 0: // Bank select. Not supported
+ break;
+ case 1: // Modulation Wheel
+ part->modulationWheel(param2);
+ break;
+ case 7: // Volume
+ part->volume(param2);
+ break;
+ case 10: // Pan Position
+ part->set_pan(param2 - 0x40);
+ break;
+ case 16: // Pitchbend Factor(non-standard)
+ part->pitchBendFactor(param2);
+ break;
+ case 17: // GP Slider 2
+ part->set_detune(param2 - 0x40);
+ break;
+ case 18: // GP Slider 3
+ part->set_pri(param2 - 0x40);
+ _se->reallocateMidiChannels(_midi);
+ break;
+ case 64: // Sustain Pedal
+ part->sustain(param2 != 0);
+ break;
+ case 91: // Effects Level
+ part->effectLevel(param2);
+ break;
+ case 93: // Chorus Level
+ part->chorusLevel(param2);
+ break;
+ case 116: // XMIDI For Loop. Not supported
+ // Used in the ending sequence of puttputt
+ break;
+ case 117: // XMIDI Next/Break. Not supported
+ // Used in the ending sequence of puttputt
+ break;
+ case 123: // All Notes Off
+ part->allNotesOff();
+ break;
+ default:
+ error("Player::send(): Invalid control change %d", param1);
+ }
+ break;
+
+ case 0xC: // Program Change
+ part = getPart(chan);
+ if (part) {
+ if (_isMIDI) {
+ if (param1 < 128)
+ part->programChange(param1);
+ } else {
+ if (param1 < 32)
+ part->load_global_instrument(param1);
+ }
+ }
+ break;
+
+ case 0xE: // Pitch Bend
+ part = getPart(chan);
+ if (part)
+ part->pitchBend(((param2 << 7) | param1) - 0x2000);
+ break;
+
+ case 0xA: // Aftertouch
+ case 0xD: // Channel Pressure
+ case 0xF: // Sequence Controls
+ break;
+
+ default:
+ if (!_scanning) {
+ error("Player::send(): Invalid command %d", cmd);
+ clear();
+ }
+ }
+ return;
+}
+
+void Player::sysEx(byte *p, uint16 len) {
+ byte code;
+ byte a;
+ uint b;
+ byte buf[128];
+ Part *part;
+
+ if (_passThrough) {
+ _midi->sysEx(p, len);
+ return;
+ }
+
+ // Check SysEx manufacturer.
+ a = *p++;
+ --len;
+ if (a != IMUSE_SYSEX_ID) {
+ if (a == ROLAND_SYSEX_ID) {
+ // Roland custom instrument definition.
+ part = getPart(p[0] & 0x0F);
+ if (part) {
+ part->_instrument.roland(p - 1);
+ if (part->clearToTransmit())
+ part->_instrument.send(part->_mc);
+ }
+ } else if (a == YM2612_SYSEX_ID) {
+ // FM-TOWNS custom instrument definition
+ _midi->sysEx_customInstrument(p[0], 'EUP ', p + 1);
+ } else {
+ error("Unknown SysEx manufacturer 0x%02X", (int)a);
+ }
+ return;
+ }
+ --len;
+
+ // Too big?
+ if (len >= sizeof(buf) * 2)
+ return;
+
+#ifdef IMUSE_DEBUG
+ if (!_scanning) {
+ for (a = 0; a < len + 1 && a < 19; ++a) {
+ sprintf((char *)&buf[a*3], " %02X", p[a]);
+ } // next for
+ if (a < len + 1) {
+ buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.';
+ ++a;
+ } // end if
+ buf[a*3] = '\0';
+ debug(0, "[%02d] SysEx:%s", _id, buf);
+ }
+#endif
+
+ switch (code = *p++) {
+ case 0:
+ if (g_scumm->_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)
+ // BYTE 04: Priority adjustment [guessing]
+ // BYTE 05: Volume(upper 4 bits) [guessing]
+ // BYTE 06: Volume(lower 4 bits) [guessing]
+ // BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
+ // 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->set_pri(p[4]);
+ part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
+ part->_percussion = _isMIDI ? ((p[9] & 0x08) > 0) : false;
+ 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:
+ // This SysEx is used in Sam & Max for 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);
+ }
+}
+
+void Player::decode_sysex_bytes(const byte *src, byte *dst, int len) {
+ while (len >= 0) {
+ *dst++ = ((src[0] << 4)&0xFF) | (src[1] & 0xF);
+ src += 2;
+ len -= 2;
+ }
+}
+
+void Player::maybe_jump(byte cmd, uint track, uint beat, uint tick) {
+ // Is this the hook I'm waiting for?
+ if (cmd && _hook._jump[0] != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80) {
+ _hook._jump[0] = _hook._jump[1];
+ _hook._jump[1] = 0;
+ }
+
+ jump(track, beat, tick);
+}
+
+void Player::maybe_set_transpose(byte *data) {
+ byte cmd;
+
+ cmd = data[0];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && _hook._transpose != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ _hook._transpose = 0;
+
+ setTranspose(data[1], (int8)data[2]);
+}
+
+void Player::maybe_part_onoff(byte *data) {
+ byte cmd, *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ p = &_hook._part_onoff[chan];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && *p != cmd)
+ return;
+
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->set_onoff(data[2] != 0);
+}
+
+void Player::maybe_set_volume(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ p = &_hook._part_volume[chan];
+
+ // Is this the hook I'm waiting for?
+ if (cmd && *p != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->volume(data[2]);
+}
+
+void Player::maybe_set_program(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+ Part *part;
+
+ cmd = data[1];
+ chan = data[0];
+
+ // Is this the hook I'm waiting for?
+ p = &_hook._part_program[chan];
+
+ if (cmd && *p != cmd)
+ return;
+
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part = getPart(chan);
+ if (part)
+ part->programChange(data[2]);
+}
+
+void Player::maybe_set_transpose_part(byte *data) {
+ byte cmd;
+ byte *p;
+ uint chan;
+
+ cmd = data[1];
+ chan = data[0];
+
+ // Is this the hook I'm waiting for?
+ p = &_hook._part_transpose[chan];
+
+ if (cmd && *p != cmd)
+ return;
+
+ // Reset hook?
+ if (cmd != 0 && cmd < 0x80)
+ *p = 0;
+
+ part_set_transpose(chan, data[2], (int8)data[3]);
+}
+
+int Player::setTranspose(byte relative, int b) {
+ Part *part;
+
+ if (b > 24 || b < -24 || relative > 1)
+ return -1;
+ if (relative)
+ b = transpose_clamp(_transpose + b, -24, 24);
+
+ _transpose = b;
+
+ for (part = _parts; part; part = part->_next) {
+ part->set_transpose(part->_transpose);
+ }
+
+ return 0;
+}
+
+void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
+ Part *part;
+
+ if (b > 24 || b < -24)
+ return;
+
+ part = getPart(chan);
+ if (!part)
+ return;
+ if (relative)
+ b = transpose_clamp(b + part->_transpose, -7, 7);
+ part->set_transpose(b);
+}
+
+bool Player::jump(uint track, uint beat, uint tick) {
+ if (!_parser)
+ return false;
+ if (_parser->setTrack(track))
+ _track_index = track;
+ if (!_parser->jumpToTick((beat - 1) * TICKS_PER_BEAT + tick))
+ return false;
+ turn_off_pedals();
+ return true;
+}
+
+bool Player::setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
+ if (tobeat + 1 >= frombeat)
+ return false;
+
+ if (tobeat == 0)
+ tobeat = 1;
+
+ _loop_counter = 0; // Because of possible interrupts
+ _loop_to_beat = tobeat;
+ _loop_to_tick = totick;
+ _loop_from_beat = frombeat;
+ _loop_from_tick = fromtick;
+ _loop_counter = count;
+
+ return true;
+}
+
+void Player::clearLoop() {
+ _loop_counter = 0;
+}
+
+void Player::turn_off_pedals() {
+ Part *part;
+
+ for (part = _parts; part; part = part->_next) {
+ if (part->_pedal)
+ part->sustain(false);
+ }
+}
+
+Part *Player::getActivePart(uint8 chan) {
+ Part *part = _parts;
+ while (part) {
+ if (part->_chan == chan)
+ return part;
+ part = part->_next;
+ }
+ return 0;
+}
+
+Part *Player::getPart(uint8 chan) {
+ Part *part = getActivePart(chan);
+ if (part)
+ return part;
+
+ part = _se->allocate_part(_priority, _midi);
+ if (!part) {
+ debug(1, "No parts available");
+ return NULL;
+ }
+
+ // Insert part into front of parts list
+ part->_prev = NULL;
+ part->_next = _parts;
+ if (_parts)
+ _parts->_prev = part;
+ _parts = part;
+
+
+ part->_chan = chan;
+ part->setup(this);
+
+ return part;
+}
+
+void Player::setPriority(int pri) {
+ Part *part;
+
+ _priority = pri;
+ for (part = _parts; part; part = part->_next) {
+ part->set_pri(part->_pri);
+ }
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::setPan(int pan) {
+ Part *part;
+
+ _pan = pan;
+ for (part = _parts; part; part = part->_next) {
+ part->set_pan(part->_pan);
+ }
+}
+
+void Player::setDetune(int detune) {
+ Part *part;
+
+ _detune = detune;
+ for (part = _parts; part; part = part->_next) {
+ part->set_detune(part->_detune);
+ }
+}
+
+int Player::scan(uint totrack, uint tobeat, uint totick) {
+ if (!_active || !_parser)
+ return -1;
+
+ if (tobeat == 0)
+ tobeat++;
+
+ turn_off_parts();
+ memset(_active_notes, 0, sizeof(_active_notes));
+ _scanning = true;
+
+ // If the scan involves a track switch, scan to the end of
+ // the current track so that our state when starting the
+ // new track is fully up to date.
+ if (totrack != _track_index)
+ _parser->jumpToTick((uint32)-1, true);
+ _parser->setTrack(totrack);
+ if (!_parser->jumpToTick((tobeat - 1) * TICKS_PER_BEAT + totick, true)) {
+ _scanning = false;
+ return -1;
+ }
+
+ _scanning = false;
+ _se->reallocateMidiChannels(_midi);
+ play_active_notes();
+
+ if (_track_index != totrack) {
+ _track_index = totrack;
+ _loop_counter = 0;
+ }
+ return 0;
+}
+
+void Player::turn_off_parts() {
+ Part *part;
+
+ for (part = _parts; part; part = part->_next)
+ part->off();
+ _se->reallocateMidiChannels(_midi);
+}
+
+void Player::play_active_notes() {
+ int i, j;
+ uint mask;
+ Part *part;
+
+ for (i = 0; i < 16; ++i) {
+ part = getPart(i);
+ if (part) {
+ mask = 1 << i;
+ for (j = 0; j < 128; ++j) {
+ if (_active_notes[j] & mask)
+ part->noteOn(j, 80);
+ }
+ }
+ }
+}
+
+int Player::setVolume(byte vol) {
+ Part *part;
+
+ if (vol > 127)
+ return -1;
+
+ _volume = vol;
+ _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
+
+ for (part = _parts; part; part = part->_next) {
+ part->volume(part->_vol);
+ }
+
+ return 0;
+}
+
+int Player::getParam(int param, byte chan) {
+ switch (param) {
+ case 0:
+ return (byte)_priority;
+ case 1:
+ return (byte)_volume;
+ case 2:
+ return (byte)_pan;
+ case 3:
+ return (byte)_transpose;
+ case 4:
+ return (byte)_detune;
+ case 5:
+ return _speed;
+ case 6:
+ return _track_index;
+ case 7:
+ return getBeatIndex();
+ case 8:
+ return (_parser ? _parser->getTick() % TICKS_PER_BEAT : 0); // _tick_index;
+ case 9:
+ return _loop_counter;
+ case 10:
+ return _loop_to_beat;
+ case 11:
+ return _loop_to_tick;
+ case 12:
+ return _loop_from_beat;
+ case 13:
+ return _loop_from_tick;
+ case 14:
+ case 15:
+ case 16:
+ case 17:
+ return query_part_param(param, chan);
+ case 18:
+ case 19:
+ case 20:
+ case 21:
+ case 22:
+ case 23:
+ return _hook.query_param(param, chan);
+ default:
+ return -1;
+ }
+}
+
+int Player::query_part_param(int param, byte chan) {
+ Part *part;
+
+ part = _parts;
+ while (part) {
+ if (part->_chan == chan) {
+ switch (param) {
+ case 14:
+ return part->_on;
+ case 15:
+ return part->_vol;
+ case 16:
+// FIXME: Need to know where this occurs...
+error("Trying to cast instrument (%d, %d) -- please tell Fingolfin\n", param, chan);
+// In old versions of the code, this used to return part->_program.
+// This was changed in revision 2.29 of imuse.cpp (where this code used
+// to reside).
+// return (int)part->_instrument;
+ case 17:
+ return part->_transpose;
+ default:
+ return -1;
+ }
+ }
+ part = part->_next;
+ }
+ return 129;
+}
+
+void Player::onTimer() {
+ // First handle any parameter transitions
+ // that are occuring.
+ transitionParameters();
+
+ // Since the volume parameter can cause
+ // the player to be deactivated, check
+ // to make sure we're still active.
+ if (!_active || !_parser)
+ return;
+
+ uint32 target_tick = _parser->getTick();
+ uint beat_index = target_tick / TICKS_PER_BEAT + 1;
+ uint tick_index = target_tick % TICKS_PER_BEAT;
+
+ if (_loop_counter &&(beat_index > _loop_from_beat ||
+ (beat_index == _loop_from_beat && tick_index >= _loop_from_tick)))
+ {
+ _loop_counter--;
+ jump(_track_index, _loop_to_beat, _loop_to_tick);
+ }
+ _parser->onTimer();
+}
+
+// "time" is referenced as hundredths of a second.
+// IS THAT CORRECT??
+// We convert it to microseconds before proceeding
+int Player::addParameterFader(int param, int target, int time) {
+ int start;
+
+ switch (param) {
+ case ParameterFader::pfVolume:
+ // HACK: If volume is set to 0 with 0 time,
+ // set it so immediately but DON'T clear
+ // the player. This fixes a problem with
+ // music being cleared inappropriately
+ // in S&M when playing with the Dinosaur.
+ if (!target && !time) {
+ setVolume(0);
+ return 0;
+ }
+
+ // Volume fades are handled differently.
+ start = _volume;
+ break;
+
+ case ParameterFader::pfTranspose:
+ // FIXME: Is this transpose? And what's the scale?
+ // It's set to fade to -2400 in the tunnel of love.
+// debug(0, "parameterTransition(3) outside Tunnel of Love?");
+ start = _transpose;
+// target /= 200;
+ break;
+
+ case ParameterFader::pfSpeed: // impSpeed
+ // FIXME: Is the speed from 0-100?
+ // Right now I convert it to 0-128.
+ start = _speed;
+// target = target * 128 / 100;
+ break;
+
+ case 127:
+ { // FIXME? I *think* this clears all parameter faders.
+ ParameterFader *ptr = &_parameterFaders[0];
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr)
+ ptr->param = 0;
+ return 0;
+ }
+ break;
+
+ default:
+ debug(0, "Player::addParameterFader (%d, %d, %d): Unknown parameter", param, target, time);
+ return 0; // Should be -1, but we'll let the script think it worked.
+ }
+
+ ParameterFader *ptr = &_parameterFaders[0];
+ ParameterFader *best = 0;
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
+ if (ptr->param == param) {
+ best = ptr;
+ start = ptr->end;
+ break;
+ } else if (!ptr->param) {
+ best = ptr;
+ }
+ }
+
+ if (best) {
+ best->param = param;
+ best->start = start;
+ best->end = target;
+ if (!time)
+ best->total_time = 1;
+ else
+ best->total_time = (uint32)time * 10000;
+ best->current_time = 0;
+ } else {
+ debug(0, "IMuse Player %d: Out of parameter faders", _id);
+ return -1;
+ }
+
+ return 0;
+}
+
+void Player::transitionParameters() {
+ uint32 advance = _midi->getBaseTempo();
+ int value;
+
+ ParameterFader *ptr = &_parameterFaders[0];
+ int i;
+ for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
+ if (!ptr->param)
+ continue;
+
+ ptr->current_time += advance;
+ if (ptr->current_time > ptr->total_time)
+ ptr->current_time = ptr->total_time;
+ value = (int32)ptr->start + (int32)(ptr->end - ptr->start) * (int32)ptr->current_time / (int32)ptr->total_time;
+
+ switch (ptr->param) {
+ case ParameterFader::pfVolume:
+ // Volume.
+ if (!value && !ptr->end) {
+ clear();
+ return;
+ }
+ setVolume((byte)value);
+ break;
+
+ case ParameterFader::pfTranspose:
+ // FIXME: Is this really transpose?
+ setTranspose(0, value / 100);
+ setDetune(value % 100);
+ break;
+
+ case ParameterFader::pfSpeed: // impSpeed:
+ // Speed.
+ setSpeed((byte)value);
+ break;
+
+ default:
+ ptr->param = 0;
+ }
+
+ if (ptr->current_time >= ptr->total_time)
+ ptr->param = 0;
+ }
+}
+
+uint Player::getBeatIndex() {
+ return (_parser ? (_parser->getTick() / TICKS_PER_BEAT + 1) : 0);
+}
+
+void Player::removePart(Part *part) {
+ // Unlink
+ if (part->_next)
+ part->_next->_prev = part->_prev;
+ if (part->_prev)
+ part->_prev->_next = part->_next;
+ else
+ _parts = part->_next;
+ part->_next = part->_prev = 0;
+}
+
+void Player::fixAfterLoad() {
+ _midi = _se->getBestMidiDriver(_id);
+ if (!_midi) {
+ clear();
+ } else {
+ start_seq_sound(_id, false);
+ setSpeed(_speed);
+ if (_parser)
+ _parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks
+ _isMT32 = _se->isMT32(_id);
+ _isMIDI = _se->isMIDI(_id);
+ }
+}
+
+uint32 Player::getBaseTempo() {
+ return (_midi ? _midi->getBaseTempo() : 0);
+}
+
+void Player::metaEvent(byte type, byte *msg, uint16 len) {
+ if (type == 0x2F)
+ clear();
+}
+
+
+
+////////////////////////////////////////
+//
+// Player save/load functions
+//
+////////////////////////////////////////
+
+void Player::saveLoadWithSerializer(Serializer *ser) {
+ static const SaveLoadEntry playerEntries[] = {
+ MKLINE(Player, _active, sleByte, VER(8)),
+ MKLINE(Player, _id, sleUint16, VER(8)),
+ MKLINE(Player, _priority, sleByte, VER(8)),
+ MKLINE(Player, _volume, sleByte, VER(8)),
+ MKLINE(Player, _pan, sleInt8, VER(8)),
+ MKLINE(Player, _transpose, sleByte, VER(8)),
+ MKLINE(Player, _detune, sleInt8, VER(8)),
+ MKLINE(Player, _vol_chan, sleUint16, VER(8)),
+ MKLINE(Player, _vol_eff, sleByte, VER(8)),
+ MKLINE(Player, _speed, sleByte, VER(8)),
+ MK_OBSOLETE(Player, _song_index, sleUint16, VER(8), VER(19)),
+ MKLINE(Player, _track_index, sleUint16, VER(8)),
+ MK_OBSOLETE(Player, _timer_counter, sleUint16, VER(8), VER(17)),
+ MKLINE(Player, _loop_to_beat, sleUint16, VER(8)),
+ MKLINE(Player, _loop_from_beat, sleUint16, VER(8)),
+ MKLINE(Player, _loop_counter, sleUint16, VER(8)),
+ MKLINE(Player, _loop_to_tick, sleUint16, VER(8)),
+ MKLINE(Player, _loop_from_tick, sleUint16, VER(8)),
+ MK_OBSOLETE(Player, _tempo, sleUint32, VER(8), VER(19)),
+ MK_OBSOLETE(Player, _cur_pos, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _next_pos, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _song_offset, sleUint32, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _tick_index, sleUint16, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _beat_index, sleUint16, VER(8), VER(17)),
+ MK_OBSOLETE(Player, _ticks_per_beat, sleUint16, VER(8), VER(17)),
+ MKLINE(Player, _music_tick, sleUint32, VER(19)),
+ MKLINE(Player, _hook._jump[0], sleByte, VER(8)),
+ MKLINE(Player, _hook._transpose, sleByte, VER(8)),
+ MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER(8)),
+ MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER(8)),
+ MKEND()
+ };
+
+ const SaveLoadEntry parameterFaderEntries[] = {
+ MKLINE(ParameterFader, param, sleInt16, VER(17)),
+ MKLINE(ParameterFader, start, sleInt16, VER(17)),
+ MKLINE(ParameterFader, end, sleInt16, VER(17)),
+ MKLINE(ParameterFader, total_time, sleUint32, VER(17)),
+ MKLINE(ParameterFader, current_time, sleUint32, VER(17)),
+ MKEND()
+ };
+
+ if (!ser->isSaving() && _parser) {
+ delete _parser;
+ _parser = 0;
+ }
+ _music_tick = _parser ? _parser->getTick() : 0;
+
+ int num;
+ if (ser->isSaving()) {
+ num = (_parts ? (_parts - _se->_parts + 1) : 0);
+ ser->saveUint16(num);
+ } else {
+ num = ser->loadUint16();
+ _parts = (num ? &_se->_parts[num - 1] : 0);
+ }
+ ser->saveLoadEntries(this, playerEntries);
+ ser->saveLoadArrayOf(_parameterFaders, ARRAYSIZE(_parameterFaders),
+ sizeof(ParameterFader), parameterFaderEntries);
+ return;
+}
+
+} // End of namespace Scumm
Copied: scummvm/trunk/engines/scumm/imuse/instrument.cpp (from rev 20787, scummvm/trunk/engines/scumm/instrument.cpp)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/instrument.cpp (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/instrument.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,462 @@
+/* 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 "scumm/scumm.h"
+#include "scumm/saveload.h"
+#include "scumm/imuse/instrument.h"
+#include "sound/mididrv.h"
+
+namespace Scumm {
+
+static bool _native_mt32 = false;
+
+static struct {
+ const char *name;
+ byte program;
+}
+
+roland_to_gm_map [] = {
+ // Monkey Island 2 instruments
+ // TODO: Complete
+ { "badspit ", 62 },
+ { "Big Drum ", 116 },
+ { "burp ", 58 },
+// { "dinkfall ", ??? },
+// { "Fire Pit ", ??? },
+ { "foghorn ", 60 },
+ { "glop ", 39 },
+// { "jacob's la", ??? },
+ { "LeshBass ", 33 },
+// { "lowsnort ", ??? },
+ { "ML explosn", 127 },
+ { "ReggaeBass", 32 },
+// { "rope fall ", ??? },
+ { "rumble ", 89 },
+ { "SdTrk Bend", 97 },
+// { "snort ", ??? },
+ { "spitting ", 62 },
+ { "Swell 1 ", 95 },
+ { "Swell 2 ", 95 },
+ { "thnderclap", 127 }
+
+ // Fate of Atlantis instruments
+ // TODO: Build
+// { "*aah! ", ??? },
+// { "*ooh! ", ??? },
+// { "*ShotFar4 ", ??? },
+// { "*splash3 ", ??? },
+// { "*torpedo5 ", ??? },
+// { "*whip3 ", ??? },
+// { "*woodknock", ??? },
+// { "35 lavabub", ??? },
+// { "49 bzzt! ", ??? },
+// { "applause ", ??? },
+// { "Arabongo ", ??? },
+// { "Big Drum ", ??? }, // DUPLICATE (todo: confirm)
+// { "bodythud1 ", ??? },
+// { "boneKLOK2 ", ??? },
+// { "boom10 ", ??? },
+// { "boom11 ", ??? },
+// { "boom15 ", ??? },
+// { "boxclik1a ", ??? },
+// { "brassbonk3", ??? },
+// { "carstart ", ??? },
+// { "cb tpt 2 ", ??? },
+// { "cell door ", ??? },
+// { "chains ", ??? },
+// { "crash ", ??? },
+// { "crsrt/idl3", ??? },
+// { "Fire Pit ", ??? }, // DUPLICATE (todo: confirm)
+// { "Fzooom ", ??? },
+// { "Fzooom 2 ", ??? },
+// { "ghostwhosh", ??? },
+// { "glasssmash", ??? },
+// { "gloop2 ", ??? },
+// { "gunShotNea", ??? },
+// { "idoorclse ", ??? },
+// { "knife ", ??? },
+// { "lavacmbl4 ", ??? },
+// { "Mellow Str", ??? },
+// { "mtlheater1", ??? },
+// { "pachinko5 ", ??? },
+// { "Ping1 ", ??? },
+// { "rockcrunch", ??? },
+// { "rumble ", ??? }, // DUPLICATE (todo: confirm)
+// { "runngwatr ", ??? },
+// { "scrape2 ", ??? },
+// { "snakeHiss ", ??? },
+// { "snort ", ??? }, // DUPLICATE (todo: confirm)
+// { "spindle4 ", ??? },
+// { "splash2 ", ??? },
+// { "squirel ", ??? },
+// { "steam3 ", ??? },
+// { "stonwheel6", ??? },
+// { "street ", ??? },
+// { "trickle4 ", ??? }
+};
+
+const byte Instrument::_gmRhythmMap[35] = {
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47,
+ 65, 48, 56};
+ // This emulates the percussion bank setup LEC used with the MT-32,
+ // where notes 24 - 34 were assigned instruments without reverb.
+ // It also fixes problems on GS devices that map sounds to these
+ // notes by default.
+
+class Instrument_Program : public InstrumentInternal {
+private:
+ byte _program;
+ bool _mt32;
+
+public:
+ Instrument_Program (byte program, bool mt32);
+ Instrument_Program (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->program (_program, _mt32); }
+ bool is_valid() {
+ return (_program < 128) &&
+ ((_native_mt32 == _mt32) || _native_mt32
+ ? (MidiDriver::_gmToMt32[_program] < 128)
+ : (MidiDriver::_mt32ToGm[_program] < 128)); }
+};
+
+class Instrument_Adlib : public InstrumentInternal {
+private:
+ struct {
+ byte flags_1;
+ byte oplvl_1;
+ byte atdec_1;
+ byte sustrel_1;
+ byte waveform_1;
+ byte flags_2;
+ byte oplvl_2;
+ byte atdec_2;
+ byte sustrel_2;
+ byte waveform_2;
+ byte feedback;
+ byte flags_a;
+ struct { byte a,b,c,d,e,f,g,h; } extra_a;
+ byte flags_b;
+ struct { byte a,b,c,d,e,f,g,h; } extra_b;
+ byte duration;
+ } _instrument;
+
+public:
+ Instrument_Adlib (byte *data);
+ Instrument_Adlib (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->adlib ((byte *) &_instrument); }
+ bool is_valid() { return true; }
+};
+
+class Instrument_Roland : public InstrumentInternal {
+private:
+ struct RolandInstrument {
+ byte roland_id;
+ byte device_id;
+ byte model_id;
+ byte command;
+ byte address[3];
+ struct {
+ byte name[10];
+ byte partial_struct12;
+ byte partial_struct34;
+ byte partial_mute;
+ byte env_mode;
+ } common;
+ struct {
+ byte wg_pitch_coarse;
+ byte wg_pitch_fine;
+ byte wg_pitch_keyfollow;
+ byte wg_pitch_bender_sw;
+ byte wg_waveform_pcm_bank;
+ byte wg_pcm_wave_num;
+ byte wg_pulse_width;
+ byte wg_pw_velo_sens;
+ byte p_env_depth;
+ byte p_evn_velo_sens;
+ byte p_env_time_keyf;
+ byte p_env_time[4];
+ byte p_env_level[3];
+ byte p_env_sustain_level;
+ byte end_level;
+ byte p_lfo_rate;
+ byte p_lfo_depth;
+ byte p_lfo_mod_sens;
+ byte tvf_cutoff_freq;
+ byte tvf_resonance;
+ byte tvf_keyfollow;
+ byte tvf_bias_point_dir;
+ byte tvf_bias_level;
+ byte tvf_env_depth;
+ byte tvf_env_velo_sens;
+ byte tvf_env_depth_keyf;
+ byte tvf_env_time_keyf;
+ byte tvf_env_time[5];
+ byte tvf_env_level[3];
+ byte tvf_env_sustain_level;
+ byte tva_level;
+ byte tva_velo_sens;
+ byte tva_bias_point_1;
+ byte tva_bias_level_1;
+ byte tva_bias_point_2;
+ byte tva_bias_level_2;
+ byte tva_env_time_keyf;
+ byte tva_env_time_v_follow;
+ byte tva_env_time[5];
+ byte tva_env_level[3];
+ byte tva_env_sustain_level;
+ } partial[4];
+ byte checksum;
+ } GNUPACK;
+ RolandInstrument _instrument;
+
+ char _instrument_name [11];
+
+ uint8 getEquivalentGM();
+
+public:
+ Instrument_Roland (byte *data);
+ Instrument_Roland (Serializer *s);
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc);
+ void copy_to (Instrument *dest) { dest->roland ((byte *) &_instrument); }
+ bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); }
+};
+
+////////////////////////////////////////
+//
+// Instrument class members
+//
+////////////////////////////////////////
+
+void Instrument::nativeMT32 (bool native) {
+ _native_mt32 = native;
+}
+
+void Instrument::clear() {
+ if (_instrument)
+ delete _instrument;
+ _instrument = NULL;
+ _type = itNone;
+}
+
+void Instrument::program (byte prog, bool mt32) {
+ clear();
+ if (prog > 127)
+ return;
+ _type = itProgram;
+ _instrument = new Instrument_Program (prog, mt32);
+}
+
+void Instrument::adlib (byte *instrument) {
+ clear();
+ if (!instrument)
+ return;
+ _type = itAdlib;
+ _instrument = new Instrument_Adlib (instrument);
+}
+
+void Instrument::roland (byte *instrument) {
+ clear();
+ if (!instrument)
+ return;
+ _type = itRoland;
+ _instrument = new Instrument_Roland (instrument);
+}
+
+void Instrument::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveByte (_type);
+ if (_instrument)
+ _instrument->saveOrLoad (s);
+ } else {
+ clear();
+ _type = s->loadByte();
+ switch (_type) {
+ case itNone:
+ break;
+ case itProgram:
+ _instrument = new Instrument_Program (s);
+ break;
+ case itAdlib:
+ _instrument = new Instrument_Adlib (s);
+ break;
+ case itRoland:
+ _instrument = new Instrument_Roland (s);
+ break;
+ default:
+ warning ("No known instrument classification #%d", (int) _type);
+ _type = itNone;
+ }
+ }
+}
+
+////////////////////////////////////////
+//
+// Instrument_Program class members
+//
+////////////////////////////////////////
+
+Instrument_Program::Instrument_Program (byte program, bool mt32) :
+_program (program),
+_mt32 (mt32) {
+ if (program > 127)
+ _program = 255;
+}
+
+Instrument_Program::Instrument_Program (Serializer *s) {
+ _program = 255;
+ if (!s->isSaving())
+ saveOrLoad (s);
+}
+
+void Instrument_Program::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveByte (_program);
+ s->saveByte (_mt32 ? 1 : 0);
+ } else {
+ _program = s->loadByte();
+ _mt32 = (s->loadByte() > 0);
+ }
+}
+
+void Instrument_Program::send (MidiChannel *mc) {
+ if (_program > 127)
+ return;
+
+ byte program = _program;
+ if (_native_mt32 != _mt32)
+ program = _native_mt32 ? MidiDriver::_gmToMt32 [program] : MidiDriver::_mt32ToGm [program];
+ if (program < 128)
+ mc->programChange (program);
+}
+
+////////////////////////////////////////
+//
+// Instrument_Adlib class members
+//
+////////////////////////////////////////
+
+Instrument_Adlib::Instrument_Adlib (byte *data) {
+ memcpy (&_instrument, data, sizeof (_instrument));
+}
+
+Instrument_Adlib::Instrument_Adlib (Serializer *s) {
+ if (!s->isSaving())
+ saveOrLoad (s);
+ else
+ memset (&_instrument, 0, sizeof (_instrument));
+}
+
+void Instrument_Adlib::saveOrLoad (Serializer *s) {
+ if (s->isSaving())
+ s->saveBytes (&_instrument, sizeof (_instrument));
+ else
+ s->loadBytes (&_instrument, sizeof (_instrument));
+}
+
+void Instrument_Adlib::send (MidiChannel *mc) {
+ mc->sysEx_customInstrument ('ADL ', (byte *) &_instrument);
+}
+
+////////////////////////////////////////
+//
+// Instrument_Roland class members
+//
+////////////////////////////////////////
+
+Instrument_Roland::Instrument_Roland (byte *data) {
+ memcpy (&_instrument, data, sizeof (_instrument));
+ memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
+ _instrument_name[10] = '\0';
+ if (!_native_mt32 && getEquivalentGM() >= 128) {
+ debug (0, "MT-32 instrument \"%s\" not supported yet", _instrument_name);
+ _instrument_name[0] = '\0';
+ }
+}
+
+Instrument_Roland::Instrument_Roland (Serializer *s) {
+ _instrument_name[0] = '\0';
+ if (!s->isSaving())
+ saveOrLoad (s);
+ else
+ memset (&_instrument, 0, sizeof (_instrument));
+}
+
+void Instrument_Roland::saveOrLoad (Serializer *s) {
+ if (s->isSaving()) {
+ s->saveBytes (&_instrument, sizeof (_instrument));
+ } else {
+ s->loadBytes (&_instrument, sizeof (_instrument));
+ memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
+ _instrument_name[10] = '\0';
+ if (!_native_mt32 && getEquivalentGM() >= 128) {
+ debug (2, "MT-32 custom instrument \"%s\" not supported", _instrument_name);
+ _instrument_name[0] = '\0';
+ }
+ } // end if
+}
+
+void Instrument_Roland::send (MidiChannel *mc) {
+ if (_native_mt32) {
+ if (mc->getNumber() > 8)
+ return;
+ _instrument.device_id = mc->getNumber();
+
+ // Remap instrument to appropriate address space.
+ int address = 0x008000;
+ _instrument.address[0] = (address >> 14) & 0x7F;
+ _instrument.address[1] = (address >> 7) & 0x7F;
+ _instrument.address[2] = (address ) & 0x7F;
+
+ // Recompute the checksum.
+ byte checksum = 0;
+ byte *ptr = (byte *) &_instrument + 4;
+ int i;
+ for (i = 4; i < (int)sizeof (_instrument) - 1; ++i)
+ checksum -= *ptr++;
+ _instrument.checksum = checksum & 0x7F;
+
+ mc->device()->sysEx ((byte *) &_instrument, sizeof (_instrument));
+ } else {
+ // Convert to a GM program change.
+ byte program = getEquivalentGM();
+ if (program < 128)
+ mc->programChange (program);
+ }
+}
+
+uint8 Instrument_Roland::getEquivalentGM() {
+ byte i;
+ for (i = 0; i != ARRAYSIZE(roland_to_gm_map); ++i) {
+ if (!memcmp (roland_to_gm_map[i].name, _instrument.common.name, 10))
+ return roland_to_gm_map[i].program;
+ }
+ return 255;
+}
+
+} // End of namespace Scumm
Copied: scummvm/trunk/engines/scumm/imuse/instrument.h (from rev 20787, scummvm/trunk/engines/scumm/instrument.h)
===================================================================
--- scummvm/trunk/engines/scumm/imuse/instrument.h (rev 0)
+++ scummvm/trunk/engines/scumm/imuse/instrument.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -0,0 +1,79 @@
+/* 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 INSTRUMENT_H
+#define INSTRUMENT_H
+
+#include "common/stdafx.h"
+#include "common/scummsys.h"
+
+class MidiChannel;
+
+namespace Scumm {
+
+class Serializer;
+class Instrument;
+
+class InstrumentInternal {
+public:
+ virtual ~InstrumentInternal() {}
+ virtual void saveOrLoad (Serializer *s) = 0;
+ virtual void send (MidiChannel *mc) = 0;
+ virtual void copy_to (Instrument *dest) = 0;
+ virtual bool is_valid() = 0;
+ virtual operator int() { return 255; }
+};
+
+class Instrument {
+private:
+ byte _type;
+ InstrumentInternal *_instrument;
+
+public:
+ enum {
+ itNone = 0,
+ itProgram = 1,
+ itAdlib = 2,
+ itRoland = 3
+ };
+
+ Instrument() : _type (0), _instrument (0) { }
+ ~Instrument() { delete _instrument; }
+ static void nativeMT32 (bool native);
+ static const byte _gmRhythmMap[35];
+
+ void clear();
+ void copy_to (Instrument *dest) { if (_instrument) _instrument->copy_to (dest); else dest->clear(); }
+
+ void program (byte program, bool mt32);
+ void adlib (byte *instrument);
+ void roland (byte *instrument);
+
+ byte getType() { return _type; }
+ bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
+ void saveOrLoad (Serializer *s);
+ void send (MidiChannel *mc) { if (_instrument) _instrument->send (mc); }
+};
+
+} // End of namespace Scumm
+
+#endif
Deleted: scummvm/trunk/engines/scumm/imuse.cpp
===================================================================
--- scummvm/trunk/engines/scumm/imuse.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/imuse.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,2043 +0,0 @@
-/* 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 "base/version.h"
-
-#include "common/util.h"
-#include "common/system.h"
-
-#include "scumm/imuse.h"
-#include "scumm/imuse_internal.h"
-#include "scumm/instrument.h"
-#include "scumm/saveload.h"
-#include "scumm/scumm.h"
-#include "scumm/util.h"
-
-#include "sound/mididrv.h"
-
-
-namespace Scumm {
-
-////////////////////////////////////////
-//
-// IMuseInternal implementation
-//
-////////////////////////////////////////
-
-IMuseInternal::IMuseInternal() :
-_native_mt32(false),
-_enable_gs(false),
-_sc55(false),
-_midi_adlib(0),
-_midi_native(0),
-_base_sounds(0),
-_paused(false),
-_initialized(false),
-_tempoFactor(0),
-_player_limit(ARRAYSIZE(_players)),
-_recycle_players(false),
-_direct_passthrough(false),
-_queue_end(0),
-_queue_pos(0),
-_queue_sound(0),
-_queue_adding(0),
-_queue_marker(0),
-_queue_cleared(0),
-_master_volume(0),
-_music_volume(0),
-_trigger_count(0),
-_snm_trigger_index(0) {
- memset(_channel_volume,0,sizeof(_channel_volume));
- memset(_channel_volume_eff,0,sizeof(_channel_volume_eff));
- memset(_volchan_table,0,sizeof(_volchan_table));
-}
-
-byte *IMuseInternal::findStartOfSound(int sound) {
- byte *ptr = NULL;
- int32 size, pos;
-
- if (_base_sounds)
- ptr = _base_sounds[sound];
-
- if (ptr == NULL) {
- debug(1, "IMuseInternal::findStartOfSound(): Sound %d doesn't exist!", sound);
- return NULL;
- }
-
- // Check for old-style headers first, like 'RO'
- if (ptr[4] == 'R' && ptr[5] == 'O'&& ptr[6] != 'L')
- return ptr + 4;
- if (ptr[8] == 'S' && ptr[9] == 'O')
- return ptr + 8;
-
- ptr += 8;
- size = READ_BE_UINT32(ptr);
- ptr += 4;
-
- // Okay, we're looking for one of those things: either
- // an 'MThd' tag (for SMF), or a 'FORM' tag (for XMIDI).
- size = 48; // Arbitrary; we should find our tag within the first 48 bytes of the resource
- pos = 0;
- while (pos < size) {
- if (!memcmp(ptr + pos, "MThd", 4) || !memcmp(ptr + pos, "FORM", 4))
- return ptr + pos;
- ++pos; // We could probably iterate more intelligently
- }
-
- debug(3, "IMuseInternal::findStartOfSound(): Failed to align on sound %d!", sound);
- return 0;
-}
-
-bool IMuseInternal::isMT32(int sound) {
- byte *ptr = NULL;
- uint32 tag;
-
- if (_base_sounds)
- ptr = _base_sounds[sound];
-
- if (ptr == NULL)
- return false;
-
- tag = *(((uint32 *)ptr) + 1);
- switch (tag) {
- case MKID('ADL '):
- case MKID('ASFX'): // Special AD class for old Adlib sound effects
- case MKID('SPK '):
- return false;
-
- case MKID('AMI '):
- case MKID('ROL '):
- return true;
-
- case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
- return true;
-
- case MKID('GMD '):
- case MKID('MIDI'): // Occurs in Sam & Max
- return false;
- }
-
- // Old style 'RO' has equivalent properties to 'ROL'
- if (ptr[4] == 'R' && ptr[5] == 'O')
- return true;
- // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
- if (ptr[8] == 'S' && ptr[9] == 'O')
- return false;
-
- error("Unknown music type: '%s'", tag2str(tag));
-
- return false;
-}
-
-bool IMuseInternal::isMIDI(int sound) {
- byte *ptr = NULL;
- uint32 tag;
-
- if (_base_sounds)
- ptr = _base_sounds[sound];
-
- if (ptr == NULL)
- return false;
-
- tag = *(((uint32 *)ptr) + 1);
- switch (tag) {
- case MKID('ADL '):
- case MKID('ASFX'): // Special AD class for old Adlib sound effects
- case MKID('SPK '):
- return false;
-
- case MKID('AMI '):
- case MKID('ROL '):
- return true;
-
- case MKID('MAC '): // Occurs in the Mac version of FOA and MI2
- return true;
-
- case MKID('GMD '):
- case MKID('MIDI'): // Occurs in Sam & Max
- return true;
- }
-
- // Old style 'RO' has equivalent properties to 'ROL'
- if (ptr[4] == 'R' && ptr[5] == 'O')
- return true;
- // Euphony tracks show as 'SO' and have equivalent properties to 'ADL'
- // FIXME: Right now we're pretending it's GM.
- if (ptr[8] == 'S' && ptr[9] == 'O')
- return true;
-
- error("Unknown music type: '%s'", tag2str(tag));
-
- return false;
-}
-
-MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
- MidiDriver *driver = NULL;
-
- if (isMIDI(sound)) {
- if (_midi_native) {
- driver = _midi_native;
- } else {
- // Route it through Adlib anyway.
- driver = _midi_adlib;
- }
- } else {
- driver = _midi_adlib;
- }
- return driver;
-}
-
-bool IMuseInternal::startSound(int sound) {
- Player *player;
- void *ptr;
-
- // Do not start a sound if it is already set to start on an ImTrigger
- // event. This fixes carnival music problems where a sound has been set
- // to trigger at the right time, but then is started up immediately
- // anyway, only to be restarted later when the trigger occurs.
- //
- // However, we have to make sure the sound with the trigger is actually
- // playing, otherwise the music may stop when Sam and Max are thrown
- // out of Bumpusville, because entering the mansion sets up a trigger
- // for a sound that isn't necessarily playing. This is somewhat related
- // to bug #780918.
-
- int i;
- ImTrigger *trigger = _snm_triggers;
- for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trigger) {
- if (trigger->sound && trigger->id && trigger->command[0] == 8 && trigger->command[1] == sound && getSoundStatus(trigger->sound))
- return false;
- }
-
- ptr = findStartOfSound(sound);
- if (!ptr) {
- debug(2, "IMuseInternal::startSound(): Couldn't find sound %d!", sound);
- return false;
- }
-
- // Check which MIDI driver this track should use.
- // If it's NULL, it ain't something we can play.
- MidiDriver *driver = getBestMidiDriver(sound);
- if (!driver)
- return false;
-
- // If the requested sound is already playing, start it over
- // from scratch. This was originally a hack to prevent Sam & Max
- // iMuse messiness while upgrading the iMuse engine, but it
- // is apparently necessary to deal with fade-and-restart
- // race conditions that were observed in MI2. Reference
- // Bug #590511 and Patch #607175 (which was reversed to fix
- // an FOA regression: Bug #622606).
- player = findActivePlayer(sound);
- if (!player)
- player = allocate_player(128);
- if (!player)
- return false;
-
- // HACK: This is to work around a problem at the Dino Bungie Memorial.
- // There are three pieces of music involved here:
- //
- // 80 - Main theme (looping)
- // 81 - Music when entering Rex's and Wally's room (not looping)
- // 82 - Music when listening to Rex or Wally
- //
- // When entering, tune 81 starts, tune 80 is faded down (not out) and
- // a trigger is set in tune 81 to fade tune 80 back up.
- //
- // When listening to Rex or Wally, tune 82 is started, tune 81 is faded
- // out and tune 80 is faded down even further.
- //
- // However, when tune 81 is faded out its trigger will cause tune 80 to
- // fade back up, resulting in two tunes being played simultaneously at
- // full blast. It's no use trying to keep tune 81 playing at volume 0.
- // It doesn't loop, so eventually it will terminate on its own.
- //
- // I don't know how the original interpreter handled this - or even if
- // it handled it at all - but it looks like sloppy scripting to me. Our
- // workaround is to clear the trigger if the player listens to Rex or
- // Wally before tune 81 has finished on its own.
-
- if (g_scumm->_game.id == GID_SAMNMAX && sound == 82 && getSoundStatus(81, false))
- ImClearTrigger(81, 1);
-
- player->clear();
- return player->startSound(sound, driver, _direct_passthrough);
-}
-
-
-Player *IMuseInternal::allocate_player(byte priority) {
- Player *player = _players, *best = NULL;
- int i;
- byte bestpri = 255;
-
- for (i = _player_limit; i != 0; i--, player++) {
- if (!player->isActive())
- return player;
- if (player->getPriority() < bestpri) {
- best = player;
- bestpri = player->getPriority();
- }
- }
-
- if (bestpri < priority || _recycle_players)
- return best;
-
- debug(1, "Denying player request");
- return NULL;
-}
-
-void IMuseInternal::init_players() {
- Player *player = _players;
- int i;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- player->_se = this;
- player->clear(); // Used to just set _active to false
- }
-}
-
-void IMuseInternal::init_parts() {
- Part *part;
- int i;
-
- for (i = 0, part = _parts; i != ARRAYSIZE(_parts); i++, part++) {
- part->init();
- part->_se = this;
- part->_slot = i;
- }
-}
-
-int IMuseInternal::stopSound(int sound) {
- int r = -1;
- Player *player = findActivePlayer(sound);
- if (player) {
- player->clear();
- r = 0;
- }
- return r;
-}
-
-int IMuseInternal::stopAllSounds() {
- Player *player = _players;
- int i;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive())
- player->clear();
- }
- return 0;
-}
-
-void IMuseInternal::on_timer(MidiDriver *midi) {
- if (_paused || !_initialized)
- return;
-
- if (midi == _midi_native || !_midi_native)
- handleDeferredCommands(midi);
- sequencer_timers(midi);
-}
-
-int IMuseInternal::getMusicTimer() const {
- int best_time = 0;
- const Player *player = _players;
- int i;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive()) {
- int timer = player->getMusicTimer();
- if (timer > best_time)
- best_time = timer;
- }
- }
- return best_time;
-}
-
-void IMuseInternal::sequencer_timers(MidiDriver *midi) {
- Player *player = _players;
- int i;
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive() && player->getMidiDriver() == midi) {
- player->onTimer();
- }
- }
-}
-
-void IMuseInternal::handle_marker(uint id, byte data) {
- uint16 *p = 0;
- uint pos;
-
- if (_queue_adding && _queue_sound == id && data == _queue_marker)
- return;
-
- // Fix for bug #733401, revised for bug #761637:
- // It would seem that sometimes a marker is in the queue
- // but not at the head position. In the case of our bug,
- // this seems to be the result of commands in the queue
- // for songs that are no longer playing. So we skip
- // ahead to the appropriate marker, effectively chomping
- // anything in the queue before it. This fixes the FOA
- // end credits music, but needs to be tested for inappopriate
- // behavior elsewhere.
- pos = _queue_end;
- while (pos != _queue_pos) {
- p = _cmd_queue[pos].array;
- if (p[0] == TRIGGER_ID && p[1] == id && p[2] == data)
- break;
- pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
- }
-
- if (pos == _queue_pos)
- return;
-
- if (pos != _queue_end)
- debug(0, "Skipping entries in iMuse command queue to reach marker");
-
- _trigger_count--;
- _queue_cleared = false;
- do {
- pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
- if (_queue_pos == pos)
- break;
- p = _cmd_queue[pos].array;
- if (*p++ != COMMAND_ID)
- break;
- _queue_end = pos;
-
- doCommand(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
-
- if (_queue_cleared)
- return;
- pos = _queue_end;
- } while (1);
-
- _queue_end = pos;
-}
-
-int IMuseInternal::get_channel_volume(uint a) {
- if (a < 8)
- return _channel_volume_eff[a];
- return (_master_volume * _music_volume / 255) / 2;
-}
-
-Part *IMuseInternal::allocate_part(byte pri, MidiDriver *midi) {
- Part *part, *best = NULL;
- int i;
-
- for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
- if (!part->_player)
- return part;
- if (pri >= part->_pri_eff) {
- pri = part->_pri_eff;
- best = part;
- }
- }
-
- if (best) {
- best->uninit();
- reallocateMidiChannels(midi);
- } else {
- debug(1, "Denying part request");
- }
- return best;
-}
-
-int IMuseInternal::getSoundStatus(int sound, bool ignoreFadeouts) const {
- int i;
- const Player *player = _players;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive() && (!ignoreFadeouts || !player->isFadingOut())) {
- if (sound == -1)
- return player->getID();
- else if (player->getID() == (uint16)sound)
- return 1;
- }
- }
- return (sound == -1) ? 0 : get_queue_sound_status(sound);
-}
-
-int IMuseInternal::get_queue_sound_status(int sound) const {
- const uint16 *a;
- int i, j;
-
- j = _queue_pos;
- i = _queue_end;
-
- while (i != j) {
- a = _cmd_queue[i].array;
- if (a[0] == COMMAND_ID && a[1] == 8 && a[2] == (uint16)sound)
- return 2;
- i = (i + 1) % ARRAYSIZE(_cmd_queue);
- }
-
- for (i = 0; i < ARRAYSIZE (_deferredCommands); ++i) {
- if (_deferredCommands[i].time_left && _deferredCommands[i].a == 8 &&
- _deferredCommands[i].b == sound) {
- return 2;
- }
- }
-
- return 0;
-}
-
-int IMuseInternal::set_volchan(int sound, int volchan) {
- int r;
- int i;
- int num;
- Player *player, *best, *sameid;
-
- r = get_volchan_entry(volchan);
- if (r == -1)
- return -1;
-
- if (r >= 8) {
- player = findActivePlayer(sound);
- if (player && player->_vol_chan != (uint16)volchan) {
- player->_vol_chan = volchan;
- player->setVolume(player->getVolume());
- return 0;
- }
- return -1;
- } else {
- best = NULL;
- num = 0;
- sameid = NULL;
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->isActive()) {
- if (player->_vol_chan == (uint16)volchan) {
- num++;
- if (!best || player->getPriority() <= best->getPriority())
- best = player;
- } else if (player->getID() == (uint16)sound) {
- sameid = player;
- }
- }
- }
- if (sameid == NULL)
- return -1;
- if (num >= r)
- best->clear();
- player->_vol_chan = volchan;
- player->setVolume(player->getVolume());
- return 0;
- }
-}
-
-int IMuseInternal::clear_queue() {
- _queue_adding = false;
- _queue_cleared = true;
- _queue_pos = 0;
- _queue_end = 0;
- _trigger_count = 0;
- return 0;
-}
-
-int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g) {
- uint16 *p;
- uint i;
-
- i = _queue_pos;
-
- if (i == _queue_end)
- return -1;
-
- if (a == -1) {
- _queue_adding = false;
- _trigger_count++;
- return 0;
- }
-
- p = _cmd_queue[_queue_pos].array;
- p[0] = COMMAND_ID;
- p[1] = a;
- p[2] = b;
- p[3] = c;
- p[4] = d;
- p[5] = e;
- p[6] = f;
- p[7] = g;
-
- i = (i + 1) % ARRAYSIZE(_cmd_queue);
-
- if (_queue_end != i) {
- _queue_pos = i;
- return 0;
- } else {
- _queue_pos = (i - 1) % ARRAYSIZE(_cmd_queue);
- return -1;
- }
-}
-
-int IMuseInternal::query_queue(int param) {
- switch (param) {
- case 0: // Get trigger count
- return _trigger_count;
- case 1: // Get trigger type
- if (_queue_end == _queue_pos)
- return -1;
- return _cmd_queue[_queue_end].array[1];
- case 2: // Get trigger sound
- if (_queue_end == _queue_pos)
- return 0xFF;
- return _cmd_queue[_queue_end].array[2];
- default:
- return -1;
- }
-}
-
-int IMuseInternal::setMusicVolume(uint vol) {
- if (vol > 255)
- vol = 255;
- if (_music_volume == vol)
- return 0;
- _music_volume = vol;
- vol = _master_volume * _music_volume / 255;
- for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
- _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
- }
- if (!_paused)
- update_volumes();
- return 0;
-}
-
-int IMuseInternal::setImuseMasterVolume(uint vol) {
- if (vol > 255)
- vol = 255;
- if (_master_volume == vol)
- return 0;
- _master_volume = vol;
- vol = _master_volume * _music_volume / 255;
- for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
- _channel_volume_eff[i] = _channel_volume[i] * vol / 255;
- }
- if (!_paused)
- update_volumes();
- return 0;
-}
-
-int IMuseInternal::terminate1() {
- _initialized = false;
- stopAllSounds();
- return 0;
-}
-
-// This is the stuff that has to be done
-// outside the monitor's mutex, otherwise
-// a deadlock occurs.
-int IMuseInternal::terminate2() {
- if (_midi_adlib) {
- _midi_adlib->close();
- delete _midi_adlib;
- _midi_adlib = 0;
- }
-
- if (_midi_native) {
- _midi_native->close();
- delete _midi_native;
- _midi_native = 0;
- }
-
- return 0;
-}
-
-int IMuseInternal::enqueue_trigger(int sound, int marker) {
- uint16 *p;
- uint pos;
-
- pos = _queue_pos;
-
- p = _cmd_queue[pos].array;
- p[0] = TRIGGER_ID;
- p[1] = sound;
- p[2] = marker;
-
- pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
- if (_queue_end == pos) {
- _queue_pos = (pos - 1) % ARRAYSIZE(_cmd_queue);
- return -1;
- }
-
- _queue_pos = pos;
- _queue_adding = true;
- _queue_sound = sound;
- _queue_marker = marker;
- return 0;
-}
-
-int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
- int args[8];
- args[0] = a;
- args[1] = b;
- args[2] = c;
- args[3] = d;
- args[4] = e;
- args[5] = f;
- args[6] = g;
- args[7] = h;
- return doCommand(8, args);
-}
-
-int32 IMuseInternal::doCommand(int numargs, int a[]) {
- int i;
-
- if (numargs < 1)
- return -1;
- byte cmd = a[0] & 0xFF;
- byte param = a[0] >> 8;
- Player *player = NULL;
-
- if (!_initialized && (cmd || param))
- return -1;
-
-#ifdef IMUSE_DEBUG
- {
- char string[128];
- sprintf(string, "doCommand - %d (%d/%d)", a[0], (int)param, (int)cmd);
- for (i = 1; i < numargs; ++i)
- sprintf(string + strlen(string), ", %d", a[i]);
- debug(0, string);
- }
-#endif
-
- if (param == 0) {
- switch (cmd) {
- case 6:
- if (a[1] > 127)
- return -1;
- else {
- debug(0, "IMuse doCommand(6) - setImuseMasterVolume (%d)", a[1]);
- return setImuseMasterVolume((a[1] << 1) | (a[1] ? 0 : 1)); // Convert from 0-127 to 0-255
- }
- case 7:
- debug(0, "IMuse doCommand(7) - getMasterVolume (%d)", a[1]);
- return _master_volume / 2; // Convert from 0-255 to 0-127
- case 8:
- return startSound(a[1]) ? 0 : -1;
- case 9:
- return stopSound(a[1]);
- case 10: // FIXME: Sam and Max - Not sure if this is correct
- return stopAllSounds();
- case 11:
- return stopAllSounds();
- case 12:
- // Sam & Max: Player-scope commands
- player = findActivePlayer(a[1]);
- if (!player)
- return -1;
-
- switch (a[3]) {
- case 6:
- // Set player volume.
- return player->setVolume(a[4]);
- default:
- error("IMuseInternal::doCommand(12) unsupported sub-command %d", a[3]);
- }
- return -1;
- case 13:
- return getSoundStatus(a[1]);
- case 14:
- // Sam and Max: Parameter fade
- player = findActivePlayer(a[1]);
- if (player)
- return player->addParameterFader(a[3], a[4], a[5]);
- return -1;
-
- case 15:
- // Sam & Max: Set hook for a "maybe" jump
- player = findActivePlayer(a[1]);
- if (player) {
- player->setHook(0, a[3], 0);
- return 0;
- }
- return -1;
- case 16:
- debug(0, "IMuse doCommand(16) - set_volchan (%d, %d)", a[1], a[2]);
- return set_volchan(a[1], a[2]);
- case 17:
- if (g_scumm->_game.id != GID_SAMNMAX) {
- debug(0, "IMuse doCommand(17) - set_channel_volume (%d, %d)", a[1], a[2]);
- return set_channel_volume(a[1], a[2]);
- } else {
- if (a[4]) {
- int b[16];
- memset(b, 0, sizeof(b));
- for (i = 0; i < numargs; ++i)
- b[i] = a[i];
- return ImSetTrigger(b[1], b[3], b[4], b[5], b[6], b[7], b[8], b[9], b[10], b[11]);
- } else {
- return ImClearTrigger(a[1], a[3]);
- }
- }
- case 18:
- if (g_scumm->_game.id != GID_SAMNMAX) {
- return set_volchan_entry(a[1], a[2]);
- } else {
- // Sam & Max: ImCheckTrigger.
- // According to Mike's notes to Ender,
- // this function returns the number of triggers
- // associated with a particular player ID and
- // trigger ID.
- a[0] = 0;
- for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
- if (_snm_triggers[i].sound == a[1] && _snm_triggers[i].id &&
- (a[3] == -1 || _snm_triggers[i].id == a[3]))
- {
- ++a[0];
- }
- }
- return a[0];
- }
- case 19:
- // Sam & Max: ImClearTrigger
- // This should clear a trigger that's been set up
- // with ImSetTrigger(cmd == 17). Seems to work....
- return ImClearTrigger(a[1], a[3]);
- case 20:
- // Sam & Max: Deferred Command
- addDeferredCommand(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
- return 0;
- case 2:
- case 3:
- return 0;
- default:
- error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
- }
- } else if (param == 1) {
- if ((1 << cmd) & 0x783FFF) {
- player = findActivePlayer(a[1]);
- if (!player)
- return -1;
- if ((1 << cmd) & (1 << 11 | 1 << 22)) {
- assert(a[2] >= 0 && a[2] <= 15);
- player = (Player *)player->getPart(a[2]);
- if (!player)
- return -1;
- }
- }
-
- switch (cmd) {
- case 0:
- if (g_scumm->_game.id == GID_SAMNMAX) {
- if (a[3] == 1) // Measure number
- return ((player->getBeatIndex() - 1) >> 2) + 1;
- else if (a[3] == 2) // Beat number
- return player->getBeatIndex();
- return -1;
- } else {
- return player->getParam(a[2], a[3]);
- }
- case 1:
- if (g_scumm->_game.id == GID_SAMNMAX) {
- // FIXME: Could someone verify this?
- //
- // This jump instruction is known to be used in
- // the following cases:
- //
- // 1) Going anywhere on the USA map
- // 2) Winning the Wak-A-Rat game
- // 3) Losing or quitting the Wak-A-Rat game
- // 4) Conroy hitting Max with a golf club
- //
- // For all these cases the position parameters
- // are always the same: 2, 1, 0, 0.
- //
- // 5) When leaving the bigfoot party. The
- // position parameters are: 3, 4, 300, 0
- // 6) At Frog Rock, when the UFO appears. The
- // position parameters are: 10, 4, 400, 1
- //
- // The last two cases used to be buggy, so I
- // have made a change to how the last two
- // position parameters are handled. I still do
- // not know if it's correct, but it sounds
- // good to me at least.
-
- debug(0, "doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d)", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
- player->jump(a[3] - 1, (a[4] - 1) * 4 + a[5], a[6] + ((a[7] * player->getTicksPerBeat()) >> 2));
- } else
- player->setPriority(a[2]);
- return 0;
- case 2:
- return player->setVolume(a[2]);
- case 3:
- player->setPan(a[2]);
- return 0;
- case 4:
- return player->setTranspose(a[2], a[3]);
- case 5:
- player->setDetune(a[2]);
- return 0;
- case 6:
- player->setSpeed(a[2]);
- return 0;
- case 7:
- return player->jump(a[2], a[3], a[4]) ? 0 : -1;
- case 8:
- return player->scan(a[2], a[3], a[4]);
- case 9:
- return player->setLoop(a[2], a[3], a[4], a[5], a[6]) ? 0 : -1;
- case 10:
- player->clearLoop();
- return 0;
- case 11:
- ((Part *)player)->set_onoff(a[3] != 0);
- return 0;
- case 12:
- return player->setHook(a[2], a[3], a[4]);
- case 13:
- return player->addParameterFader(ParameterFader::pfVolume, a[2], a[3]);
- case 14:
- return enqueue_trigger(a[1], a[2]);
- case 15:
- return enqueue_command(a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
- case 16:
- return clear_queue();
- case 19:
- return player->getParam(a[2], a[3]);
- case 20:
- return player->setHook(a[2], a[3], a[4]);
- case 21:
- return -1;
- case 22:
- ((Part *)player)->volume(a[3]);
- return 0;
- case 23:
- return query_queue(a[1]);
- case 24:
- return 0;
- default:
- error("doCommand(%d [%d/%d], %d, %d, %d, %d, %d, %d, %d) unsupported", a[0], param, cmd, a[1], a[2], a[3], a[4], a[5], a[6], a[7]);
- return -1;
- }
- }
-
- return -1;
-}
-
-int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h) {
- // Sam & Max: ImSetTrigger.
- // Sets a trigger for a particular player and
- // marker ID, along with doCommand parameters
- // to invoke at the marker. The marker is
- // represented by MIDI SysEx block 00 xx(F7)
- // where "xx" is the marker ID.
- uint16 oldest_trigger = 0;
- ImTrigger *oldest_ptr = NULL;
-
- int i;
- ImTrigger *trig = _snm_triggers;
- for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
- if (!trig->id)
- break;
- // We used to only compare 'id' and 'sound' here, but at least
- // at the Dino Bungie Memorial that causes the music to stop
- // after getting the T-Rex tooth. See bug #888161.
- if (trig->id == id && trig->sound == sound && trig->command[0] == a)
- break;
-
- uint16 diff;
- if (trig->expire <= _snm_trigger_index)
- diff = _snm_trigger_index - trig->expire;
- else
- diff = 0x10000 - trig->expire + _snm_trigger_index;
-
- if (!oldest_ptr || oldest_trigger < diff) {
- oldest_ptr = trig;
- oldest_trigger = diff;
- }
- }
-
- // If we didn't find a trigger, see if we can expire one.
- if (!i) {
- if (!oldest_ptr)
- return -1;
- trig = oldest_ptr;
- }
-
- trig->id = id;
- trig->sound = sound;
- trig->expire = (++_snm_trigger_index & 0xFFFF);
- trig->command[0] = a;
- trig->command[1] = b;
- trig->command[2] = c;
- trig->command[3] = d;
- trig->command[4] = e;
- trig->command[5] = f;
- trig->command[6] = g;
- trig->command[7] = h;
-
- // If the command is to start a sound, stop that sound if it's already playing.
- // This fixes some carnival music problems.
- // NOTE: We ONLY do this if the sound that will trigger the command is actually
- // playing. Otherwise, there's a problem when exiting and re-entering the
- // Bumpusville mansion. Ref Bug #780918.
- if (trig->command[0] == 8 && getSoundStatus(trig->command[1]) && getSoundStatus(sound))
- stopSound(trig->command[1]);
- return 0;
-}
-
-int32 IMuseInternal::ImClearTrigger(int sound, int id) {
- int count = 0;
- int i;
- ImTrigger *trig = _snm_triggers;
- for (i = ARRAYSIZE(_snm_triggers); i; --i, ++trig) {
- if ((sound == -1 || trig->sound == sound) && trig->id && (id == -1 || trig->id == id)) {
- trig->sound = trig->id = 0;
- ++count;
- }
- }
- return (count > 0) ? 0 : -1;
-}
-
-int32 IMuseInternal::ImFireAllTriggers(int sound) {
- if (!sound)
- return 0;
- int count = 0;
- int i;
- for (i = 0; i < ARRAYSIZE(_snm_triggers); ++i) {
- if (_snm_triggers[i].sound == sound) {
- _snm_triggers[i].sound = _snm_triggers[i].id = 0;
- doCommand(8, _snm_triggers[i].command);
- ++count;
- }
- }
- return (count > 0) ? 0 : -1;
-}
-
-int IMuseInternal::set_channel_volume(uint chan, uint vol)
-{
- if (chan >= 8 || vol > 127)
- return -1;
-
- _channel_volume[chan] = vol;
- _channel_volume_eff[chan] = _master_volume * _music_volume * vol / 255 / 255;
- update_volumes();
- return 0;
-}
-
-void IMuseInternal::update_volumes() {
- Player *player;
- int i;
-
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->isActive())
- player->setVolume(player->getVolume());
- }
-}
-
-int IMuseInternal::set_volchan_entry(uint a, uint b) {
- if (a >= 8)
- return -1;
- _volchan_table[a] = b;
- return 0;
-}
-
-int HookDatas::query_param(int param, byte chan) {
- switch (param) {
- case 18:
- return _jump[0];
- case 19:
- return _transpose;
- case 20:
- return _part_onoff[chan];
- case 21:
- return _part_volume[chan];
- case 22:
- return _part_program[chan];
- case 23:
- return _part_transpose[chan];
- default:
- return -1;
- }
-}
-
-int HookDatas::set(byte cls, byte value, byte chan) {
- switch (cls) {
- case 0:
- if (value != _jump[0]) {
- _jump[1] = _jump[0];
- _jump[0] = value;
- }
- break;
- case 1:
- _transpose = value;
- break;
- case 2:
- if (chan < 16)
- _part_onoff[chan] = value;
- else if (chan == 16)
- memset(_part_onoff, value, 16);
- break;
- case 3:
- if (chan < 16)
- _part_volume[chan] = value;
- else if (chan == 16)
- memset(_part_volume, value, 16);
- break;
- case 4:
- if (chan < 16)
- _part_program[chan] = value;
- else if (chan == 16)
- memset(_part_program, value, 16);
- break;
- case 5:
- if (chan < 16)
- _part_transpose[chan] = value;
- else if (chan == 16)
- memset(_part_transpose, value, 16);
- break;
- default:
- return -1;
- }
- return 0;
-}
-
-Player *IMuseInternal::findActivePlayer(int id) {
- int i;
- Player *player = _players;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive() && player->getID() == (uint16)id)
- return player;
- }
- return NULL;
-}
-
-int IMuseInternal::get_volchan_entry(uint a) {
- if (a < 8)
- return _volchan_table[a];
- return -1;
-}
-
-uint32 IMuseInternal::property(int prop, uint32 value) {
- switch (prop) {
- case IMuse::PROP_TEMPO_BASE:
- // This is a specified as a percentage of normal
- // music speed. The number must be an integer
- // ranging from 50 to 200(for 50% to 200% normal speed).
- if (value >= 50 && value <= 200)
- _tempoFactor = value;
- break;
-
- case IMuse::PROP_NATIVE_MT32:
- _native_mt32 = (value > 0);
- Instrument::nativeMT32(_native_mt32);
- if (_midi_native && _native_mt32)
- initMT32(_midi_native);
- break;
-
- case IMuse::PROP_GS:
- _enable_gs = (value > 0);
-
- // If True Roland MT-32 is not selected, run in GM or GS mode.
- // If it is selected, change the Roland GS synth to MT-32 mode.
- if (_midi_native && !_native_mt32)
- initGM(_midi_native);
- else if (_midi_native && _native_mt32 && _enable_gs) {
- _sc55 = true;
- initGM(_midi_native);
- }
- break;
-
- case IMuse::PROP_LIMIT_PLAYERS:
- if (value > 0 && value <= ARRAYSIZE(_players))
- _player_limit = (int)value;
- break;
-
- case IMuse::PROP_RECYCLE_PLAYERS:
- _recycle_players = (value != 0);
- break;
-
- case IMuse::PROP_DIRECT_PASSTHROUGH:
- _direct_passthrough = (value != 0);
- break;
- }
-
- return 0;
-}
-
-void IMuseInternal::setBase(byte **base) {
- _base_sounds = base;
-}
-
-IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
- IMuseInternal *i = new IMuseInternal;
- i->initialize(syst, nativeMidiDriver, adlibMidiDriver);
- return i;
-}
-
-int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver *adlib_midi) {
- int i;
-
- _midi_native = native_midi;
- _midi_adlib = adlib_midi;
- if (native_midi != NULL)
- initMidiDriver(native_midi);
- if (adlib_midi != NULL)
- initMidiDriver(adlib_midi);
-
- if (!_tempoFactor)
- _tempoFactor = 100;
- _master_volume = 255;
-
- for (i = 0; i != 8; i++)
- _channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
-
- init_players();
- init_queue();
- init_parts();
-
- _initialized = true;
-
- return 0;
-}
-
-void IMuseInternal::initMidiDriver(MidiDriver *midi) {
- // Open MIDI driver
- int result = midi->open();
- if (result)
- error("IMuse initialization - %s", MidiDriver::getErrorName(result));
-
- // Connect to the driver's timer
- midi->setTimerCallback(midi, &IMuseInternal::midiTimerCallback);
-}
-
-void IMuseInternal::initMT32(MidiDriver *midi) {
- byte buffer[52];
- char info[256] = "ScummVM ";
- int len;
-
- // Reset the MT-32
- memcpy(&buffer[0], "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
- midi->sysEx(buffer, 9);
- g_system->delayMillis(100);
-
- // Compute version string (truncated to 20 chars max.)
- strcat(info, gScummVMVersion);
- len = strlen(info);
- if (len > 20)
- len = 20;
-
- // Display a welcome message on MT-32 displays.
- memcpy(&buffer[4], "\x20\x00\x00", 3);
- memcpy(&buffer[7], " ", 20);
- memcpy(buffer + 7 +(20 - len) / 2, info, len);
- byte checksum = 0;
- for (int i = 4; i < 27; ++i)
- checksum -= buffer[i];
- buffer[27] = checksum & 0x7F;
- midi->sysEx(buffer, 28);
- g_system->delayMillis(500);
-
- // Setup master tune, reverb mode, reverb time, reverb level,
- // channel mapping, partial reserve and master volume
- memcpy(&buffer[4], "\x10\x00\x00\x40\x00\x04\x04\x04\x04\x04\x04\x04\x04\x04\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x64\x77", 27);
- midi->sysEx(buffer, 31);
- g_system->delayMillis(250);
-
- // Map percussion to notes 24 - 34 without reverb
- memcpy(&buffer[4], "\x03\x01\x10\x40\x64\x07\x00\x4a\x64\x06\x00\x41\x64\x07\x00\x4b\x64\x08\x00\x45\x64\x06\x00\x44\x64\x0b\x00\x51\x64\x05\x00\x43\x64\x08\x00\x50\x64\x07\x00\x42\x64\x03\x00\x4c\x64\x07\x00\x44", 48);
- midi->sysEx(buffer, 52);
- g_system->delayMillis(250);
-}
-
-void IMuseInternal::initGM(MidiDriver *midi) {
- byte buffer[11];
- int i;
-
- // General MIDI System On message
- // Resets all GM devices to default settings
- memcpy(&buffer[0], "\xF0\x7E\x7F\x09\x01\xF7", 6);
- midi->sysEx(buffer, 6);
- debug(2, "GM SysEx: GM System On");
- g_system->delayMillis(200);
-
- if (_enable_gs) {
-
- // All GS devices recognize the GS Reset command,
- // even with Roland's ID. It is impractical to
- // support other manufacturers' devices for
- // further GS settings, as there are limitless
- // numbers of them out there that would each
- // require individual SysEx commands with unique IDs.
-
- // Roland GS SysEx ID
- memcpy(&buffer[0], "\xF0\x41\x10\x42\x12", 5);
-
- // GS Reset
- memcpy(&buffer[5], "\x40\x00\x7F\x00\x41\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: GS Reset");
- g_system->delayMillis(200);
-
- if (_sc55) {
- // This mode is for GS devices that support an MT-32-compatible
- // Map, such as the Roland Sound Canvas line of modules. It
- // will allow them to work with True MT-32 mode, but will
- // obviously still ignore MT-32 SysEx (and thus custom
- // instruments).
-
- // Set Channels 1-16 to SC-55 Map, then CM-64/32L Variation
- for (i = 0; i < 16; ++i) {
- midi->send(( 127 << 16) | (0 << 8) | (0xB0 | i));
- midi->send(( 1 << 16) | (32 << 8) | (0xB0 | i));
- midi->send(( 0 << 16) | (0 << 8) | (0xC0 | i));
- }
- debug(2, "GS Program Change: CM-64/32L Map Selected");
-
- // Set Percussion Channel to SC-55 Map (CC#32, 01H), then
- // Switch Drum Map to CM-64/32L (MT-32 Compatible Drums)
- midi->getPercussionChannel()->controlChange(0, 0);
- midi->getPercussionChannel()->controlChange(32, 1);
- midi->send(127 << 8 | 0xC0 | 9);
- debug(2, "GS Program Change: Drum Map is CM-64/32L");
-
- }
-
- // Set Master Chorus to 0. The MT-32 has no chorus capability.
- memcpy(&buffer[5], "\x40\x01\x3A\x00\x05\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Master Chorus Level is 0");
-
- // Set Channels 1-16 Reverb to 64, which is the
- // equivalent of MT-32 default Reverb Level 5
- for (i = 0; i < 16; ++i)
- midi->send(( 64 << 16) | (91 << 8) | (0xB0 | i));
- debug(2, "GM Controller 91 Change: Channels 1-16 Reverb Level is 64");
-
- // Set Channels 1-16 Pitch Bend Sensitivity to
- // 12 semitones; then lock the RPN by setting null.
- for (i = 0; i < 16; ++i) {
- midi->send(( 0 << 16) | (100 << 8) | (0xB0 | i));
- midi->send(( 0 << 16) | (101 << 8) | (0xB0 | i));
- midi->send(( 12 << 16) | (6 << 8) | (0xB0 | i));
- midi->send(( 0 << 16) | (38 << 8) | (0xB0 | i));
- midi->send(( 127 << 16) | (100 << 8) | (0xB0 | i));
- midi->send(( 127 << 16) | (101 << 8) | (0xB0 | i));
- }
- debug(2, "GM Controller 6 Change: Channels 1-16 Pitch Bend Sensitivity is 12 semitones");
-
- // Set channels 1-16 Mod. LFO1 Pitch Depth to 4
- memcpy(&buffer[5], "\x40\x20\x04\x04\x18\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x21\x04\x04\x17\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x22\x04\x04\x16\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x23\x04\x04\x15\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x24\x04\x04\x14\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x25\x04\x04\x13\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x26\x04\x04\x12\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x27\x04\x04\x11\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x28\x04\x04\x10\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x29\x04\x04\x0F\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2A\x04\x04\x0E\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2B\x04\x04\x0D\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2C\x04\x04\x0C\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2D\x04\x04\x0B\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2E\x04\x04\x0A\xF7", 6);
- midi->sysEx(buffer, 11);
- memcpy(&buffer[5], "\x40\x2F\x04\x04\x09\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Channels 1-16 Mod. LFO1 Pitch Depth Level is 4");
-
- // Set Percussion Channel Expression to 80
- midi->getPercussionChannel()->controlChange(11, 80);
- debug(2, "GM Controller 11 Change: Percussion Channel Expression Level is 80");
-
- // Turn off Percussion Channel Rx. Expression so that
- // Expression cannot be modified. I don't know why, but
- // Roland does it this way.
- memcpy(&buffer[5], "\x40\x10\x0E\x00\x22\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Percussion Channel Rx. Expression is OFF");
-
- // Change Reverb Character to 0. I don't think this
- // sounds most like MT-32, but apparently Roland does.
- memcpy(&buffer[5], "\x40\x01\x31\x00\x0E\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Reverb Character is 0");
-
- // Change Reverb Pre-LF to 4, which is similar to
- // what MT-32 reverb does.
- memcpy(&buffer[5], "\x40\x01\x32\x04\x09\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Reverb Pre-LF is 4");
-
- // Change Reverb Time to 106; the decay on Hall 2
- // Reverb is too fast compared to the MT-32's
- memcpy(&buffer[5], "\x40\x01\x34\x6A\x21\xF7", 6);
- midi->sysEx(buffer, 11);
- debug(2, "GS SysEx: Reverb Time is 106");
- }
-}
-
-void IMuseInternal::init_queue() {
- _queue_adding = false;
- _queue_pos = 0;
- _queue_end = 0;
- _trigger_count = 0;
-}
-
-void IMuseInternal::pause(bool paused) {
- if (_paused == paused)
- return;
- int vol = _music_volume;
- if (paused)
- _music_volume = 0;
- update_volumes();
- _music_volume = vol;
-
- // Fix for Bug #817871. The MT-32 apparently fails
- // sometimes to respond to a channel volume message
- // (or only uses it for subsequent note events).
- // The result is hanging notes on pause. Reportedly
- // happens in the original distro, too. To fix that,
- // just send AllNotesOff to the channels.
- if (_midi_native && _native_mt32) {
- for (int i = 0; i < 16; ++i)
- _midi_native->send(123 << 8 | 0xB0 | i);
- }
-
- _paused = paused;
-}
-
-void IMuseInternal::handleDeferredCommands(MidiDriver *midi) {
- uint32 advance = midi->getBaseTempo();
-
- DeferredCommand *ptr = &_deferredCommands[0];
- int i;
- for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
- if (!ptr->time_left)
- continue;
- if (ptr->time_left <= advance) {
- doCommand(ptr->a, ptr->b, ptr->c, ptr->d, ptr->e, ptr->f, 0, 0);
- ptr->time_left = advance;
- }
- ptr->time_left -= advance;
- }
-}
-
-// "time" is interpreted as hundredths of a second.
-// FIXME: Is that correct?
-// We convert it to microseconds before prceeding
-void IMuseInternal::addDeferredCommand(int time, int a, int b, int c, int d, int e, int f) {
- DeferredCommand *ptr = &_deferredCommands[0];
- int i;
- for (i = ARRAYSIZE(_deferredCommands); i; --i, ++ptr) {
- if (!ptr->time_left)
- break;
- }
-
- if (i) {
- ptr->time_left = time * 10000;
- ptr->a = a;
- ptr->b = b;
- ptr->c = c;
- ptr->d = d;
- ptr->e = e;
- ptr->f = f;
- }
-}
-
-////////////////////////////////////////////////////////////
-//
-// IMuseInternal load/save implementation
-//
-////////////////////////////////////////////////////////////
-
-int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) {
- const SaveLoadEntry mainEntries[] = {
- MKLINE(IMuseInternal, _queue_end, sleUint8, VER(8)),
- MKLINE(IMuseInternal, _queue_pos, sleUint8, VER(8)),
- MKLINE(IMuseInternal, _queue_sound, sleUint16, VER(8)),
- MKLINE(IMuseInternal, _queue_adding, sleByte, VER(8)),
- MKLINE(IMuseInternal, _queue_marker, sleByte, VER(8)),
- MKLINE(IMuseInternal, _queue_cleared, sleByte, VER(8)),
- MKLINE(IMuseInternal, _master_volume, sleByte, VER(8)),
- MKLINE(IMuseInternal, _trigger_count, sleUint16, VER(8)),
- MKLINE(IMuseInternal, _snm_trigger_index, sleUint16, VER(54)),
- MKARRAY(IMuseInternal, _channel_volume[0], sleUint16, 8, VER(8)),
- MKARRAY(IMuseInternal, _volchan_table[0], sleUint16, 8, VER(8)),
- MKEND()
- };
-
- const SaveLoadEntry cmdQueueEntries[] = {
- MKARRAY(CommandQueue, array[0], sleUint16, 8, VER(23)),
- MKEND()
- };
-
- // VolumeFader is obsolete.
- const SaveLoadEntry volumeFaderEntries[] = {
- MK_OBSOLETE(VolumeFader, player, sleUint16, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, active, sleUint8, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, curvol, sleUint8, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, speed_lo_max, sleUint16, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, num_steps, sleUint16, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, speed_hi, sleInt8, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, direction, sleInt8, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, speed_lo, sleInt8, VER(8), VER(16)),
- MK_OBSOLETE(VolumeFader, speed_lo_counter, sleUint16, VER(8), VER(16)),
- MKEND()
- };
-
- const SaveLoadEntry snmTriggerEntries[] = {
- MKLINE(ImTrigger, sound, sleInt16, VER(54)),
- MKLINE(ImTrigger, id, sleByte, VER(54)),
- MKLINE(ImTrigger, expire, sleUint16, VER(54)),
- MKARRAY(ImTrigger, command[0], sleUint16, 8, VER(54)),
- MKEND()
- };
-
- int i;
-
- ser->saveLoadEntries(this, mainEntries);
- ser->saveLoadArrayOf(_cmd_queue, ARRAYSIZE(_cmd_queue), sizeof(_cmd_queue[0]), cmdQueueEntries);
- ser->saveLoadArrayOf(_snm_triggers, ARRAYSIZE(_snm_triggers), sizeof(_snm_triggers[0]), snmTriggerEntries);
-
- // The players
- for (i = 0; i < ARRAYSIZE(_players); ++i)
- _players[i].saveLoadWithSerializer(ser);
-
- // The parts
- for (i = 0; i < ARRAYSIZE(_parts); ++i)
- _parts[i].saveLoadWithSerializer(ser);
-
- { // Load/save the instrument definitions, which were revamped with V11.
- Part *part = &_parts[0];
- if (ser->getVersion() >= VER(11)) {
- for (i = ARRAYSIZE(_parts); i; --i, ++part) {
- part->_instrument.saveOrLoad(ser);
- }
- } else {
- for (i = ARRAYSIZE(_parts); i; --i, ++part)
- part->_instrument.clear();
- }
- }
-
- // VolumeFader has been replaced with the more generic ParameterFader.
- // FIXME: replace this loop by something like
- // if (loading && version <= 16) ser->skip(XXX bytes);
- for (i = 0; i < 8; ++i)
- ser->saveLoadEntries(0, volumeFaderEntries);
-
- if (ser->isLoading()) {
- // Load all sounds that we need
- fix_players_after_load(scumm);
- fix_parts_after_load();
- setImuseMasterVolume(_master_volume);
-
- if (_midi_native)
- reallocateMidiChannels(_midi_native);
- if (_midi_adlib)
- reallocateMidiChannels(_midi_adlib);
- }
-
- return 0;
-}
-
-void IMuseInternal::fix_parts_after_load() {
- Part *part;
- int i;
-
- for (i = ARRAYSIZE(_parts), part = _parts; i != 0; i--, part++) {
- if (part->_player)
- part->fix_after_load();
- }
-}
-
-// Only call this routine from the main thread,
-// since it uses getResourceAddress
-void IMuseInternal::fix_players_after_load(ScummEngine *scumm) {
- Player *player = _players;
- int i;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->isActive()) {
- scumm->getResourceAddress(rtSound, player->getID());
- player->fixAfterLoad();
- }
- }
-}
-
-Part::Part() {
- _slot = 0;
- _next = 0;
- _prev = 0;
- _mc = 0;
- _player = 0;
- _pitchbend = 0;
- _pitchbend_factor = 0;
- _transpose = 0;
- _transpose_eff = 0;
- _vol = 0;
- _vol_eff = 0;
- _detune = 0;
- _detune_eff = 0;
- _pan = 0;
- _pan_eff = 0;
- _on = false;
- _modwheel = 0;
- _pedal = false;
- _pri = 0;
- _pri_eff = 0;
- _chan = 0;
- _effect_level = 0;
- _chorus = 0;
- _percussion = 0;
- _bank = 0;
- _unassigned_instrument = false;
-}
-
-void Part::saveLoadWithSerializer(Serializer *ser) {
- const SaveLoadEntry partEntries[] = {
- MKLINE(Part, _pitchbend, sleInt16, VER(8)),
- MKLINE(Part, _pitchbend_factor, sleUint8, VER(8)),
- MKLINE(Part, _transpose, sleInt8, VER(8)),
- MKLINE(Part, _vol, sleUint8, VER(8)),
- MKLINE(Part, _detune, sleInt8, VER(8)),
- MKLINE(Part, _pan, sleInt8, VER(8)),
- MKLINE(Part, _on, sleUint8, VER(8)),
- MKLINE(Part, _modwheel, sleUint8, VER(8)),
- MKLINE(Part, _pedal, sleUint8, VER(8)),
- MK_OBSOLETE(Part, _program, sleUint8, VER(8), VER(16)),
- MKLINE(Part, _pri, sleUint8, VER(8)),
- MKLINE(Part, _chan, sleUint8, VER(8)),
- MKLINE(Part, _effect_level, sleUint8, VER(8)),
- MKLINE(Part, _chorus, sleUint8, VER(8)),
- MKLINE(Part, _percussion, sleUint8, VER(8)),
- MKLINE(Part, _bank, sleUint8, VER(8)),
- MKEND()
- };
-
- int num;
- if (ser->isSaving()) {
- num = (_next ? (_next - _se->_parts + 1) : 0);
- ser->saveUint16(num);
-
- num = (_prev ? (_prev - _se->_parts + 1) : 0);
- ser->saveUint16(num);
-
- num = (_player ? (_player - _se->_players + 1) : 0);
- ser->saveUint16(num);
- } else {
- num = ser->loadUint16();
- _next = (num ? &_se->_parts[num - 1] : 0);
-
- num = ser->loadUint16();
- _prev = (num ? &_se->_parts[num - 1] : 0);
-
- num = ser->loadUint16();
- _player = (num ? &_se->_players[num - 1] : 0);
- }
- ser->saveLoadEntries(this, partEntries);
-}
-
-void Part::set_detune(int8 detune) {
- _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
- if (_mc)
- sendPitchBend();
-}
-
-void Part::pitchBend(int16 value) {
- _pitchbend = value;
- if (_mc)
- sendPitchBend();
-}
-
-void Part::volume(byte value) {
- _vol_eff = ((_vol = value) + 1) * _player->getEffectiveVolume() >> 7;
- if (_mc)
- _mc->volume(_vol_eff);
-}
-
-void Part::set_pri(int8 pri) {
- _pri_eff = clamp((_pri = pri) + _player->getPriority(), 0, 255);
- if (_mc)
- _mc->priority(_pri_eff);
-}
-
-void Part::set_pan(int8 pan) {
- _pan_eff = clamp((_pan = pan) + _player->getPan(), -64, 63);
- if (_mc)
- _mc->panPosition(_pan_eff + 0x40);
-}
-
-void Part::set_transpose(int8 transpose) {
- _transpose_eff = transpose_clamp((_transpose = transpose) + _player->getTranspose(), -24, 24);
- if (_mc)
- sendPitchBend();
-}
-
-void Part::sustain(bool value) {
- _pedal = value;
- if (_mc)
- _mc->sustain(value);
-}
-
-void Part::modulationWheel(byte value) {
- _modwheel = value;
- if (_mc)
- _mc->modulationWheel(value);
-}
-
-void Part::chorusLevel(byte value) {
- _chorus = value;
- if (_mc)
- _mc->chorusLevel(value);
-}
-
-void Part::effectLevel(byte value)
-{
- _effect_level = value;
- if (_mc)
- _mc->effectLevel(value);
-}
-
-void Part::fix_after_load() {
- set_transpose(_transpose);
- volume(_vol);
- set_detune(_detune);
- set_pri(_pri);
- set_pan(_pan);
- sendAll();
-}
-
-void Part::pitchBendFactor(byte value) {
- if (value > 12)
- return;
- pitchBend(0);
- _pitchbend_factor = value;
- if (_mc)
- _mc->pitchBendFactor(value);
-}
-
-void Part::set_onoff(bool on) {
- if (_on != on) {
- _on = on;
- if (!on)
- off();
- if (!_percussion)
- _player->_se->reallocateMidiChannels(_player->getMidiDriver());
- }
-}
-
-void Part::set_instrument(byte * data) {
- _instrument.adlib(data);
- if (clearToTransmit())
- _instrument.send(_mc);
-}
-
-void Part::load_global_instrument(byte slot) {
- _player->_se->copyGlobalAdlibInstrument(slot, &_instrument);
- if (clearToTransmit())
- _instrument.send(_mc);
-}
-
-void Part::noteOn(byte note, byte velocity) {
- if (!_on)
- return;
-
- MidiChannel *mc = _mc;
-
- // DEBUG
- if (_unassigned_instrument && !_percussion) {
- _unassigned_instrument = false;
- if (!_instrument.isValid()) {
- debug(0, "[%02d] No instrument specified", (int)_chan);
- return;
- }
- }
-
- if (mc && _instrument.isValid()) {
- mc->noteOn(note, velocity);
- } else if (_percussion) {
- mc = _player->getMidiDriver()->getPercussionChannel();
- if (!mc)
- return;
- static byte prev_vol_eff = 128;
- if (_vol_eff != prev_vol_eff){
- mc->volume(_vol_eff);
- prev_vol_eff = _vol_eff;
- }
- if ((note < 35) && (!_player->_se->isNativeMT32()))
- note = Instrument::_gmRhythmMap[note];
-
- mc->noteOn(note, velocity);
- }
-}
-
-void Part::noteOff(byte note) {
- if (!_on)
- return;
-
- MidiChannel *mc = _mc;
- if (mc) {
- mc->noteOff(note);
- } else if (_percussion) {
- mc = _player->getMidiDriver()->getPercussionChannel();
- if (mc)
- mc->noteOff(note);
- }
-}
-
-void Part::init() {
- _player = NULL;
- _next = NULL;
- _prev = NULL;
- _mc = NULL;
-}
-
-void Part::setup(Player *player) {
- _player = player;
-
- _percussion = (player->isMIDI() && _chan == 9); // true;
- _on = true;
- _pri_eff = player->getPriority();
- _pri = 0;
- _vol = 127;
- _vol_eff = player->getEffectiveVolume();
- _pan = clamp(player->getPan(), -64, 63);
- _transpose_eff = player->getTranspose();
- _transpose = 0;
- _detune = 0;
- _detune_eff = player->getDetune();
- _pitchbend_factor = 2;
- _pitchbend = 0;
- _effect_level = 64;
- _instrument.clear();
- _unassigned_instrument = true;
- _chorus = 0;
- _modwheel = 0;
- _bank = 0;
- _pedal = false;
- _mc = NULL;
-}
-
-void Part::uninit() {
- if (!_player)
- return;
- off();
- _player->removePart(this);
- _player = NULL;
-}
-
-void Part::off() {
- if (_mc) {
- _mc->allNotesOff();
- _mc->release();
- _mc = NULL;
- }
-}
-
-bool Part::clearToTransmit() {
- if (_mc)
- return true;
- if (_instrument.isValid())
- _player->_se->reallocateMidiChannels(_player->getMidiDriver());
- return false;
-}
-
-void Part::sendAll() {
- if (!clearToTransmit())
- return;
- _mc->pitchBendFactor(_pitchbend_factor);
- sendPitchBend();
- _mc->volume(_vol_eff);
- _mc->sustain(_pedal);
- _mc->modulationWheel(_modwheel);
- _mc->panPosition(_pan_eff + 0x40);
- _mc->effectLevel(_effect_level);
- if (_instrument.isValid())
- _instrument.send(_mc);
- _mc->chorusLevel(_chorus);
- _mc->priority(_pri_eff);
-}
-
-void Part::sendPitchBend() {
- int16 bend = _pitchbend;
- // RPN-based pitchbend range doesn't work for the MT32,
- // so we'll do the scaling ourselves.
- if (_player->_se->isNativeMT32())
- bend = bend * _pitchbend_factor / 12;
- _mc->pitchBend(clamp(bend + (_detune_eff * 64 / 12) + (_transpose_eff * 8192 / 12), -8192, 8191));
-}
-
-void Part::programChange(byte value) {
- _bank = 0;
- _instrument.program(value, _player->isMT32());
- if (clearToTransmit())
- _instrument.send(_mc);
-}
-
-void Part::set_instrument(uint b) {
- _bank = (byte)(b >> 8);
- if (_bank)
- error("Non-zero instrument bank selection. Please report this");
- _instrument.program((byte)b, _player->isMT32());
- if (clearToTransmit())
- _instrument.send(_mc);
-}
-
-void Part::allNotesOff() {
- if (!_mc)
- return;
- _mc->allNotesOff();
-}
-
-////////////////////////////////////////
-//
-// Some more IMuseInternal stuff
-//
-////////////////////////////////////////
-
-void IMuseInternal::midiTimerCallback(void *data) {
- MidiDriver *driver = (MidiDriver *)data;
- if (g_scumm->_imuse)
- g_scumm->_imuse->on_timer(driver);
-}
-
-void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
- Part *part, *hipart;
- int i;
- byte hipri, lopri;
- Part *lopart;
-
- while (true) {
- hipri = 0;
- hipart = NULL;
- for (i = 32, part = _parts; i; i--, part++) {
- if (part->_player && part->_player->getMidiDriver() == midi &&
- !part->_percussion && part->_on &&
- !part->_mc && part->_pri_eff >= hipri) {
- hipri = part->_pri_eff;
- hipart = part;
- }
- }
-
- if (!hipart)
- return;
-
- if ((hipart->_mc = midi->allocateChannel()) == NULL) {
- lopri = 255;
- lopart = NULL;
- for (i = 32, part = _parts; i; i--, part++) {
- if (part->_mc && part->_mc->device() == midi && part->_pri_eff <= lopri) {
- lopri = part->_pri_eff;
- lopart = part;
- }
- }
-
- if (lopart == NULL || lopri >= hipri)
- return;
- lopart->off();
-
- if ((hipart->_mc = midi->allocateChannel()) == NULL)
- return;
- }
- hipart->sendAll();
- }
-}
-
-void IMuseInternal::setGlobalAdlibInstrument(byte slot, byte *data) {
- if (slot < 32) {
- _global_adlib_instruments[slot].adlib(data);
- }
-}
-
-void IMuseInternal::copyGlobalAdlibInstrument(byte slot, Instrument *dest) {
- if (slot >= 32)
- return;
- _global_adlib_instruments[slot].copy_to(dest);
-}
-
-////////////////////////////////////////////////////////////
-//
-// IMuse implementation
-//
-// IMuse actually serves as a concurency monitor front-end
-// to IMuseInternal and ensures that only one thread
-// accesses the object at a time. This is necessary to
-// prevent scripts and the MIDI parser from yanking objects
-// out from underneath each other.
-//
-////////////////////////////////////////////////////////////
-
-IMuse::IMuse(OSystem *system, IMuseInternal *target)
- : _system(system), _target(target) {
- _mutex = system->createMutex();
-}
-
-IMuse::~IMuse() {
- if (_mutex)
- _system->deleteMutex(_mutex);
- if (_target)
- delete _target;
-}
-
-inline void IMuse::in() const {
- _system->lockMutex(_mutex);
-}
-inline void IMuse::out() const {
- _system->unlockMutex(_mutex);
-}
-
-void IMuse::on_timer(MidiDriver *midi) { in(); _target->on_timer(midi); out(); }
-void IMuse::pause(bool paused) { in(); _target->pause(paused); out(); }
-int IMuse::save_or_load(Serializer *ser, ScummEngine *scumm) { in(); int ret = _target->save_or_load(ser, scumm); out(); return ret; }
-void IMuse::setMusicVolume(int vol) { in(); _target->setMusicVolume(vol); out(); }
-void IMuse::startSound(int sound) { in(); _target->startSound(sound); out(); }
-void IMuse::stopSound(int sound) { in(); _target->stopSound(sound); out(); }
-void IMuse::stopAllSounds() { in(); _target->stopAllSounds(); out(); }
-int IMuse::getSoundStatus(int sound) const { in(); int ret = _target->getSoundStatus(sound, true); out(); return ret; }
-bool IMuse::get_sound_active(int sound) const { in(); bool ret = _target->getSoundStatus(sound, false) ? 1 : 0; out(); return ret; }
-int IMuse::getMusicTimer() const { in(); int ret = _target->getMusicTimer(); out(); return ret; }
-int32 IMuse::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) { in(); int32 ret = _target->doCommand(a,b,c,d,e,f,g,h); out(); return ret; }
-int32 IMuse::doCommand(int numargs, int args[]) { in(); int32 ret = _target->doCommand(numargs, args); out(); return ret; }
-int IMuse::clear_queue() { in(); int ret = _target->clear_queue(); out(); return ret; }
-void IMuse::setBase(byte **base) { in(); _target->setBase(base); out(); }
-uint32 IMuse::property(int prop, uint32 value) { in(); uint32 ret = _target->property(prop, value); out(); return ret; }
-void IMuse::terminate() { in(); _target->terminate1(); out(); _target->terminate2(); }
-
-// The IMuse::create method provides a front-end factory
-// for creating IMuseInternal without exposing that class
-// to the client.
-IMuse *IMuse::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
- IMuseInternal *engine = IMuseInternal::create(syst, nativeMidiDriver, adlibMidiDriver);
- return new IMuse(syst, engine);
-}
-
-} // End of namespace Scumm
Deleted: scummvm/trunk/engines/scumm/imuse.h
===================================================================
--- scummvm/trunk/engines/scumm/imuse.h 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/imuse.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,85 +0,0 @@
-/* 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 IMUSE_H
-#define IMUSE_H
-
-#include "common/scummsys.h"
-#include "common/mutex.h"
-#include "scumm/music.h"
-
-class MidiDriver;
-class OSystem;
-
-namespace Scumm {
-
-class IMuseInternal;
-class ScummEngine;
-class Serializer;
-
-class IMuse : public MusicEngine {
-private:
- OSystem *_system;
- IMuseInternal *_target;
- mutable Common::MutexRef _mutex;
-
- IMuse(OSystem *system, IMuseInternal *target);
- void in() const;
- void out() const;
-
-public:
- ~IMuse();
-
- enum {
- PROP_TEMPO_BASE,
- PROP_NATIVE_MT32,
- PROP_GS,
- PROP_LIMIT_PLAYERS,
- PROP_RECYCLE_PLAYERS,
- PROP_DIRECT_PASSTHROUGH
- };
-
- void on_timer(MidiDriver *midi);
- void pause(bool paused);
- int save_or_load(Serializer *ser, ScummEngine *scumm);
- void setMusicVolume(int vol);
- void startSound(int sound);
- void stopSound(int sound);
- void stopAllSounds();
- int getSoundStatus(int sound) const;
- bool get_sound_active(int sound) const;
- int getMusicTimer() const;
- int32 doCommand(int a, int b, int c, int d, int e, int f, int g, int h);
- int32 doCommand(int numargs, int args[]);
- int clear_queue();
- void setBase(byte **base);
- uint32 property(int prop, uint32 value);
- void terminate();
-
- // Factory methods
- static IMuse *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
-};
-
-} // End of namespace Scumm
-
-#endif
Deleted: scummvm/trunk/engines/scumm/imuse_internal.h
===================================================================
--- scummvm/trunk/engines/scumm/imuse_internal.h 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/imuse_internal.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,472 +0,0 @@
-/* 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_IMUSE_INTERNAL
-#define DEFINED_IMUSE_INTERNAL
-
-#include "common/scummsys.h"
-#include "scumm/instrument.h"
-#include "scumm/saveload.h"
-#include "sound/mididrv.h"
-
-class MidiParser;
-class OSystem;
-
-namespace Scumm {
-
-// Unremark this statement to activate some of
-// the most common iMuse diagnostic messages.
-// #define IMUSE_DEBUG
-
-struct ParameterFader;
-struct DeferredCommand;
-struct ImTrigger;
-struct SustainingNotes;
-struct CommandQueue;
-struct IsNoteCmdData;
-class Player;
-struct Part;
-class IMuseInternal;
-
-// Some entities also referenced
-class ScummEngine;
-
-
-
-//////////////////////////////////////////////////
-//
-// Some constants
-//
-//////////////////////////////////////////////////
-
-#define TICKS_PER_BEAT 480
-
-#define TRIGGER_ID 0
-#define COMMAND_ID 1
-
-#define MDPG_TAG "MDpg"
-
-
-////////////////////////////////////////
-//
-// Helper functions
-//
-////////////////////////////////////////
-
-inline int clamp(int val, int min, int max) {
- if (val < min)
- return min;
- if (val > max)
- return max;
- return val;
-}
-
-inline int transpose_clamp(int a, int b, int c) {
- if (b > a)
- a += (b - a + 11) / 12 * 12;
- if (c < a)
- a -= (a - c + 11) / 12 * 12;
- return a;
-}
-
-
-
-//////////////////////////////////////////////////
-//
-// Entity declarations
-//
-//////////////////////////////////////////////////
-
-struct HookDatas {
- byte _jump[2];
- byte _transpose;
- byte _part_onoff[16];
- byte _part_volume[16];
- byte _part_program[16];
- byte _part_transpose[16];
-
- int query_param(int param, byte chan);
- int set(byte cls, byte value, byte chan);
- HookDatas() { memset(this, 0, sizeof(HookDatas)); }
-};
-
-struct ParameterFader {
- enum {
- pfVolume = 1,
- pfTranspose = 3,
- pfSpeed = 4
- };
-
- int param;
- int start;
- int end;
- uint32 total_time;
- uint32 current_time;
-
- ParameterFader() { param = 0; }
- void init() { param = 0; }
-};
-
-struct DeferredCommand {
- uint32 time_left;
- int a, b, c, d, e, f;
- DeferredCommand() { memset(this, 0, sizeof(DeferredCommand)); }
-};
-
-struct ImTrigger {
- int sound;
- byte id;
- uint16 expire;
- int command [8];
- ImTrigger() { memset(this, 0, sizeof(ImTrigger)); }
-};
-
-struct CommandQueue {
- uint16 array[8];
- CommandQueue() { memset(this, 0, sizeof(CommandQueue)); }
-};
-
-class Player : public MidiDriver {
-protected:
- // Moved from IMuseInternal.
- // This is only used by one player at a time.
- static uint16 _active_notes[128];
-
-protected:
- MidiDriver *_midi;
- MidiParser *_parser;
- bool _passThrough; // Only respond to EOT, all else direct to MidiDriver
-
- Part *_parts;
- bool _active;
- bool _scanning;
- int _id;
- byte _priority;
- byte _volume;
- int8 _pan;
- int8 _transpose;
- int8 _detune;
- byte _vol_eff;
-
- uint _track_index;
- uint _loop_to_beat;
- uint _loop_from_beat;
- uint _loop_counter;
- uint _loop_to_tick;
- uint _loop_from_tick;
- byte _speed;
- bool _abort;
-
- // This does not get used by us! It is only
- // here for save/load purposes, and gets
- // passed on to the MidiParser during
- // fixAfterLoad().
- uint32 _music_tick;
-
- HookDatas _hook;
- ParameterFader _parameterFaders[4];
-
- bool _isMT32;
- bool _isMIDI;
-
-protected:
- // Player part
- void hook_clear();
- void uninit_parts();
- byte *parse_midi(byte *s);
- void part_set_transpose(uint8 chan, byte relative, int8 b);
- void parse_sysex(byte *p, uint len);
- void maybe_jump(byte cmd, uint track, uint beat, uint tick);
- void maybe_set_transpose(byte *data);
- void maybe_part_onoff(byte *data);
- void maybe_set_volume(byte *data);
- void maybe_set_program(byte *data);
- void maybe_set_transpose_part(byte *data);
- void turn_off_pedals();
- int query_part_param(int param, byte chan);
- void turn_off_parts();
- void play_active_notes();
-
- void transitionParameters();
-
- static void decode_sysex_bytes(const byte *src, byte *dst, int len);
-
- // Sequencer part
- int start_seq_sound(int sound, bool reset_vars = true);
- int query_param(int param);
-
-public:
- IMuseInternal *_se;
- uint _vol_chan;
-
-public:
- Player();
- virtual ~Player();
-
- int addParameterFader(int param, int target, int time);
- void clear();
- void clearLoop();
- void fixAfterLoad();
- Part * getActivePart(uint8 part);
- uint getBeatIndex();
- int8 getDetune() const { return _detune; }
- byte getEffectiveVolume() const { return _vol_eff; }
- int getID() const { return _id; }
- MidiDriver *getMidiDriver() const { return _midi; }
- int getParam(int param, byte chan);
- int8 getPan() const { return _pan; }
- Part * getPart(uint8 part);
- byte getPriority() const { return _priority; }
- uint getTicksPerBeat() const { return TICKS_PER_BEAT; }
- int8 getTranspose() const { return _transpose; }
- byte getVolume() const { return _volume; }
- bool isActive() const { return _active; }
- bool isFadingOut() const;
- bool isMIDI() const { return _isMIDI; }
- bool isMT32() const { return _isMT32; }
- bool jump(uint track, uint beat, uint tick);
- void onTimer();
- void removePart(Part *part);
- int scan(uint totrack, uint tobeat, uint totick);
- void saveLoadWithSerializer(Serializer *ser);
- int setHook(byte cls, byte value, byte chan) { return _hook.set(cls, value, chan); }
- void setDetune(int detune);
- bool setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
- void setPan(int pan);
- void setPriority(int pri);
- void setSpeed(byte speed);
- int setTranspose(byte relative, int b);
- int setVolume(byte vol);
- bool startSound(int sound, MidiDriver *midi, bool passThrough);
- int getMusicTimer() const;
-
-public:
- // MidiDriver interface
- int open() { return 0; }
- void close() { }
- void send(uint32 b);
- const char *getErrorName(int error_code) { return "Unknown"; }
- void sysEx(byte *msg, uint16 length);
- void metaEvent(byte type, byte *data, uint16 length);
- void setTimerCallback(void *timer_param, void(*timer_proc)(void *)) { }
- uint32 getBaseTempo();
- MidiChannel *allocateChannel() { return 0; }
- MidiChannel *getPercussionChannel() { return 0; }
-};
-
-struct Part : public Serializable {
- IMuseInternal *_se;
- int _slot;
- Part *_next, *_prev;
- MidiChannel *_mc;
- Player *_player;
- int16 _pitchbend;
- byte _pitchbend_factor;
- int8 _transpose, _transpose_eff;
- byte _vol, _vol_eff;
- int8 _detune, _detune_eff;
- int8 _pan, _pan_eff;
- bool _on;
- byte _modwheel;
- bool _pedal;
- int8 _pri;
- byte _pri_eff;
- byte _chan;
- byte _effect_level;
- byte _chorus;
- byte _percussion;
- byte _bank;
-
- // New abstract instrument definition
- Instrument _instrument;
- bool _unassigned_instrument; // For diagnostic reporting purposes only
-
- // MidiChannel interface
- // (We don't currently derive from MidiChannel,
- // but if we ever do, this will make it easy.)
- void noteOff(byte note);
- void noteOn(byte note, byte velocity);
- void programChange(byte value);
- void pitchBend(int16 value);
- void modulationWheel(byte value);
- void volume(byte value);
- void pitchBendFactor(byte value);
- void sustain(bool value);
- void effectLevel(byte value);
- void chorusLevel(byte value);
- void allNotesOff();
-
- void set_param(byte param, int value) { }
- void init();
- void setup(Player *player);
- void uninit();
- void off();
- void set_instrument(uint b);
- void set_instrument(byte *data);
- void load_global_instrument(byte b);
-
- void set_transpose(int8 transpose);
- void set_detune(int8 detune);
- void set_pri(int8 pri);
- void set_pan(int8 pan);
-
- void set_onoff(bool on);
- void fix_after_load();
-
- void sendAll();
- void sendPitchBend();
- bool clearToTransmit();
-
- Part();
-
- void saveLoadWithSerializer(Serializer *ser);
-};
-
-// WARNING: This is the internal variant of the IMUSE class.
-// imuse.h contains a public version of the same class.
-// the public version, only contains a set of methods.
-class IMuseInternal {
- friend class Player;
- friend struct Part;
-
-protected:
- bool _native_mt32;
- bool _enable_gs;
- bool _sc55;
- MidiDriver *_midi_adlib;
- MidiDriver *_midi_native;
-
- byte **_base_sounds;
-
-protected:
- bool _paused;
- bool _initialized;
-
- int _tempoFactor;
-
- int _player_limit; // Limits how many simultaneous music tracks are played
- bool _recycle_players; // Can we stop a player in order to start another one?
- bool _direct_passthrough; // Pass data direct to MidiDriver (no interactivity)
-
- uint _queue_end, _queue_pos, _queue_sound;
- byte _queue_adding;
-
- byte _queue_marker;
- byte _queue_cleared;
- byte _master_volume; // Master volume. 0-255
- byte _music_volume; // Global music volume. 0-255
-
- uint16 _trigger_count;
- ImTrigger _snm_triggers[16]; // Sam & Max triggers
- uint16 _snm_trigger_index;
-
- uint16 _channel_volume[8];
- uint16 _channel_volume_eff[8]; // No Save
- uint16 _volchan_table[8];
-
- Player _players[8];
- Part _parts[32];
-
- Instrument _global_adlib_instruments[32];
- CommandQueue _cmd_queue[64];
- DeferredCommand _deferredCommands[4];
-
-protected:
- byte *findStartOfSound(int sound);
- bool isMT32(int sound);
- bool isMIDI(int sound);
- int get_queue_sound_status(int sound) const;
- void handle_marker(uint id, byte data);
- int get_channel_volume(uint a);
- void initMidiDriver(MidiDriver *midi);
- void initGM(MidiDriver *midi);
- void initMT32(MidiDriver *midi);
- void init_players();
- void init_parts();
- void init_queue();
-
- void sequencer_timers(MidiDriver *midi);
-
- MidiDriver *getBestMidiDriver(int sound);
- Player *allocate_player(byte priority);
- Part *allocate_part(byte pri, MidiDriver *midi);
-
- int32 ImSetTrigger(int sound, int id, int a, int b, int c, int d, int e, int f, int g, int h);
- int32 ImClearTrigger(int sound, int id);
- int32 ImFireAllTriggers(int sound);
-
- void addDeferredCommand(int time, int a, int b, int c, int d, int e, int f);
- void handleDeferredCommands(MidiDriver *midi);
-
- int enqueue_command(int a, int b, int c, int d, int e, int f, int g);
- int enqueue_trigger(int sound, int marker);
- int query_queue(int param);
- Player *findActivePlayer(int id);
-
- int get_volchan_entry(uint a);
- int set_volchan_entry(uint a, uint b);
- int set_channel_volume(uint chan, uint vol);
- void update_volumes();
- void reset_tick();
-
- int set_volchan(int sound, int volchan);
-
- void fix_parts_after_load();
- void fix_players_after_load(ScummEngine *scumm);
-
- static void midiTimerCallback(void *data);
-
-public:
- IMuseInternal();
-
- int initialize(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
- void reallocateMidiChannels(MidiDriver *midi);
- void setGlobalAdlibInstrument(byte slot, byte *data);
- void copyGlobalAdlibInstrument(byte slot, Instrument *dest);
- bool isNativeMT32() { return _native_mt32; }
-
- // IMuse interface
-
- void on_timer(MidiDriver *midi);
- void pause(bool paused);
- int terminate1();
- int terminate2();
- int save_or_load(Serializer *ser, ScummEngine *scumm);
- int setMusicVolume(uint vol);
- int setImuseMasterVolume(uint vol);
- bool startSound(int sound);
- int stopSound(int sound);
- int stopAllSounds();
- int getSoundStatus(int sound, bool ignoreFadeouts = true) const;
- int getMusicTimer() const;
- int32 doCommand (int a, int b, int c, int d, int e, int f, int g, int h);
- int32 doCommand (int numargs, int args[]);
- int clear_queue();
- void setBase(byte **base);
- uint32 property(int prop, uint32 value);
-
- static IMuseInternal *create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver);
-};
-
-} // End of namespace Scumm
-
-#endif
Deleted: scummvm/trunk/engines/scumm/imuse_player.cpp
===================================================================
--- scummvm/trunk/engines/scumm/imuse_player.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/imuse_player.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,1241 +0,0 @@
-/* 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"
-#include "base/engine.h"
-
-#include "scumm/imuse_internal.h"
-#include "scumm/saveload.h"
-#include "scumm/scumm.h"
-
-#include "sound/midiparser.h"
-
-namespace Scumm {
-
-////////////////////////////////////////
-//
-// Miscellaneous
-//
-////////////////////////////////////////
-
-#define IMUSE_SYSEX_ID 0x7D
-#define YM2612_SYSEX_ID 0x7C
-#define ROLAND_SYSEX_ID 0x41
-#define PERCUSSION_CHANNEL 9
-
-extern MidiParser *MidiParser_createRO();
-extern MidiParser *MidiParser_createEUP();
-
-uint16 Player::_active_notes[128];
-
-
-
-//////////////////////////////////////////////////
-//
-// IMuse Player implementation
-//
-//////////////////////////////////////////////////
-
-Player::Player() :
- _midi(0),
- _parser(0),
- _passThrough(0),
- _parts(0),
- _active(false),
- _scanning(false),
- _id(0),
- _priority(0),
- _volume(0),
- _pan(0),
- _transpose(0),
- _detune(0),
- _vol_eff(0),
- _track_index(0),
- _loop_to_beat(0),
- _loop_from_beat(0),
- _loop_counter(0),
- _loop_to_tick(0),
- _loop_from_tick(0),
- _speed(128),
- _isMT32(false),
- _isMIDI(false),
- _se(0),
- _vol_chan(0){
-}
-
-Player::~Player() {
- if (_parser) {
- delete _parser;
- _parser = 0;
- }
-}
-
-bool Player::startSound(int sound, MidiDriver *midi, bool passThrough) {
- void *ptr;
- int i;
-
- // Not sure what the old code was doing,
- // but we'll go ahead and do a similar check.
- ptr = _se->findStartOfSound(sound);
- if (!ptr) {
- error("Player::startSound(): Couldn't find start of sound %d!", sound);
- return false;
- }
-
- _isMT32 = _se->isMT32(sound);
- _isMIDI = _se->isMIDI(sound);
-
- _parts = NULL;
- _active = true;
- _midi = midi;
- _id = sound;
- _priority = 0x80;
- _volume = 0x7F;
- _vol_chan = 0xFFFF;
- _vol_eff = (_se->get_channel_volume(0xFFFF) << 7) >> 7;
- _pan = 0;
- _transpose = 0;
- _detune = 0;
- _passThrough = passThrough;
-
- for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i)
- _parameterFaders[i].init();
- hook_clear();
-
- if (start_seq_sound(sound) != 0) {
- _active = false;
- _midi = NULL;
- return false;
- }
-
-#ifdef IMUSE_DEBUG
- debug(0, "Starting music %d", sound);
-#endif
- return true;
-}
-
-int Player::getMusicTimer() const {
- return _parser ? (_parser->getTick() * 2 / _parser->getPPQN()) : 0;
-}
-
-bool Player::isFadingOut() const {
- int i;
- for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) {
- if (_parameterFaders[i].param == ParameterFader::pfVolume &&
- _parameterFaders[i].end == 0) {
- return true;
- }
- }
- return false;
-}
-
-void Player::clear() {
- if (!_active)
- return;
-
-#ifdef IMUSE_DEBUG
- debug(0, "Stopping music %d", _id);
-#endif
-
- if (_parser) {
- _parser->unloadMusic();
- delete _parser;
- _parser = 0;
- }
- uninit_parts();
- _se->ImFireAllTriggers(_id);
- _active = false;
- _midi = NULL;
- _id = 0;
-}
-
-void Player::hook_clear() {
- memset(&_hook, 0, sizeof(_hook));
-}
-
-int Player::start_seq_sound(int sound, bool reset_vars) {
- byte *ptr;
-
- if (reset_vars) {
- _loop_to_beat = 1;
- _loop_from_beat = 1;
- _track_index = 0;
- _loop_counter = 0;
- _loop_to_tick = 0;
- _loop_from_tick = 0;
- }
-
- ptr = _se->findStartOfSound(sound);
- if (ptr == NULL)
- return -1;
- if (_parser)
- delete _parser;
-
- if (!memcmp(ptr, "RO", 2)) {
- // Old style 'RO' resource
- _parser = MidiParser_createRO();
- } else if (!memcmp(ptr, "SO", 2)) {
- // Euphony (FM-TOWNS) resource
- _parser = MidiParser_createEUP();
- } else if (!memcmp(ptr, "FORM", 4)) {
- // Humongous Games XMIDI resource
- _parser = MidiParser::createParser_XMIDI();
- } else {
- // SCUMM SMF resource
- _parser = MidiParser::createParser_SMF();
- }
-
- _parser->setMidiDriver(this);
- _parser->property(MidiParser::mpSmartJump, 1);
- _parser->loadMusic(ptr, 0);
- _parser->setTrack(_track_index);
- setSpeed(reset_vars ? 128 : _speed);
-
- return 0;
-}
-
-void Player::uninit_parts() {
- if (_parts && _parts->_player != this)
- error("asd");
- while (_parts)
- _parts->uninit();
-
- // In case another player is waiting to allocate parts
- if (_midi)
- _se->reallocateMidiChannels(_midi);
-}
-
-void Player::setSpeed(byte speed) {
- _speed = speed;
- if (_parser)
- _parser->setTimerRate(((_midi->getBaseTempo() * speed) >> 7) * _se->_tempoFactor / 100);
-}
-
-void Player::send(uint32 b) {
- if (_passThrough) {
- _midi->send(b);
- return;
- }
-
- byte cmd = (byte)(b & 0xF0);
- byte chan = (byte)(b & 0x0F);
- byte param1 = (byte)((b >> 8) & 0xFF);
- byte param2 = (byte)((b >> 16) & 0xFF);
- Part *part;
-
- switch (cmd >> 4) {
- case 0x8: // Key Off
- if (!_scanning) {
- if ((part = getPart(chan)) != 0)
- part->noteOff(param1);
- } else {
- _active_notes[param1] &= ~(1 << chan);
- }
- break;
-
- case 0x9: // Key On
- if (!_scanning) {
- if (_isMT32 && !_se->isNativeMT32())
- param2 = (((param2 * 3) >> 2) + 32) & 0x7F;
- if ((part = getPart(chan)) != 0)
- part->noteOn(param1, param2);
- } else {
- _active_notes[param1] |= (1 << chan);
- }
- break;
-
- case 0xB: // Control Change
- part = (param1 == 123 ? getActivePart(chan) : getPart(chan));
- if (!part)
- break;
-
- switch (param1) {
- case 0: // Bank select. Not supported
- break;
- case 1: // Modulation Wheel
- part->modulationWheel(param2);
- break;
- case 7: // Volume
- part->volume(param2);
- break;
- case 10: // Pan Position
- part->set_pan(param2 - 0x40);
- break;
- case 16: // Pitchbend Factor(non-standard)
- part->pitchBendFactor(param2);
- break;
- case 17: // GP Slider 2
- part->set_detune(param2 - 0x40);
- break;
- case 18: // GP Slider 3
- part->set_pri(param2 - 0x40);
- _se->reallocateMidiChannels(_midi);
- break;
- case 64: // Sustain Pedal
- part->sustain(param2 != 0);
- break;
- case 91: // Effects Level
- part->effectLevel(param2);
- break;
- case 93: // Chorus Level
- part->chorusLevel(param2);
- break;
- case 116: // XMIDI For Loop. Not supported
- // Used in the ending sequence of puttputt
- break;
- case 117: // XMIDI Next/Break. Not supported
- // Used in the ending sequence of puttputt
- break;
- case 123: // All Notes Off
- part->allNotesOff();
- break;
- default:
- error("Player::send(): Invalid control change %d", param1);
- }
- break;
-
- case 0xC: // Program Change
- part = getPart(chan);
- if (part) {
- if (_isMIDI) {
- if (param1 < 128)
- part->programChange(param1);
- } else {
- if (param1 < 32)
- part->load_global_instrument(param1);
- }
- }
- break;
-
- case 0xE: // Pitch Bend
- part = getPart(chan);
- if (part)
- part->pitchBend(((param2 << 7) | param1) - 0x2000);
- break;
-
- case 0xA: // Aftertouch
- case 0xD: // Channel Pressure
- case 0xF: // Sequence Controls
- break;
-
- default:
- if (!_scanning) {
- error("Player::send(): Invalid command %d", cmd);
- clear();
- }
- }
- return;
-}
-
-void Player::sysEx(byte *p, uint16 len) {
- byte code;
- byte a;
- uint b;
- byte buf[128];
- Part *part;
-
- if (_passThrough) {
- _midi->sysEx(p, len);
- return;
- }
-
- // Check SysEx manufacturer.
- a = *p++;
- --len;
- if (a != IMUSE_SYSEX_ID) {
- if (a == ROLAND_SYSEX_ID) {
- // Roland custom instrument definition.
- part = getPart(p[0] & 0x0F);
- if (part) {
- part->_instrument.roland(p - 1);
- if (part->clearToTransmit())
- part->_instrument.send(part->_mc);
- }
- } else if (a == YM2612_SYSEX_ID) {
- // FM-TOWNS custom instrument definition
- _midi->sysEx_customInstrument(p[0], 'EUP ', p + 1);
- } else {
- error("Unknown SysEx manufacturer 0x%02X", (int)a);
- }
- return;
- }
- --len;
-
- // Too big?
- if (len >= sizeof(buf) * 2)
- return;
-
-#ifdef IMUSE_DEBUG
- if (!_scanning) {
- for (a = 0; a < len + 1 && a < 19; ++a) {
- sprintf((char *)&buf[a*3], " %02X", p[a]);
- } // next for
- if (a < len + 1) {
- buf[a*3] = buf[a*3+1] = buf[a*3+2] = '.';
- ++a;
- } // end if
- buf[a*3] = '\0';
- debug(0, "[%02d] SysEx:%s", _id, buf);
- }
-#endif
-
- switch (code = *p++) {
- case 0:
- if (g_scumm->_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)
- // BYTE 04: Priority adjustment [guessing]
- // BYTE 05: Volume(upper 4 bits) [guessing]
- // BYTE 06: Volume(lower 4 bits) [guessing]
- // BYTE 09: BIT 04(0x08): Percussion?(1 = yes)
- // 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->set_pri(p[4]);
- part->volume((p[5] & 0x0F) << 4 |(p[6] & 0x0F));
- part->_percussion = _isMIDI ? ((p[9] & 0x08) > 0) : false;
- 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:
- // This SysEx is used in Sam & Max for 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);
- }
-}
-
-void Player::decode_sysex_bytes(const byte *src, byte *dst, int len) {
- while (len >= 0) {
- *dst++ = ((src[0] << 4)&0xFF) | (src[1] & 0xF);
- src += 2;
- len -= 2;
- }
-}
-
-void Player::maybe_jump(byte cmd, uint track, uint beat, uint tick) {
- // Is this the hook I'm waiting for?
- if (cmd && _hook._jump[0] != cmd)
- return;
-
- // Reset hook?
- if (cmd != 0 && cmd < 0x80) {
- _hook._jump[0] = _hook._jump[1];
- _hook._jump[1] = 0;
- }
-
- jump(track, beat, tick);
-}
-
-void Player::maybe_set_transpose(byte *data) {
- byte cmd;
-
- cmd = data[0];
-
- // Is this the hook I'm waiting for?
- if (cmd && _hook._transpose != cmd)
- return;
-
- // Reset hook?
- if (cmd != 0 && cmd < 0x80)
- _hook._transpose = 0;
-
- setTranspose(data[1], (int8)data[2]);
-}
-
-void Player::maybe_part_onoff(byte *data) {
- byte cmd, *p;
- uint chan;
- Part *part;
-
- cmd = data[1];
- chan = data[0];
-
- p = &_hook._part_onoff[chan];
-
- // Is this the hook I'm waiting for?
- if (cmd && *p != cmd)
- return;
-
- if (cmd != 0 && cmd < 0x80)
- *p = 0;
-
- part = getPart(chan);
- if (part)
- part->set_onoff(data[2] != 0);
-}
-
-void Player::maybe_set_volume(byte *data) {
- byte cmd;
- byte *p;
- uint chan;
- Part *part;
-
- cmd = data[1];
- chan = data[0];
-
- p = &_hook._part_volume[chan];
-
- // Is this the hook I'm waiting for?
- if (cmd && *p != cmd)
- return;
-
- // Reset hook?
- if (cmd != 0 && cmd < 0x80)
- *p = 0;
-
- part = getPart(chan);
- if (part)
- part->volume(data[2]);
-}
-
-void Player::maybe_set_program(byte *data) {
- byte cmd;
- byte *p;
- uint chan;
- Part *part;
-
- cmd = data[1];
- chan = data[0];
-
- // Is this the hook I'm waiting for?
- p = &_hook._part_program[chan];
-
- if (cmd && *p != cmd)
- return;
-
- if (cmd != 0 && cmd < 0x80)
- *p = 0;
-
- part = getPart(chan);
- if (part)
- part->programChange(data[2]);
-}
-
-void Player::maybe_set_transpose_part(byte *data) {
- byte cmd;
- byte *p;
- uint chan;
-
- cmd = data[1];
- chan = data[0];
-
- // Is this the hook I'm waiting for?
- p = &_hook._part_transpose[chan];
-
- if (cmd && *p != cmd)
- return;
-
- // Reset hook?
- if (cmd != 0 && cmd < 0x80)
- *p = 0;
-
- part_set_transpose(chan, data[2], (int8)data[3]);
-}
-
-int Player::setTranspose(byte relative, int b) {
- Part *part;
-
- if (b > 24 || b < -24 || relative > 1)
- return -1;
- if (relative)
- b = transpose_clamp(_transpose + b, -24, 24);
-
- _transpose = b;
-
- for (part = _parts; part; part = part->_next) {
- part->set_transpose(part->_transpose);
- }
-
- return 0;
-}
-
-void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
- Part *part;
-
- if (b > 24 || b < -24)
- return;
-
- part = getPart(chan);
- if (!part)
- return;
- if (relative)
- b = transpose_clamp(b + part->_transpose, -7, 7);
- part->set_transpose(b);
-}
-
-bool Player::jump(uint track, uint beat, uint tick) {
- if (!_parser)
- return false;
- if (_parser->setTrack(track))
- _track_index = track;
- if (!_parser->jumpToTick((beat - 1) * TICKS_PER_BEAT + tick))
- return false;
- turn_off_pedals();
- return true;
-}
-
-bool Player::setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
- if (tobeat + 1 >= frombeat)
- return false;
-
- if (tobeat == 0)
- tobeat = 1;
-
- _loop_counter = 0; // Because of possible interrupts
- _loop_to_beat = tobeat;
- _loop_to_tick = totick;
- _loop_from_beat = frombeat;
- _loop_from_tick = fromtick;
- _loop_counter = count;
-
- return true;
-}
-
-void Player::clearLoop() {
- _loop_counter = 0;
-}
-
-void Player::turn_off_pedals() {
- Part *part;
-
- for (part = _parts; part; part = part->_next) {
- if (part->_pedal)
- part->sustain(false);
- }
-}
-
-Part *Player::getActivePart(uint8 chan) {
- Part *part = _parts;
- while (part) {
- if (part->_chan == chan)
- return part;
- part = part->_next;
- }
- return 0;
-}
-
-Part *Player::getPart(uint8 chan) {
- Part *part = getActivePart(chan);
- if (part)
- return part;
-
- part = _se->allocate_part(_priority, _midi);
- if (!part) {
- debug(1, "No parts available");
- return NULL;
- }
-
- // Insert part into front of parts list
- part->_prev = NULL;
- part->_next = _parts;
- if (_parts)
- _parts->_prev = part;
- _parts = part;
-
-
- part->_chan = chan;
- part->setup(this);
-
- return part;
-}
-
-void Player::setPriority(int pri) {
- Part *part;
-
- _priority = pri;
- for (part = _parts; part; part = part->_next) {
- part->set_pri(part->_pri);
- }
- _se->reallocateMidiChannels(_midi);
-}
-
-void Player::setPan(int pan) {
- Part *part;
-
- _pan = pan;
- for (part = _parts; part; part = part->_next) {
- part->set_pan(part->_pan);
- }
-}
-
-void Player::setDetune(int detune) {
- Part *part;
-
- _detune = detune;
- for (part = _parts; part; part = part->_next) {
- part->set_detune(part->_detune);
- }
-}
-
-int Player::scan(uint totrack, uint tobeat, uint totick) {
- if (!_active || !_parser)
- return -1;
-
- if (tobeat == 0)
- tobeat++;
-
- turn_off_parts();
- memset(_active_notes, 0, sizeof(_active_notes));
- _scanning = true;
-
- // If the scan involves a track switch, scan to the end of
- // the current track so that our state when starting the
- // new track is fully up to date.
- if (totrack != _track_index)
- _parser->jumpToTick((uint32)-1, true);
- _parser->setTrack(totrack);
- if (!_parser->jumpToTick((tobeat - 1) * TICKS_PER_BEAT + totick, true)) {
- _scanning = false;
- return -1;
- }
-
- _scanning = false;
- _se->reallocateMidiChannels(_midi);
- play_active_notes();
-
- if (_track_index != totrack) {
- _track_index = totrack;
- _loop_counter = 0;
- }
- return 0;
-}
-
-void Player::turn_off_parts() {
- Part *part;
-
- for (part = _parts; part; part = part->_next)
- part->off();
- _se->reallocateMidiChannels(_midi);
-}
-
-void Player::play_active_notes() {
- int i, j;
- uint mask;
- Part *part;
-
- for (i = 0; i < 16; ++i) {
- part = getPart(i);
- if (part) {
- mask = 1 << i;
- for (j = 0; j < 128; ++j) {
- if (_active_notes[j] & mask)
- part->noteOn(j, 80);
- }
- }
- }
-}
-
-int Player::setVolume(byte vol) {
- Part *part;
-
- if (vol > 127)
- return -1;
-
- _volume = vol;
- _vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
-
- for (part = _parts; part; part = part->_next) {
- part->volume(part->_vol);
- }
-
- return 0;
-}
-
-int Player::getParam(int param, byte chan) {
- switch (param) {
- case 0:
- return (byte)_priority;
- case 1:
- return (byte)_volume;
- case 2:
- return (byte)_pan;
- case 3:
- return (byte)_transpose;
- case 4:
- return (byte)_detune;
- case 5:
- return _speed;
- case 6:
- return _track_index;
- case 7:
- return getBeatIndex();
- case 8:
- return (_parser ? _parser->getTick() % TICKS_PER_BEAT : 0); // _tick_index;
- case 9:
- return _loop_counter;
- case 10:
- return _loop_to_beat;
- case 11:
- return _loop_to_tick;
- case 12:
- return _loop_from_beat;
- case 13:
- return _loop_from_tick;
- case 14:
- case 15:
- case 16:
- case 17:
- return query_part_param(param, chan);
- case 18:
- case 19:
- case 20:
- case 21:
- case 22:
- case 23:
- return _hook.query_param(param, chan);
- default:
- return -1;
- }
-}
-
-int Player::query_part_param(int param, byte chan) {
- Part *part;
-
- part = _parts;
- while (part) {
- if (part->_chan == chan) {
- switch (param) {
- case 14:
- return part->_on;
- case 15:
- return part->_vol;
- case 16:
-// FIXME: Need to know where this occurs...
-error("Trying to cast instrument (%d, %d) -- please tell Fingolfin\n", param, chan);
-// In old versions of the code, this used to return part->_program.
-// This was changed in revision 2.29 of imuse.cpp (where this code used
-// to reside).
-// return (int)part->_instrument;
- case 17:
- return part->_transpose;
- default:
- return -1;
- }
- }
- part = part->_next;
- }
- return 129;
-}
-
-void Player::onTimer() {
- // First handle any parameter transitions
- // that are occuring.
- transitionParameters();
-
- // Since the volume parameter can cause
- // the player to be deactivated, check
- // to make sure we're still active.
- if (!_active || !_parser)
- return;
-
- uint32 target_tick = _parser->getTick();
- uint beat_index = target_tick / TICKS_PER_BEAT + 1;
- uint tick_index = target_tick % TICKS_PER_BEAT;
-
- if (_loop_counter &&(beat_index > _loop_from_beat ||
- (beat_index == _loop_from_beat && tick_index >= _loop_from_tick)))
- {
- _loop_counter--;
- jump(_track_index, _loop_to_beat, _loop_to_tick);
- }
- _parser->onTimer();
-}
-
-// "time" is referenced as hundredths of a second.
-// IS THAT CORRECT??
-// We convert it to microseconds before proceeding
-int Player::addParameterFader(int param, int target, int time) {
- int start;
-
- switch (param) {
- case ParameterFader::pfVolume:
- // HACK: If volume is set to 0 with 0 time,
- // set it so immediately but DON'T clear
- // the player. This fixes a problem with
- // music being cleared inappropriately
- // in S&M when playing with the Dinosaur.
- if (!target && !time) {
- setVolume(0);
- return 0;
- }
-
- // Volume fades are handled differently.
- start = _volume;
- break;
-
- case ParameterFader::pfTranspose:
- // FIXME: Is this transpose? And what's the scale?
- // It's set to fade to -2400 in the tunnel of love.
-// debug(0, "parameterTransition(3) outside Tunnel of Love?");
- start = _transpose;
-// target /= 200;
- break;
-
- case ParameterFader::pfSpeed: // impSpeed
- // FIXME: Is the speed from 0-100?
- // Right now I convert it to 0-128.
- start = _speed;
-// target = target * 128 / 100;
- break;
-
- case 127:
- { // FIXME? I *think* this clears all parameter faders.
- ParameterFader *ptr = &_parameterFaders[0];
- int i;
- for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr)
- ptr->param = 0;
- return 0;
- }
- break;
-
- default:
- debug(0, "Player::addParameterFader (%d, %d, %d): Unknown parameter", param, target, time);
- return 0; // Should be -1, but we'll let the script think it worked.
- }
-
- ParameterFader *ptr = &_parameterFaders[0];
- ParameterFader *best = 0;
- int i;
- for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
- if (ptr->param == param) {
- best = ptr;
- start = ptr->end;
- break;
- } else if (!ptr->param) {
- best = ptr;
- }
- }
-
- if (best) {
- best->param = param;
- best->start = start;
- best->end = target;
- if (!time)
- best->total_time = 1;
- else
- best->total_time = (uint32)time * 10000;
- best->current_time = 0;
- } else {
- debug(0, "IMuse Player %d: Out of parameter faders", _id);
- return -1;
- }
-
- return 0;
-}
-
-void Player::transitionParameters() {
- uint32 advance = _midi->getBaseTempo();
- int value;
-
- ParameterFader *ptr = &_parameterFaders[0];
- int i;
- for (i = ARRAYSIZE(_parameterFaders); i; --i, ++ptr) {
- if (!ptr->param)
- continue;
-
- ptr->current_time += advance;
- if (ptr->current_time > ptr->total_time)
- ptr->current_time = ptr->total_time;
- value = (int32)ptr->start + (int32)(ptr->end - ptr->start) * (int32)ptr->current_time / (int32)ptr->total_time;
-
- switch (ptr->param) {
- case ParameterFader::pfVolume:
- // Volume.
- if (!value && !ptr->end) {
- clear();
- return;
- }
- setVolume((byte)value);
- break;
-
- case ParameterFader::pfTranspose:
- // FIXME: Is this really transpose?
- setTranspose(0, value / 100);
- setDetune(value % 100);
- break;
-
- case ParameterFader::pfSpeed: // impSpeed:
- // Speed.
- setSpeed((byte)value);
- break;
-
- default:
- ptr->param = 0;
- }
-
- if (ptr->current_time >= ptr->total_time)
- ptr->param = 0;
- }
-}
-
-uint Player::getBeatIndex() {
- return (_parser ? (_parser->getTick() / TICKS_PER_BEAT + 1) : 0);
-}
-
-void Player::removePart(Part *part) {
- // Unlink
- if (part->_next)
- part->_next->_prev = part->_prev;
- if (part->_prev)
- part->_prev->_next = part->_next;
- else
- _parts = part->_next;
- part->_next = part->_prev = 0;
-}
-
-void Player::fixAfterLoad() {
- _midi = _se->getBestMidiDriver(_id);
- if (!_midi) {
- clear();
- } else {
- start_seq_sound(_id, false);
- setSpeed(_speed);
- if (_parser)
- _parser->jumpToTick(_music_tick); // start_seq_sound already switched tracks
- _isMT32 = _se->isMT32(_id);
- _isMIDI = _se->isMIDI(_id);
- }
-}
-
-uint32 Player::getBaseTempo() {
- return (_midi ? _midi->getBaseTempo() : 0);
-}
-
-void Player::metaEvent(byte type, byte *msg, uint16 len) {
- if (type == 0x2F)
- clear();
-}
-
-
-
-////////////////////////////////////////
-//
-// Player save/load functions
-//
-////////////////////////////////////////
-
-void Player::saveLoadWithSerializer(Serializer *ser) {
- static const SaveLoadEntry playerEntries[] = {
- MKLINE(Player, _active, sleByte, VER(8)),
- MKLINE(Player, _id, sleUint16, VER(8)),
- MKLINE(Player, _priority, sleByte, VER(8)),
- MKLINE(Player, _volume, sleByte, VER(8)),
- MKLINE(Player, _pan, sleInt8, VER(8)),
- MKLINE(Player, _transpose, sleByte, VER(8)),
- MKLINE(Player, _detune, sleInt8, VER(8)),
- MKLINE(Player, _vol_chan, sleUint16, VER(8)),
- MKLINE(Player, _vol_eff, sleByte, VER(8)),
- MKLINE(Player, _speed, sleByte, VER(8)),
- MK_OBSOLETE(Player, _song_index, sleUint16, VER(8), VER(19)),
- MKLINE(Player, _track_index, sleUint16, VER(8)),
- MK_OBSOLETE(Player, _timer_counter, sleUint16, VER(8), VER(17)),
- MKLINE(Player, _loop_to_beat, sleUint16, VER(8)),
- MKLINE(Player, _loop_from_beat, sleUint16, VER(8)),
- MKLINE(Player, _loop_counter, sleUint16, VER(8)),
- MKLINE(Player, _loop_to_tick, sleUint16, VER(8)),
- MKLINE(Player, _loop_from_tick, sleUint16, VER(8)),
- MK_OBSOLETE(Player, _tempo, sleUint32, VER(8), VER(19)),
- MK_OBSOLETE(Player, _cur_pos, sleUint32, VER(8), VER(17)),
- MK_OBSOLETE(Player, _next_pos, sleUint32, VER(8), VER(17)),
- MK_OBSOLETE(Player, _song_offset, sleUint32, VER(8), VER(17)),
- MK_OBSOLETE(Player, _tick_index, sleUint16, VER(8), VER(17)),
- MK_OBSOLETE(Player, _beat_index, sleUint16, VER(8), VER(17)),
- MK_OBSOLETE(Player, _ticks_per_beat, sleUint16, VER(8), VER(17)),
- MKLINE(Player, _music_tick, sleUint32, VER(19)),
- MKLINE(Player, _hook._jump[0], sleByte, VER(8)),
- MKLINE(Player, _hook._transpose, sleByte, VER(8)),
- MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER(8)),
- MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER(8)),
- MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER(8)),
- MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER(8)),
- MKEND()
- };
-
- const SaveLoadEntry parameterFaderEntries[] = {
- MKLINE(ParameterFader, param, sleInt16, VER(17)),
- MKLINE(ParameterFader, start, sleInt16, VER(17)),
- MKLINE(ParameterFader, end, sleInt16, VER(17)),
- MKLINE(ParameterFader, total_time, sleUint32, VER(17)),
- MKLINE(ParameterFader, current_time, sleUint32, VER(17)),
- MKEND()
- };
-
- if (!ser->isSaving() && _parser) {
- delete _parser;
- _parser = 0;
- }
- _music_tick = _parser ? _parser->getTick() : 0;
-
- int num;
- if (ser->isSaving()) {
- num = (_parts ? (_parts - _se->_parts + 1) : 0);
- ser->saveUint16(num);
- } else {
- num = ser->loadUint16();
- _parts = (num ? &_se->_parts[num - 1] : 0);
- }
- ser->saveLoadEntries(this, playerEntries);
- ser->saveLoadArrayOf(_parameterFaders, ARRAYSIZE(_parameterFaders),
- sizeof(ParameterFader), parameterFaderEntries);
- return;
-}
-
-} // End of namespace Scumm
Modified: scummvm/trunk/engines/scumm/input.cpp
===================================================================
--- scummvm/trunk/engines/scumm/input.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/input.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -32,7 +32,7 @@
#include "scumm/debugger.h"
#include "scumm/dialogs.h"
#include "scumm/insane/insane.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#ifndef DISABLE_HE
#include "scumm/he/intern_he.h"
#include "scumm/he/logic_he.h"
Modified: scummvm/trunk/engines/scumm/insane/insane.cpp
===================================================================
--- scummvm/trunk/engines/scumm/insane/insane.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/insane/insane.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -31,7 +31,7 @@
#include "scumm/actor.h"
#include "scumm/sound.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/smush/smush_player.h"
Deleted: scummvm/trunk/engines/scumm/instrument.cpp
===================================================================
--- scummvm/trunk/engines/scumm/instrument.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/instrument.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,462 +0,0 @@
-/* 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 "scumm/scumm.h"
-#include "scumm/saveload.h"
-#include "scumm/instrument.h"
-#include "sound/mididrv.h"
-
-namespace Scumm {
-
-static bool _native_mt32 = false;
-
-static struct {
- const char *name;
- byte program;
-}
-
-roland_to_gm_map [] = {
- // Monkey Island 2 instruments
- // TODO: Complete
- { "badspit ", 62 },
- { "Big Drum ", 116 },
- { "burp ", 58 },
-// { "dinkfall ", ??? },
-// { "Fire Pit ", ??? },
- { "foghorn ", 60 },
- { "glop ", 39 },
-// { "jacob's la", ??? },
- { "LeshBass ", 33 },
-// { "lowsnort ", ??? },
- { "ML explosn", 127 },
- { "ReggaeBass", 32 },
-// { "rope fall ", ??? },
- { "rumble ", 89 },
- { "SdTrk Bend", 97 },
-// { "snort ", ??? },
- { "spitting ", 62 },
- { "Swell 1 ", 95 },
- { "Swell 2 ", 95 },
- { "thnderclap", 127 }
-
- // Fate of Atlantis instruments
- // TODO: Build
-// { "*aah! ", ??? },
-// { "*ooh! ", ??? },
-// { "*ShotFar4 ", ??? },
-// { "*splash3 ", ??? },
-// { "*torpedo5 ", ??? },
-// { "*whip3 ", ??? },
-// { "*woodknock", ??? },
-// { "35 lavabub", ??? },
-// { "49 bzzt! ", ??? },
-// { "applause ", ??? },
-// { "Arabongo ", ??? },
-// { "Big Drum ", ??? }, // DUPLICATE (todo: confirm)
-// { "bodythud1 ", ??? },
-// { "boneKLOK2 ", ??? },
-// { "boom10 ", ??? },
-// { "boom11 ", ??? },
-// { "boom15 ", ??? },
-// { "boxclik1a ", ??? },
-// { "brassbonk3", ??? },
-// { "carstart ", ??? },
-// { "cb tpt 2 ", ??? },
-// { "cell door ", ??? },
-// { "chains ", ??? },
-// { "crash ", ??? },
-// { "crsrt/idl3", ??? },
-// { "Fire Pit ", ??? }, // DUPLICATE (todo: confirm)
-// { "Fzooom ", ??? },
-// { "Fzooom 2 ", ??? },
-// { "ghostwhosh", ??? },
-// { "glasssmash", ??? },
-// { "gloop2 ", ??? },
-// { "gunShotNea", ??? },
-// { "idoorclse ", ??? },
-// { "knife ", ??? },
-// { "lavacmbl4 ", ??? },
-// { "Mellow Str", ??? },
-// { "mtlheater1", ??? },
-// { "pachinko5 ", ??? },
-// { "Ping1 ", ??? },
-// { "rockcrunch", ??? },
-// { "rumble ", ??? }, // DUPLICATE (todo: confirm)
-// { "runngwatr ", ??? },
-// { "scrape2 ", ??? },
-// { "snakeHiss ", ??? },
-// { "snort ", ??? }, // DUPLICATE (todo: confirm)
-// { "spindle4 ", ??? },
-// { "splash2 ", ??? },
-// { "squirel ", ??? },
-// { "steam3 ", ??? },
-// { "stonwheel6", ??? },
-// { "street ", ??? },
-// { "trickle4 ", ??? }
-};
-
-const byte Instrument::_gmRhythmMap[35] = {
- 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
- 0, 0, 0, 0, 0, 0, 0, 0, 36, 37, 38, 39, 40, 41, 66, 47,
- 65, 48, 56};
- // This emulates the percussion bank setup LEC used with the MT-32,
- // where notes 24 - 34 were assigned instruments without reverb.
- // It also fixes problems on GS devices that map sounds to these
- // notes by default.
-
-class Instrument_Program : public InstrumentInternal {
-private:
- byte _program;
- bool _mt32;
-
-public:
- Instrument_Program (byte program, bool mt32);
- Instrument_Program (Serializer *s);
- void saveOrLoad (Serializer *s);
- void send (MidiChannel *mc);
- void copy_to (Instrument *dest) { dest->program (_program, _mt32); }
- bool is_valid() {
- return (_program < 128) &&
- ((_native_mt32 == _mt32) || _native_mt32
- ? (MidiDriver::_gmToMt32[_program] < 128)
- : (MidiDriver::_mt32ToGm[_program] < 128)); }
-};
-
-class Instrument_Adlib : public InstrumentInternal {
-private:
- struct {
- byte flags_1;
- byte oplvl_1;
- byte atdec_1;
- byte sustrel_1;
- byte waveform_1;
- byte flags_2;
- byte oplvl_2;
- byte atdec_2;
- byte sustrel_2;
- byte waveform_2;
- byte feedback;
- byte flags_a;
- struct { byte a,b,c,d,e,f,g,h; } extra_a;
- byte flags_b;
- struct { byte a,b,c,d,e,f,g,h; } extra_b;
- byte duration;
- } _instrument;
-
-public:
- Instrument_Adlib (byte *data);
- Instrument_Adlib (Serializer *s);
- void saveOrLoad (Serializer *s);
- void send (MidiChannel *mc);
- void copy_to (Instrument *dest) { dest->adlib ((byte *) &_instrument); }
- bool is_valid() { return true; }
-};
-
-class Instrument_Roland : public InstrumentInternal {
-private:
- struct RolandInstrument {
- byte roland_id;
- byte device_id;
- byte model_id;
- byte command;
- byte address[3];
- struct {
- byte name[10];
- byte partial_struct12;
- byte partial_struct34;
- byte partial_mute;
- byte env_mode;
- } common;
- struct {
- byte wg_pitch_coarse;
- byte wg_pitch_fine;
- byte wg_pitch_keyfollow;
- byte wg_pitch_bender_sw;
- byte wg_waveform_pcm_bank;
- byte wg_pcm_wave_num;
- byte wg_pulse_width;
- byte wg_pw_velo_sens;
- byte p_env_depth;
- byte p_evn_velo_sens;
- byte p_env_time_keyf;
- byte p_env_time[4];
- byte p_env_level[3];
- byte p_env_sustain_level;
- byte end_level;
- byte p_lfo_rate;
- byte p_lfo_depth;
- byte p_lfo_mod_sens;
- byte tvf_cutoff_freq;
- byte tvf_resonance;
- byte tvf_keyfollow;
- byte tvf_bias_point_dir;
- byte tvf_bias_level;
- byte tvf_env_depth;
- byte tvf_env_velo_sens;
- byte tvf_env_depth_keyf;
- byte tvf_env_time_keyf;
- byte tvf_env_time[5];
- byte tvf_env_level[3];
- byte tvf_env_sustain_level;
- byte tva_level;
- byte tva_velo_sens;
- byte tva_bias_point_1;
- byte tva_bias_level_1;
- byte tva_bias_point_2;
- byte tva_bias_level_2;
- byte tva_env_time_keyf;
- byte tva_env_time_v_follow;
- byte tva_env_time[5];
- byte tva_env_level[3];
- byte tva_env_sustain_level;
- } partial[4];
- byte checksum;
- } GNUPACK;
- RolandInstrument _instrument;
-
- char _instrument_name [11];
-
- uint8 getEquivalentGM();
-
-public:
- Instrument_Roland (byte *data);
- Instrument_Roland (Serializer *s);
- void saveOrLoad (Serializer *s);
- void send (MidiChannel *mc);
- void copy_to (Instrument *dest) { dest->roland ((byte *) &_instrument); }
- bool is_valid() { return (_native_mt32 ? true : (_instrument_name[0] != '\0')); }
-};
-
-////////////////////////////////////////
-//
-// Instrument class members
-//
-////////////////////////////////////////
-
-void Instrument::nativeMT32 (bool native) {
- _native_mt32 = native;
-}
-
-void Instrument::clear() {
- if (_instrument)
- delete _instrument;
- _instrument = NULL;
- _type = itNone;
-}
-
-void Instrument::program (byte prog, bool mt32) {
- clear();
- if (prog > 127)
- return;
- _type = itProgram;
- _instrument = new Instrument_Program (prog, mt32);
-}
-
-void Instrument::adlib (byte *instrument) {
- clear();
- if (!instrument)
- return;
- _type = itAdlib;
- _instrument = new Instrument_Adlib (instrument);
-}
-
-void Instrument::roland (byte *instrument) {
- clear();
- if (!instrument)
- return;
- _type = itRoland;
- _instrument = new Instrument_Roland (instrument);
-}
-
-void Instrument::saveOrLoad (Serializer *s) {
- if (s->isSaving()) {
- s->saveByte (_type);
- if (_instrument)
- _instrument->saveOrLoad (s);
- } else {
- clear();
- _type = s->loadByte();
- switch (_type) {
- case itNone:
- break;
- case itProgram:
- _instrument = new Instrument_Program (s);
- break;
- case itAdlib:
- _instrument = new Instrument_Adlib (s);
- break;
- case itRoland:
- _instrument = new Instrument_Roland (s);
- break;
- default:
- warning ("No known instrument classification #%d", (int) _type);
- _type = itNone;
- }
- }
-}
-
-////////////////////////////////////////
-//
-// Instrument_Program class members
-//
-////////////////////////////////////////
-
-Instrument_Program::Instrument_Program (byte program, bool mt32) :
-_program (program),
-_mt32 (mt32) {
- if (program > 127)
- _program = 255;
-}
-
-Instrument_Program::Instrument_Program (Serializer *s) {
- _program = 255;
- if (!s->isSaving())
- saveOrLoad (s);
-}
-
-void Instrument_Program::saveOrLoad (Serializer *s) {
- if (s->isSaving()) {
- s->saveByte (_program);
- s->saveByte (_mt32 ? 1 : 0);
- } else {
- _program = s->loadByte();
- _mt32 = (s->loadByte() > 0);
- }
-}
-
-void Instrument_Program::send (MidiChannel *mc) {
- if (_program > 127)
- return;
-
- byte program = _program;
- if (_native_mt32 != _mt32)
- program = _native_mt32 ? MidiDriver::_gmToMt32 [program] : MidiDriver::_mt32ToGm [program];
- if (program < 128)
- mc->programChange (program);
-}
-
-////////////////////////////////////////
-//
-// Instrument_Adlib class members
-//
-////////////////////////////////////////
-
-Instrument_Adlib::Instrument_Adlib (byte *data) {
- memcpy (&_instrument, data, sizeof (_instrument));
-}
-
-Instrument_Adlib::Instrument_Adlib (Serializer *s) {
- if (!s->isSaving())
- saveOrLoad (s);
- else
- memset (&_instrument, 0, sizeof (_instrument));
-}
-
-void Instrument_Adlib::saveOrLoad (Serializer *s) {
- if (s->isSaving())
- s->saveBytes (&_instrument, sizeof (_instrument));
- else
- s->loadBytes (&_instrument, sizeof (_instrument));
-}
-
-void Instrument_Adlib::send (MidiChannel *mc) {
- mc->sysEx_customInstrument ('ADL ', (byte *) &_instrument);
-}
-
-////////////////////////////////////////
-//
-// Instrument_Roland class members
-//
-////////////////////////////////////////
-
-Instrument_Roland::Instrument_Roland (byte *data) {
- memcpy (&_instrument, data, sizeof (_instrument));
- memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
- _instrument_name[10] = '\0';
- if (!_native_mt32 && getEquivalentGM() >= 128) {
- debug (0, "MT-32 instrument \"%s\" not supported yet", _instrument_name);
- _instrument_name[0] = '\0';
- }
-}
-
-Instrument_Roland::Instrument_Roland (Serializer *s) {
- _instrument_name[0] = '\0';
- if (!s->isSaving())
- saveOrLoad (s);
- else
- memset (&_instrument, 0, sizeof (_instrument));
-}
-
-void Instrument_Roland::saveOrLoad (Serializer *s) {
- if (s->isSaving()) {
- s->saveBytes (&_instrument, sizeof (_instrument));
- } else {
- s->loadBytes (&_instrument, sizeof (_instrument));
- memcpy (&_instrument_name, &_instrument.common.name, sizeof (_instrument.common.name));
- _instrument_name[10] = '\0';
- if (!_native_mt32 && getEquivalentGM() >= 128) {
- debug (2, "MT-32 custom instrument \"%s\" not supported", _instrument_name);
- _instrument_name[0] = '\0';
- }
- } // end if
-}
-
-void Instrument_Roland::send (MidiChannel *mc) {
- if (_native_mt32) {
- if (mc->getNumber() > 8)
- return;
- _instrument.device_id = mc->getNumber();
-
- // Remap instrument to appropriate address space.
- int address = 0x008000;
- _instrument.address[0] = (address >> 14) & 0x7F;
- _instrument.address[1] = (address >> 7) & 0x7F;
- _instrument.address[2] = (address ) & 0x7F;
-
- // Recompute the checksum.
- byte checksum = 0;
- byte *ptr = (byte *) &_instrument + 4;
- int i;
- for (i = 4; i < (int)sizeof (_instrument) - 1; ++i)
- checksum -= *ptr++;
- _instrument.checksum = checksum & 0x7F;
-
- mc->device()->sysEx ((byte *) &_instrument, sizeof (_instrument));
- } else {
- // Convert to a GM program change.
- byte program = getEquivalentGM();
- if (program < 128)
- mc->programChange (program);
- }
-}
-
-uint8 Instrument_Roland::getEquivalentGM() {
- byte i;
- for (i = 0; i != ARRAYSIZE(roland_to_gm_map); ++i) {
- if (!memcmp (roland_to_gm_map[i].name, _instrument.common.name, 10))
- return roland_to_gm_map[i].program;
- }
- return 255;
-}
-
-} // End of namespace Scumm
Deleted: scummvm/trunk/engines/scumm/instrument.h
===================================================================
--- scummvm/trunk/engines/scumm/instrument.h 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/instrument.h 2006-02-20 20:57:26 UTC (rev 20801)
@@ -1,79 +0,0 @@
-/* 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 INSTRUMENT_H
-#define INSTRUMENT_H
-
-#include "common/stdafx.h"
-#include "common/scummsys.h"
-
-class MidiChannel;
-
-namespace Scumm {
-
-class Serializer;
-class Instrument;
-
-class InstrumentInternal {
-public:
- virtual ~InstrumentInternal() {}
- virtual void saveOrLoad (Serializer *s) = 0;
- virtual void send (MidiChannel *mc) = 0;
- virtual void copy_to (Instrument *dest) = 0;
- virtual bool is_valid() = 0;
- virtual operator int() { return 255; }
-};
-
-class Instrument {
-private:
- byte _type;
- InstrumentInternal *_instrument;
-
-public:
- enum {
- itNone = 0,
- itProgram = 1,
- itAdlib = 2,
- itRoland = 3
- };
-
- Instrument() : _type (0), _instrument (0) { }
- ~Instrument() { delete _instrument; }
- static void nativeMT32 (bool native);
- static const byte _gmRhythmMap[35];
-
- void clear();
- void copy_to (Instrument *dest) { if (_instrument) _instrument->copy_to (dest); else dest->clear(); }
-
- void program (byte program, bool mt32);
- void adlib (byte *instrument);
- void roland (byte *instrument);
-
- byte getType() { return _type; }
- bool isValid() { return (_instrument ? _instrument->is_valid() : false); }
- void saveOrLoad (Serializer *s);
- void send (MidiChannel *mc) { if (_instrument) _instrument->send (mc); }
-};
-
-} // End of namespace Scumm
-
-#endif
Modified: scummvm/trunk/engines/scumm/module.mk
===================================================================
--- scummvm/trunk/engines/scumm/module.mk 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/module.mk 2006-02-20 20:57:26 UTC (rev 20801)
@@ -13,42 +13,42 @@
debugger.o \
dialogs.o \
gfx.o \
- imuse.o \
- imuse_player.o \
+ he/script_v60he.o \
+ he/sound_he.o \
+ help.o \
+ imuse/imuse_player.o \
+ imuse/imuse.o \
+ imuse/instrument.o \
input.o \
- instrument.o \
- help.o \
+ midiparser_eup.o \
midiparser_ro.o \
- midiparser_eup.o \
object.o \
palette.o \
player_mod.o \
+ player_nes.o \
player_v1.o \
- player_nes.o \
player_v2.o \
player_v2a.o \
player_v3a.o \
- resource.o \
resource_v2.o \
resource_v3.o \
resource_v4.o \
+ resource.o \
room.o \
saveload.o \
- script.o \
script_c64.o \
script_v2.o \
script_v5.o \
script_v6.o \
- he/script_v60he.o \
+ script.o \
scumm.o \
sound.o \
- he/sound_he.o \
string.o \
+ thumbnail.o \
usage_bits.o \
util.o \
vars.o \
- verbs.o \
- thumbnail.o
+ verbs.o
ifndef DISABLE_SCUMM_7_8
MODULE_OBJS += \
@@ -95,6 +95,8 @@
MODULE_DIRS += \
engines/scumm \
+ engines/scumm/he \
+ engines/scumm/imuse \
engines/scumm/imuse_digi \
engines/scumm/insane \
engines/scumm/smush
Modified: scummvm/trunk/engines/scumm/resource.cpp
===================================================================
--- scummvm/trunk/engines/scumm/resource.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/resource.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -26,7 +26,7 @@
#include "scumm/charset.h"
#include "scumm/dialogs.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/intern.h"
#ifndef DISABLE_HE
Modified: scummvm/trunk/engines/scumm/saveload.cpp
===================================================================
--- scummvm/trunk/engines/scumm/saveload.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/saveload.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -30,7 +30,7 @@
#include "scumm/actor.h"
#include "scumm/charset.h"
#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/intern.h"
#include "scumm/he/intern_he.h"
#include "scumm/object.h"
Modified: scummvm/trunk/engines/scumm/script_v6.cpp
===================================================================
--- scummvm/trunk/engines/scumm/script_v6.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/script_v6.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -27,7 +27,7 @@
#include "scumm/actor.h"
#include "scumm/charset.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/insane/insane.h"
#include "scumm/intern.h"
Modified: scummvm/trunk/engines/scumm/scumm.cpp
===================================================================
--- scummvm/trunk/engines/scumm/scumm.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/scumm.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -40,7 +40,7 @@
#include "scumm/costume.h"
#include "scumm/debugger.h"
#include "scumm/dialogs.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/insane/insane.h"
#include "scumm/intern.h"
Modified: scummvm/trunk/engines/scumm/smush/smush_mixer.cpp
===================================================================
--- scummvm/trunk/engines/scumm/smush/smush_mixer.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/smush/smush_mixer.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -27,7 +27,7 @@
#include "scumm/smush/channel.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "sound/mixer.h"
Modified: scummvm/trunk/engines/scumm/smush/smush_player.cpp
===================================================================
--- scummvm/trunk/engines/scumm/smush/smush_player.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/smush/smush_player.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -32,7 +32,7 @@
#include "scumm/bomp.h"
#include "scumm/imuse_digi/dimuse.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
#include "scumm/smush/channel.h"
Modified: scummvm/trunk/engines/scumm/sound.cpp
===================================================================
--- scummvm/trunk/engines/scumm/sound.cpp 2006-02-20 20:42:03 UTC (rev 20800)
+++ scummvm/trunk/engines/scumm/sound.cpp 2006-02-20 20:57:26 UTC (rev 20801)
@@ -23,7 +23,7 @@
#include "common/stdafx.h"
#include "scumm/actor.h"
-#include "scumm/imuse.h"
+#include "scumm/imuse/imuse.h"
#include "scumm/imuse_digi/dimuse.h"
#include "scumm/scumm.h"
#include "scumm/sound.h"
More information about the Scummvm-git-logs
mailing list