aboutsummaryrefslogtreecommitdiff
path: root/engines/scumm/imuse/imuse.cpp
diff options
context:
space:
mode:
authorJamieson Christian2006-03-05 21:30:44 +0000
committerJamieson Christian2006-03-05 21:30:44 +0000
commit5e25b3b679d61a92a39f2d9ee699fad50b0bcbb6 (patch)
treeba6b9b417a8a3f7e18e8f387db53a6f42ec3b3c5 /engines/scumm/imuse/imuse.cpp
parent27e49b6f5674df4ba1ae0d1d1d8eba39c9fa1b76 (diff)
downloadscummvm-rg350-5e25b3b679d61a92a39f2d9ee699fad50b0bcbb6.tar.gz
scummvm-rg350-5e25b3b679d61a92a39f2d9ee699fad50b0bcbb6.tar.bz2
scummvm-rg350-5e25b3b679d61a92a39f2d9ee699fad50b0bcbb6.zip
Restructured IMuse and IMuseInternal.
* IMuse is no longer a concurrency front-end. * IMuseInternal now derives from IMuse. * Common::StackLock used to protect thread-sensitive interface methods (same as IMuseDigital). * clear_queue() included in stopAllSounds() so it can be removed from the public interface. * Game ID now specified at init using property(). * Timer callbacks receive a struct containing IMuseInternal and MidiDriver refs, instead of just the latter. * OSystem pointer from init is now cached and used instead of global. All references to the g_system and g_scumm globals are now gone. BOOYAH! Tested with MI2, DOTT and S&M, under Windows, in Native MIDI, Adlib, and Mixed modes. No regressions or concurrency issues observed. Manifestations of the latter are the biggest concern at this point. svn-id: r21104
Diffstat (limited to 'engines/scumm/imuse/imuse.cpp')
-rw-r--r--engines/scumm/imuse/imuse.cpp1232
1 files changed, 624 insertions, 608 deletions
diff --git a/engines/scumm/imuse/imuse.cpp b/engines/scumm/imuse/imuse.cpp
index bed0a7a45d..89871cc7c0 100644
--- a/engines/scumm/imuse/imuse.cpp
+++ b/engines/scumm/imuse/imuse.cpp
@@ -215,84 +215,6 @@ MidiDriver *IMuseInternal::getBestMidiDriver(int sound) {
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,true))
- 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;
@@ -335,28 +257,14 @@ void IMuseInternal::init_parts() {
}
}
-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;
-}
+////////////////////////////////////////
+//
+// IMuse mixin interface methods
+//
+////////////////////////////////////////
void IMuseInternal::on_timer(MidiDriver *midi) {
+ Common::StackLock lock(_mutex, "IMuseInternal::on_timer()");
if (_paused || !_initialized)
return;
@@ -365,259 +273,200 @@ void IMuseInternal::on_timer(MidiDriver *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)
+void IMuseInternal::pause(bool paused) {
+ Common::StackLock lock(_mutex, "IMuseInternal::pause()");
+ if (_paused == paused)
return;
+ int vol = _music_volume;
+ if (paused)
+ _music_volume = 0;
+ update_volumes();
+ _music_volume = vol;
- // 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);
+ // 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);
}
- 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;
+ _paused = paused;
+}
- doCommand(p[0], p[1], p[2], p[3], p[4], p[5], p[6], 0);
+int IMuseInternal::save_or_load(Serializer *ser, ScummEngine *scumm) {
+ Common::StackLock lock(_mutex, "IMuseInternal::save_or_load()");
+ 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()
+ };
- if (_queue_cleared)
- return;
- pos = _queue_end;
- } while (1);
+ const SaveLoadEntry cmdQueueEntries[] = {
+ MKARRAY(CommandQueue, array[0], sleUint16, 8, VER(23)),
+ MKEND()
+ };
- _queue_end = pos;
-}
+ // 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()
+ };
-int IMuseInternal::get_channel_volume(uint a) {
- if (a < 8)
- return _channel_volume_eff[a];
- return (_master_volume * _music_volume / 255) / 2;
-}
+ 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()
+ };
-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;
- }
- }
+ 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);
- if (best) {
- best->uninit();
- reallocateMidiChannels(midi);
- } else {
- debug(1, "Denying part request");
- }
- return best;
-}
+ // The players
+ for (i = 0; i < ARRAYSIZE(_players); ++i)
+ _players[i].saveLoadWithSerializer(ser);
-int IMuseInternal::getSoundStatus(int sound, bool ignoreFadeouts) const {
- int i;
- const Player *player = _players;
+ // The parts
+ for (i = 0; i < ARRAYSIZE(_parts); ++i)
+ _parts[i].saveLoadWithSerializer(ser);
- 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;
+ { // 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();
}
}
- 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;
+ // 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);
- 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);
- }
+ if (ser->isLoading()) {
+ // Load all sounds that we need
+ fix_players_after_load(scumm);
+ fix_parts_after_load();
+ setImuseMasterVolume(_master_volume);
- for (i = 0; i < ARRAYSIZE (_deferredCommands); ++i) {
- if (_deferredCommands[i].time_left && _deferredCommands[i].a == 8 &&
- _deferredCommands[i].b == sound) {
- return 2;
- }
+ if (_midi_native)
+ reallocateMidiChannels(_midi_native);
+ if (_midi_adlib)
+ reallocateMidiChannels(_midi_adlib);
}
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;
+bool IMuseInternal::get_sound_active(int sound) const {
+ Common::StackLock lock(_mutex, "IMuseInternal::get_sound_active()");
+ return getSoundStatus_internal (sound, false);
+}
- 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;
- }
+int32 IMuseInternal::doCommand(int numargs, int a[]) {
+ Common::StackLock lock(_mutex, "IMuseInternal::doCommand()");
+ return doCommand_internal (numargs, a);
}
-int IMuseInternal::clear_queue() {
- _queue_adding = false;
- _queue_cleared = true;
- _queue_pos = 0;
- _queue_end = 0;
- _trigger_count = 0;
- return 0;
+void IMuseInternal::setBase(byte **base) {
+ Common::StackLock lock(_mutex, "IMuseInternal::setBase()");
+ _base_sounds = base;
}
-int IMuseInternal::enqueue_command(int a, int b, int c, int d, int e, int f, int g) {
- uint16 *p;
- uint i;
+uint32 IMuseInternal::property(int prop, uint32 value) {
+ Common::StackLock lock(_mutex, "IMuseInternal::property()");
+ 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;
- i = _queue_pos;
+ case IMuse::PROP_NATIVE_MT32:
+ _native_mt32 = (value > 0);
+ Instrument::nativeMT32(_native_mt32);
+ if (_midi_native && _native_mt32)
+ initMT32(_midi_native);
+ break;
- if (i == _queue_end)
- return -1;
+ case IMuse::PROP_GS:
+ _enable_gs = (value > 0);
- if (a == -1) {
- _queue_adding = false;
- _trigger_count++;
- return 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;
- 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;
+ case IMuse::PROP_LIMIT_PLAYERS:
+ if (value > 0 && value <= ARRAYSIZE(_players))
+ _player_limit = (int)value;
+ break;
- i = (i + 1) % ARRAYSIZE(_cmd_queue);
+ case IMuse::PROP_RECYCLE_PLAYERS:
+ _recycle_players = (value != 0);
+ break;
- if (_queue_end != i) {
- _queue_pos = i;
- return 0;
- } else {
- _queue_pos = (i - 1) % ARRAYSIZE(_cmd_queue);
- return -1;
- }
-}
+ case IMuse::PROP_DIRECT_PASSTHROUGH:
+ _direct_passthrough = (value != 0);
+ break;
-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;
+ case IMuse::PROP_GAME_ID:
+ _game_id = value;
+ break;
}
+
+ return 0;
}
-int IMuseInternal::setMusicVolume(uint vol) {
+////////////////////////////////////////
+//
+// MusicEngine interface methods
+//
+////////////////////////////////////////
+
+void IMuseInternal::setMusicVolume (int vol) {
+ Common::StackLock lock(_mutex, "IMuseInternal::setMusicVolume()");
if (vol > 255)
vol = 255;
if (_music_volume == vol)
- return 0;
+ return;
_music_volume = vol;
vol = _master_volume * _music_volume / 255;
for (uint i = 0; i < ARRAYSIZE(_channel_volume); i++) {
@@ -625,34 +474,53 @@ int IMuseInternal::setMusicVolume(uint vol) {
}
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;
+void IMuseInternal::startSound(int sound) {
+ Common::StackLock lock(_mutex, "IMuseInternal::startSound()");
+ startSound_internal (sound);
}
-int IMuseInternal::terminate1() {
- _initialized = false;
- stopAllSounds();
- return 0;
+void IMuseInternal::stopSound(int sound) {
+ Common::StackLock lock(_mutex, "IMuseInternal::stopSound()");
+ stopSound_internal (sound);
+}
+
+void IMuseInternal::stopAllSounds() {
+ Common::StackLock lock(_mutex, "IMuseInternal::stopAllSounds()");
+ stopAllSounds_internal();
+}
+
+int IMuseInternal::getSoundStatus (int sound) const {
+ Common::StackLock lock(_mutex, "IMuseInternal::getSoundStatus()");
+ return getSoundStatus_internal (sound, true);
+}
+
+int IMuseInternal::getMusicTimer() const {
+ Common::StackLock lock(_mutex, "IMuseInternal::getMusicTimer()");
+ int best_time = 0;
+ const Player *player = _players;
+ for (int i = ARRAYSIZE(_players); i; i--, player++) {
+ if (player->isActive()) {
+ int timer = player->getMusicTimer();
+ if (timer > best_time)
+ best_time = timer;
+ }
+ }
+ return best_time;
}
-// This is the stuff that has to be done
-// outside the monitor's mutex, otherwise
-// a deadlock occurs.
-int IMuseInternal::terminate2() {
+void IMuseInternal::terminate() {
+ // Do just enough stuff inside the mutex to
+ // make sure any MIDI timing threads won't
+ // interrupt us, and then do the rest outside
+ // the mutex.
+ {
+ Common::StackLock lock(_mutex, "IMuseInternal::terminate()");
+ _initialized = false;
+ stopAllSounds_internal();
+ }
+
if (_midi_adlib) {
_midi_adlib->close();
delete _midi_adlib;
@@ -663,42 +531,135 @@ int IMuseInternal::terminate2() {
if (_native_mt32) {
// Reset the MT-32
_midi_native->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
- g_system->delayMillis(250);
+ _system->delayMillis(250);
}
_midi_native->close();
delete _midi_native;
_midi_native = 0;
}
-
- return 0;
}
-int IMuseInternal::enqueue_trigger(int sound, int marker) {
- uint16 *p;
- uint pos;
+////////////////////////////////////////
+//
+// Internal versions of the IMuse and
+// MusicEngine base class methods.
+// These methods assume the appropriate
+// mutex locks have already been set,
+// and may also have slightly different
+// semantics than the interface methods.
+//
+////////////////////////////////////////
- pos = _queue_pos;
+bool IMuseInternal::startSound_internal (int sound) {
+ // 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.
- p = _cmd_queue[pos].array;
- p[0] = TRIGGER_ID;
- p[1] = sound;
- p[2] = marker;
+ 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_internal (trigger->sound,true))
+ return false;
+ }
- pos = (pos + 1) % ARRAYSIZE(_cmd_queue);
- if (_queue_end == pos) {
- _queue_pos = (pos - 1) % ARRAYSIZE(_cmd_queue);
- return -1;
+ void *ptr = findStartOfSound(sound);
+ if (!ptr) {
+ debug(2, "IMuseInternal::startSound(): Couldn't find sound %d!", sound);
+ return false;
}
- _queue_pos = pos;
- _queue_adding = true;
- _queue_sound = sound;
- _queue_marker = marker;
+ // 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 *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 (_game_id == GID_SAMNMAX && sound == 82 && getSoundStatus_internal (81, false))
+ ImClearTrigger(81, 1);
+
+ player->clear();
+ return player->startSound(sound, driver, _direct_passthrough);
+}
+
+int IMuseInternal::stopSound_internal (int sound) {
+ int r = -1;
+ Player *player = findActivePlayer(sound);
+ if (player) {
+ player->clear();
+ r = 0;
+ }
+ return r;
+}
+
+int IMuseInternal::stopAllSounds_internal() {
+ Player *player = _players;
+ for (int i = ARRAYSIZE(_players); i; i--, player++) {
+ if (player->isActive())
+ player->clear();
+ }
return 0;
}
-int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g, int h) {
+int IMuseInternal::getSoundStatus_internal (int sound, bool ignoreFadeouts) const {
+ const Player *player = _players;
+ for (int i = ARRAYSIZE(_players); i; 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);
+}
+
+int32 IMuseInternal::doCommand_internal
+ (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;
@@ -708,14 +669,14 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
args[5] = f;
args[6] = g;
args[7] = h;
- return doCommand(8, args);
+ return doCommand_internal (8, args);
}
-int32 IMuseInternal::doCommand(int numargs, int a[]) {
- int i;
-
+int32 IMuseInternal::doCommand_internal (int numargs, int a[]) {
if (numargs < 1)
return -1;
+
+ int i;
byte cmd = a[0] & 0xFF;
byte param = a[0] >> 8;
Player *player = NULL;
@@ -744,17 +705,17 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
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;
+ return startSound_internal (a[1]) ? 0 : -1;
case 9:
- return stopSound(a[1]);
+ return stopSound_internal (a[1]);
case 10: // FIXME: Sam and Max - Not sure if this is correct
- return stopAllSounds();
+ return stopAllSounds_internal();
case 11:
- return stopAllSounds();
+ return stopAllSounds_internal();
case 12:
// Sam & Max: Player-scope commands
player = findActivePlayer(a[1]);
- if (!player)
+ if (player == NULL)
return -1;
switch (a[3]) {
@@ -766,7 +727,7 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
}
return -1;
case 13:
- return getSoundStatus(a[1], true);
+ return getSoundStatus_internal (a[1], true);
case 14:
// Sam and Max: Parameter fade
player = findActivePlayer(a[1]);
@@ -786,7 +747,7 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
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) {
+ if (_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 {
@@ -801,7 +762,7 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
}
}
case 18:
- if (g_scumm->_game.id != GID_SAMNMAX) {
+ if (_game_id != GID_SAMNMAX) {
return set_volchan_entry(a[1], a[2]);
} else {
// Sam & Max: ImCheckTrigger.
@@ -849,7 +810,7 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
switch (cmd) {
case 0:
- if (g_scumm->_game.id == GID_SAMNMAX) {
+ if (_game_id == GID_SAMNMAX) {
if (a[3] == 1) // Measure number
return ((player->getBeatIndex() - 1) >> 2) + 1;
else if (a[3] == 2) // Beat number
@@ -859,7 +820,7 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
return player->getParam(a[2], a[3]);
}
case 1:
- if (g_scumm->_game.id == GID_SAMNMAX) {
+ if (_game_id == GID_SAMNMAX) {
// FIXME: Could someone verify this?
//
// This jump instruction is known to be used in
@@ -946,6 +907,265 @@ int32 IMuseInternal::doCommand(int numargs, int a[]) {
return -1;
}
+// mixin end
+
+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_internal (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::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::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::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::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
@@ -1003,8 +1223,8 @@ int32 IMuseInternal::ImSetTrigger(int sound, int id, int a, int b, int c, int d,
// 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],true) && getSoundStatus(sound,true))
- stopSound(trig->command[1]);
+ if (trig->command[0] == 8 && getSoundStatus_internal (trig->command[1],true) && getSoundStatus_internal (sound,true))
+ stopSound_internal (trig->command[1]);
return 0;
}
@@ -1029,7 +1249,7 @@ int32 IMuseInternal::ImFireAllTriggers(int sound) {
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);
+ doCommand_internal (8, _snm_triggers[i].command);
++count;
}
}
@@ -1141,57 +1361,6 @@ int IMuseInternal::get_volchan_entry(uint 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);
@@ -1201,12 +1370,19 @@ IMuseInternal *IMuseInternal::create(OSystem *syst, MidiDriver *nativeMidiDriver
int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver *adlib_midi) {
int i;
+ _system = syst;
_midi_native = native_midi;
_midi_adlib = adlib_midi;
- if (native_midi != NULL)
- initMidiDriver(native_midi);
- if (adlib_midi != NULL)
- initMidiDriver(adlib_midi);
+ if (native_midi != NULL) {
+ _timer_info_native.imuse = this;
+ _timer_info_native.driver = native_midi;
+ initMidiDriver (&_timer_info_native);
+ }
+ if (adlib_midi != NULL) {
+ _timer_info_adlib.imuse = this;
+ _timer_info_adlib.driver = adlib_midi;
+ initMidiDriver (&_timer_info_adlib);
+ }
if (!_tempoFactor)
_tempoFactor = 100;
@@ -1224,14 +1400,14 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi, MidiDriver
return 0;
}
-void IMuseInternal::initMidiDriver(MidiDriver *midi) {
+void IMuseInternal::initMidiDriver (TimerCallbackInfo *info) {
// Open MIDI driver
- int result = midi->open();
+ int result = info->driver->open();
if (result)
error("IMuse initialization - %s", MidiDriver::getErrorName(result));
// Connect to the driver's timer
- midi->setTimerCallback(midi, &IMuseInternal::midiTimerCallback);
+ info->driver->setTimerCallback (info, &IMuseInternal::midiTimerCallback);
}
void IMuseInternal::initMT32(MidiDriver *midi) {
@@ -1241,16 +1417,16 @@ void IMuseInternal::initMT32(MidiDriver *midi) {
// Reset the MT-32
midi->sysEx((const byte *) "\x41\x10\x16\x12\x7f\x00\x00\x01\x00", 9);
- g_system->delayMillis(250);
+ _system->delayMillis(250);
// Setup master tune, reverb mode, reverb time, reverb level,
// channel mapping, partial reserve and master volume
midi->sysEx((const byte *) "\x41\x10\x16\x12\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", 31);
- g_system->delayMillis(250);
+ _system->delayMillis(250);
// Map percussion to notes 24 - 34 without reverb
midi->sysEx((const byte *) "\x41\x10\x16\x12\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", 52);
- g_system->delayMillis(250);
+ _system->delayMillis(250);
// Compute version string (truncated to 20 chars max.)
strcat(info, gScummVMVersion);
@@ -1267,7 +1443,7 @@ void IMuseInternal::initMT32(MidiDriver *midi) {
checksum -= buffer[i];
buffer[27] = checksum & 0x7F;
midi->sysEx(buffer, 28);
- g_system->delayMillis(1000);
+ _system->delayMillis(1000);
}
void IMuseInternal::initGM(MidiDriver *midi) {
@@ -1279,7 +1455,7 @@ void IMuseInternal::initGM(MidiDriver *midi) {
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);
+ _system->delayMillis(200);
if (_enable_gs) {
@@ -1297,7 +1473,7 @@ void IMuseInternal::initGM(MidiDriver *midi) {
memcpy(&buffer[5], "\x40\x00\x7F\x00\x41\xF7", 6);
midi->sysEx(buffer, 11);
debug(2, "GS SysEx: GS Reset");
- g_system->delayMillis(200);
+ _system->delayMillis(200);
if (_sc55) {
// This mode is for GS devices that support an MT-32-compatible
@@ -1419,29 +1595,6 @@ void IMuseInternal::init_queue() {
_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();
@@ -1451,7 +1604,7 @@ void IMuseInternal::handleDeferredCommands(MidiDriver *midi) {
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);
+ doCommand_internal (ptr->a, ptr->b, ptr->c, ptr->d, ptr->e, ptr->f, 0, 0);
ptr->time_left = advance;
}
ptr->time_left -= advance;
@@ -1480,102 +1633,6 @@ void IMuseInternal::addDeferredCommand(int time, int a, int b, int c, int d, int
}
}
-////////////////////////////////////////////////////////////
-//
-// 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;
@@ -1606,10 +1663,9 @@ void IMuseInternal::fix_players_after_load(ScummEngine *scumm) {
//
////////////////////////////////////////
-void IMuseInternal::midiTimerCallback(void *data) {
- MidiDriver *driver = (MidiDriver *)data;
- if (g_scumm->_imuse)
- g_scumm->_imuse->on_timer(driver);
+void IMuseInternal::midiTimerCallback (void *data) {
+ TimerCallbackInfo *info = (TimerCallbackInfo *) data;
+ info->imuse->on_timer (info->driver);
}
void IMuseInternal::reallocateMidiChannels(MidiDriver *midi) {
@@ -1666,60 +1722,20 @@ void IMuseInternal::copyGlobalAdlibInstrument(byte slot, Instrument *dest) {
_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.
+
+
+/**
+ * IMuseInternal factory creation method.
+ * This method provides a means for creating an IMuse
+ * implementation without requiring that the details
+ * of that implementation be exposed to the client
+ * through a header file. This allows the internals
+ * of the implementation to be changed and updated
+ * without requiring a recompile of the client code.
+ */
IMuse *IMuse::create(OSystem *syst, MidiDriver *nativeMidiDriver, MidiDriver *adlibMidiDriver) {
IMuseInternal *engine = IMuseInternal::create(syst, nativeMidiDriver, adlibMidiDriver);
- return new IMuse(syst, engine);
+ return engine;
}
} // End of namespace Scumm