aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJamieson Christian2003-05-23 04:19:47 +0000
committerJamieson Christian2003-05-23 04:19:47 +0000
commitc6568530bd6d558045c912c6318d1d7d8a60a39d (patch)
treebec2a496ca22b746d01b49afe74194be2bd756d5
parent006d0a5ff81771f8387f13187675601fd52e2668 (diff)
downloadscummvm-rg350-c6568530bd6d558045c912c6318d1d7d8a60a39d.tar.gz
scummvm-rg350-c6568530bd6d558045c912c6318d1d7d8a60a39d.tar.bz2
scummvm-rg350-c6568530bd6d558045c912c6318d1d7d8a60a39d.zip
Revamped iMuse and Player classes. Player now uses MidiParser to parse its data, which will allow it to parse other MIDI formats. To receive parsed data, Player now derives from MidiDriver to act as a "fake MIDI driver".
Miscellaneous upgrades and fixes to MidiParser, including the Smart Jump (which could not be tested before iMuse started making use of the MidiParser). *** THIS IS A BIG UPGRADE! EXTENSIVE REGRESSION TESTING IS NEEDED! *** This has been tested through the intros and a number of other scenes from MI2, FOA and S&M. NOTE! This upgrade introduces savegame format version V19. Earlier version savegames will load, but the music will simply start over from the beginning. Only V19 and later games will properly restore the position of the music! Don't say you weren't warned.... svn-id: r7849
-rw-r--r--backends/midi/adlib.cpp4
-rw-r--r--scumm/imuse.cpp366
-rw-r--r--scumm/imuse_internal.h156
-rw-r--r--scumm/imuse_player.cpp764
-rw-r--r--scumm/saveload.h5
-rw-r--r--scumm/script_v6.cpp2
-rw-r--r--sound/mididrv.h5
-rw-r--r--sound/midiparser.cpp202
-rw-r--r--sound/midiparser.h63
-rw-r--r--sound/midiparser_smf.cpp43
-rw-r--r--sound/midiparser_xmidi.cpp42
-rw-r--r--sound/mpu401.h2
12 files changed, 736 insertions, 918 deletions
diff --git a/backends/midi/adlib.cpp b/backends/midi/adlib.cpp
index dc3898ed45..743930f8f4 100644
--- a/backends/midi/adlib.cpp
+++ b/backends/midi/adlib.cpp
@@ -564,9 +564,9 @@ public:
void setTimerCallback (void *timer_param, void (*timer_proc) (void *));
uint32 getBaseTempo() {
#ifdef _WIN32_WCE
- return 0x1D9000 * 2; // Sampled down to 11 kHz
+ return 3991 * 2; // Sampled down to 11 kHz
#else //_WIN32_WCE
- return 0x1D9000;
+ return 3991; // 88 samples per call (at 22 kHz mono)
#endif //_WIN32_WCE
}
diff --git a/scumm/imuse.cpp b/scumm/imuse.cpp
index 2179ed6318..c67949b134 100644
--- a/scumm/imuse.cpp
+++ b/scumm/imuse.cpp
@@ -40,6 +40,32 @@
//
////////////////////////////////////////
+IMuseInternal::IMuseInternal() :
+_old_adlib_instruments (false),
+_enable_multi_midi (false),
+_midi_adlib (0),
+_midi_native (0),
+_base_sounds (0),
+_paused (false),
+_initialized (false),
+_tempoFactor (0),
+_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));
+ memset (_active_notes,0,sizeof(_active_notes));
+}
+
IMuseInternal::~IMuseInternal() {
terminate();
}
@@ -215,12 +241,8 @@ bool IMuseInternal::startSound(int sound) {
// race conditions that were observed in MI2. Reference
// Bug #590511 and Patch #607175 (which was reversed to fix
// an FOA regression: Bug #622606).
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == sound)
- break;
- }
-
- if (!i)
+ player = findActivePlayer (sound);
+ if (!player)
player = allocate_player(128);
if (!player)
return false;
@@ -236,11 +258,11 @@ Player *IMuseInternal::allocate_player(byte priority) {
byte bestpri = 255;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (!player->_active)
+ if (!player->isActive())
return player;
- if (player->_priority < bestpri) {
+ if (player->getPriority() < bestpri) {
best = player;
- bestpri = player->_priority;
+ bestpri = player->getPriority();
}
}
@@ -256,25 +278,11 @@ void IMuseInternal::init_players() {
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- player->_active = false;
+ player->clear(); // Used to just set _active to false
player->_se = this;
}
}
-void IMuseInternal::init_sustaining_notes() {
- SustainingNotes *next = NULL, *sn = _sustaining_notes;
- int i;
-
- _sustain_notes_used = NULL;
- _sustain_notes_head = NULL;
-
- for (i = ARRAYSIZE(_sustaining_notes); i != 0; i--, sn++) {
- sn->next = next;
- next = sn;
- }
- _sustain_notes_free = next;
-}
-
void IMuseInternal::init_parts() {
Part *part;
int i;
@@ -286,15 +294,11 @@ void IMuseInternal::init_parts() {
}
int IMuseInternal::stopSound(int sound) {
- Player *player = _players;
- int i;
int r = -1;
-
- for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->_active && player->_id == sound) {
- player->clear();
- r = 0;
- }
+ Player *player = findActivePlayer (sound);
+ if (player) {
+ player->clear();
+ r = 0;
}
return r;
}
@@ -304,7 +308,7 @@ int IMuseInternal::stop_all_sounds() {
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->_active)
+ if (player->isActive())
player->clear();
}
return 0;
@@ -317,7 +321,6 @@ void IMuseInternal::on_timer (MidiDriver *midi) {
if (midi == _midi_native || !_midi_native)
handleDeferredCommands (midi);
sequencer_timers (midi);
- expire_sustain_notes (midi);
}
void IMuseInternal::sequencer_timers (MidiDriver *midi) {
@@ -325,8 +328,8 @@ void IMuseInternal::sequencer_timers (MidiDriver *midi) {
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->_active && player->_midi == midi) {
- player->sequencer_timer();
+ if (player->isActive() && player->getMidiDriver() == midi) {
+ player->onTimer();
}
}
}
@@ -407,52 +410,10 @@ Part *IMuseInternal::allocate_part (byte pri, MidiDriver *midi) {
return best;
}
-void IMuseInternal::expire_sustain_notes (MidiDriver *midi) {
- SustainingNotes *sn, *next;
- Player *player;
- uint32 counter;
-
- for (sn = _sustain_notes_head; sn; sn = next) {
- next = sn->next;
- player = sn->player;
- if (player->_midi != midi) continue;
-
- counter = sn->counter + player->_timer_speed;
- sn->pos += counter >> 16;
- sn->counter = (unsigned short)counter & 0xFFFF;
-
- if (sn->pos >= sn->off_pos) {
- player->key_off(sn->chan, sn->note);
-
- // Unlink the node
- if (next)
- next->prev = sn->prev;
- if (sn->prev)
- sn->prev->next = next;
- else
- _sustain_notes_head = next;
-
- // And put it in the free list
- sn->next = _sustain_notes_free;
- _sustain_notes_free = sn;
- }
- }
-}
-
int IMuseInternal::getSoundStatus(int sound) {
- int i;
- Player *player;
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)sound) {
- // Assume that anyone asking for the sound status is
- // really asking "is it ok if I start playing this
- // sound now?" So if the sound is about to fade out,
- // pretend it's not playing.
- if (player->is_fading_out())
- continue;
- return 1;
- }
- }
+ Player *player = findActivePlayer (sound);
+ if (player && !player->isFadingOut())
+ return 1;
return get_queue_sound_status(sound);
}
@@ -461,12 +422,9 @@ int IMuseInternal::getSoundStatus(int sound) {
// other sounds. This is the method to use when determining
// what resources to expire from memory.
bool IMuseInternal::get_sound_active(int sound) {
- int i;
- Player *player;
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)sound)
- return 1;
- }
+ Player *player = findActivePlayer (sound);
+ if (player)
+ return 1;
return (get_queue_sound_status(sound) != 0);
}
@@ -497,12 +455,11 @@ int IMuseInternal::set_volchan(int sound, int volchan) {
return -1;
if (r >= 8) {
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)sound && player->_vol_chan != (uint16)volchan) {
- player->_vol_chan = volchan;
- player->set_vol(player->_volume);
- return 0;
- }
+ player = findActivePlayer (sound);
+ if (player && player->_vol_chan != (uint16)volchan) {
+ player->_vol_chan = volchan;
+ player->setVolume (player->getVolume());
+ return 0;
}
return -1;
} else {
@@ -510,12 +467,12 @@ int IMuseInternal::set_volchan(int sound, int volchan) {
num = 0;
sameid = NULL;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active) {
+ if (player->isActive()) {
if (player->_vol_chan == (uint16)volchan) {
num++;
- if (!best || player->_priority <= best->_priority)
+ if (!best || player->getPriority() <= best->getPriority())
best = player;
- } else if (player->_id == (uint16)sound) {
+ } else if (player->getID() == (uint16)sound) {
sameid = player;
}
}
@@ -525,7 +482,7 @@ int IMuseInternal::set_volchan(int sound, int volchan) {
if (num >= r)
best->clear();
player->_vol_chan = volchan;
- player->set_vol(player->_volume);
+ player->setVolume (player->getVolume());
return 0;
}
}
@@ -702,17 +659,14 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
return stop_all_sounds();
case 12:
// Sam & Max: Player-scope commands
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)b)
- break;
- }
- if (!i)
+ player = findActivePlayer (b);
+ if (!player)
return -1;
switch (d) {
case 6:
// Set player volume.
- return player->set_vol (e);
+ return player->setVolume (e);
default:
warning("IMuseInternal::doCommand (6) unsupported sub-command %d", d);
}
@@ -720,19 +674,18 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
case 13:
return getSoundStatus(b);
case 14:
- // Sam and Max: Parameter transition
- player = this->get_player_byid (b);
+ // Sam and Max: Parameter fade
+ player = this->findActivePlayer (b);
if (player)
return player->addParameterFader (d, e, f);
return -1;
case 15:
// Sam & Max: Set hook for a "maybe" jump
- for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)b) {
- player->_hook.set (0, d, 0);
- return 0;
- }
+ player = findActivePlayer (b);
+ if (player) {
+ player->setHook (0, d, 0);
+ return 0;
}
return -1;
case 16:
@@ -783,12 +736,12 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
}
} else if (param == 1) {
if ((1 << cmd) & (0x783FFF)) {
- player = get_player_byid(b);
+ player = findActivePlayer(b);
if (!player)
return -1;
if ((1 << cmd) & (1 << 11 | 1 << 22)) {
assert(c >= 0 && c <= 15);
- player = (Player *)player->get_part(c);
+ player = (Player *) player->getPart(c);
if (!player)
return -1;
}
@@ -798,46 +751,46 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
case 0:
if (g_scumm->_gameId == GID_SAMNMAX) {
if (d == 1) // Measure number
- return ((player->_beat_index - 1) >> 2) + 1;
+ return ((player->getBeatIndex() - 1) >> 2) + 1;
else if (d == 2) // Beat number
- return player->_beat_index;
+ return player->getBeatIndex();
return -1;
} else {
- return player->get_param(c, d);
+ return player->getParam(c, d);
}
case 1:
if (g_scumm->_gameId == GID_SAMNMAX)
- player->jump (d - 1, (e - 1) * 4 + f, ((g * player->_ticks_per_beat) >> 2) + h);
+ player->jump (d - 1, (e - 1) * 4 + f, ((g * player->getTicksPerBeat()) >> 2) + h);
else
- player->set_priority(c);
+ player->setPriority(c);
return 0;
case 2:
- return player->set_vol(c);
+ return player->setVolume(c);
case 3:
- player->set_pan(c);
+ player->setPan(c);
return 0;
case 4:
- return player->set_transpose(c, d);
+ return player->setTranspose(c, d);
case 5:
- player->set_detune(c);
+ player->setDetune(c);
return 0;
case 6:
- player->set_speed(c);
+ player->setSpeed(c);
return 0;
case 7:
return player->jump(c, d, e) ? 0 : -1;
case 8:
return player->scan(c, d, e);
case 9:
- return player->set_loop(c, d, e, f, g) ? 0 : -1;
+ return player->setLoop(c, d, e, f, g) ? 0 : -1;
case 10:
- player->clear_loop();
+ player->clearLoop();
return 0;
case 11:
((Part *)player)->set_onoff(d != 0);
return 0;
case 12:
- return player->_hook.set(c, d, e);
+ return player->setHook(c, d, e);
case 13:
return player->addParameterFader (ParameterFader::pfVolume, c, d);
case 14:
@@ -847,13 +800,13 @@ int32 IMuseInternal::doCommand(int a, int b, int c, int d, int e, int f, int g,
case 16:
return clear_queue();
case 19:
- return player->get_param(c, d);
+ return player->getParam(c, d);
case 20:
- return player->_hook.set(c, d, e);
+ return player->setHook(c, d, e);
case 21:
return -1;
case 22:
- ((Part *)player)->set_vol(d);
+ ((Part *)player)->setVolume(d);
return 0;
case 23:
return query_queue(b);
@@ -969,8 +922,8 @@ void IMuseInternal::update_volumes() {
int i;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active)
- player->set_vol(player->_volume);
+ if (player->isActive())
+ player->setVolume (player->getVolume());
}
}
@@ -1041,18 +994,15 @@ int HookDatas::set(byte cls, byte value, byte chan) {
return 0;
}
-Player *IMuseInternal::get_player_byid(int id) {
+Player *IMuseInternal::findActivePlayer (int id) {
int i;
- Player *player, *found = NULL;
+ Player *player;
for (i = ARRAYSIZE(_players), player = _players; i != 0; i--, player++) {
- if (player->_active && player->_id == (uint16)id) {
- if (found)
- return NULL;
- found = player;
- }
+ if (player->isActive() && player->getID() == (uint16)id)
+ return player;
}
- return found;
+ return NULL;
}
int IMuseInternal::get_volchan_entry(uint a) {
@@ -1082,7 +1032,7 @@ uint32 IMuseInternal::property(int prop, uint32 value) {
_midi_adlib = NULL;
int i;
for (i = 0; i < ARRAYSIZE(_players); ++i) {
- if (_players[i]._active && _players[i]._midi == driver)
+ if (_players[i].isActive() && _players[i].getMidiDriver() == driver)
_players[i].clear();
}
driver->close();
@@ -1122,7 +1072,6 @@ int IMuseInternal::initialize(OSystem *syst, MidiDriver *native_midi) {
_channel_volume[i] = _channel_volume_eff[i] = _volchan_table[i] = 127;
init_players();
- init_sustaining_notes();
init_queue();
init_parts();
@@ -1161,7 +1110,7 @@ void IMuseInternal::pause(bool paused) {
}
void IMuseInternal::handleDeferredCommands (MidiDriver *midi) {
- uint32 advance = midi->getBaseTempo() / 500;
+ uint32 advance = midi->getBaseTempo();
DeferredCommand *ptr = &_deferredCommands[0];
int i;
@@ -1176,8 +1125,8 @@ void IMuseInternal::handleDeferredCommands (MidiDriver *midi) {
}
}
-// "time" is referenced as hundredths of a second.
-// IS THAT CORRECT??
+// "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];
@@ -1250,42 +1199,6 @@ int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm) {
MKEND()
};
- const SaveLoadEntry playerEntries[] = {
- MKREF(Player, _parts, TYPE_PART, VER_V8),
- MKLINE(Player, _active, sleByte, VER_V8),
- MKLINE(Player, _id, sleUint16, VER_V8),
- MKLINE(Player, _priority, sleByte, VER_V8),
- MKLINE(Player, _volume, sleByte, VER_V8),
- MKLINE(Player, _pan, sleInt8, VER_V8),
- MKLINE(Player, _transpose, sleByte, VER_V8),
- MKLINE(Player, _detune, sleInt8, VER_V8),
- MKLINE(Player, _vol_chan, sleUint16, VER_V8),
- MKLINE(Player, _vol_eff, sleByte, VER_V8),
- MKLINE(Player, _speed, sleByte, VER_V8),
- MKLINE(Player, _song_index, sleUint16, VER_V8),
- MKLINE(Player, _track_index, sleUint16, VER_V8),
- MKLINE(Player, _timer_counter, sleUint16, VER_V8),
- MKLINE(Player, _loop_to_beat, sleUint16, VER_V8),
- MKLINE(Player, _loop_from_beat, sleUint16, VER_V8),
- MKLINE(Player, _loop_counter, sleUint16, VER_V8),
- MKLINE(Player, _loop_to_tick, sleUint16, VER_V8),
- MKLINE(Player, _loop_from_tick, sleUint16, VER_V8),
- MKLINE(Player, _tempo, sleUint32, VER_V8),
- MKLINE(Player, _cur_pos, sleUint32, VER_V8),
- MKLINE(Player, _next_pos, sleUint32, VER_V8),
- MKLINE(Player, _song_offset, sleUint32, VER_V8),
- MKLINE(Player, _tick_index, sleUint16, VER_V8),
- MKLINE(Player, _beat_index, sleUint16, VER_V8),
- MKLINE(Player, _ticks_per_beat, sleUint16, VER_V8),
- MKLINE(Player, _hook._jump[0], sleByte, VER_V8),
- MKLINE(Player, _hook._transpose, sleByte, VER_V8),
- MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER_V8),
- MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER_V8),
- MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER_V8),
- MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER_V8),
- MKEND()
- };
-
// VolumeFader is obsolete.
const SaveLoadEntry volumeFaderEntries[] = {
MK_OBSOLETE_REF(VolumeFader, player, TYPE_PLAYER, VER_V8, VER_V16),
@@ -1300,15 +1213,6 @@ int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm) {
MKEND()
};
- const SaveLoadEntry parameterFaderEntries[] = {
- MKLINE(ParameterFader, param, sleInt16, VER_V17),
- MKLINE(ParameterFader, start, sleInt16, VER_V17),
- MKLINE(ParameterFader, end, sleInt16, VER_V17),
- MKLINE(ParameterFader, total_time, sleUint32, VER_V17),
- MKLINE(ParameterFader, current_time, sleUint32, VER_V17),
- MKEND()
- };
-
const SaveLoadEntry partEntries[] = {
MKREF(Part, _next, TYPE_PART, VER_V8),
MKREF(Part, _prev, TYPE_PART, VER_V8),
@@ -1348,11 +1252,8 @@ int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm) {
ser->_load_ref = loadReference;
ser->saveLoadEntries(this, mainEntries);
- for (i = 0; i < ARRAYSIZE(_players); ++i) {
- ser->saveLoadEntries (&_players[i], playerEntries);
- ser->saveLoadArrayOf (_players[i]._parameterFaders, ARRAYSIZE(_players[i]._parameterFaders),
- sizeof(ParameterFader), parameterFaderEntries);
- }
+ for (i = 0; i < ARRAYSIZE(_players); ++i)
+ _players[i].save_or_load (ser);
ser->saveLoadArrayOf(_parts, ARRAYSIZE(_parts), sizeof(_parts[0]), partEntries);
{ // Load/save the instrument definitions, which were revamped with V11.
@@ -1374,7 +1275,6 @@ int IMuseInternal::save_or_load(Serializer *ser, Scumm *scumm) {
if (!ser->isSaving()) {
// Load all sounds that we need
fix_players_after_load(scumm);
- init_sustaining_notes();
fix_parts_after_load();
set_master_volume (_master_volume);
@@ -1407,22 +1307,15 @@ void IMuseInternal::fix_players_after_load(Scumm *scumm) {
int i;
for (i = ARRAYSIZE(_players); i != 0; i--, player++) {
- if (player->_active) {
- scumm->getResourceAddress(rtSound, player->_id);
- player->_midi = getBestMidiDriver (player->_id);
- if (player->_midi == NULL) {
- player->clear();
- } else {
- player->set_tempo(player->_tempo);
- player->_mt32emulate = isMT32(player->_id);
- player->_isGM = isGM(player->_id);
- }
+ if (player->isActive()) {
+ scumm->getResourceAddress(rtSound, player->getID());
+ player->fixAfterLoad();
}
}
}
void Part::set_detune(int8 detune) {
- _detune_eff = clamp((_detune = detune) + _player->_detune, -128, 127);
+ _detune_eff = clamp((_detune = detune) + _player->getDetune(), -128, 127);
if (_mc) {
_mc->pitchBend (clamp(_pitchbend +
(_detune_eff * 64 / 12) +
@@ -1439,26 +1332,26 @@ void Part::set_pitchbend(int value) {
}
}
-void Part::set_vol(uint8 vol) {
- _vol_eff = ((_vol = vol) + 1) * _player->_vol_eff >> 7;
+void Part::setVolume(uint8 vol) {
+ _vol_eff = ((_vol = vol) + 1) * _player->getEffectiveVolume() >> 7;
if (_mc)
_mc->volume (_vol_eff);
}
void Part::set_pri(int8 pri) {
- _pri_eff = clamp((_pri = pri) + _player->_priority, 0, 255);
+ _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->_pan, -64, 63);
+ _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->_transpose, -12, 12);
+ _transpose_eff = transpose_clamp((_transpose = transpose) + _player->getTranspose(), -12, 12);
if (_mc) {
_mc->pitchBend (clamp(_pitchbend +
(_detune_eff * 64 / 12) +
@@ -1493,7 +1386,7 @@ void Part::set_effect_level(uint level)
void Part::fix_after_load() {
set_transpose(_transpose);
- set_vol(_vol);
+ setVolume(_vol);
set_detune(_detune);
set_pri(_pri);
set_pan(_pan);
@@ -1515,7 +1408,7 @@ void Part::set_onoff(bool on) {
if (!on)
off();
if (!_percussion)
- _player->_se->reallocateMidiChannels (_player->_midi);
+ _player->_se->reallocateMidiChannels (_player->getMidiDriver());
}
}
@@ -1547,7 +1440,7 @@ void Part::key_on(byte note, byte velocity) {
if (mc && _instrument.isValid()) {
mc->noteOn (note, velocity);
} else if (_percussion) {
- mc = _player->_midi->getPercussionChannel();
+ mc = _player->getMidiDriver()->getPercussionChannel();
if (!mc)
return;
mc->volume (_vol_eff);
@@ -1562,7 +1455,7 @@ void Part::key_off(byte note) {
if (mc) {
mc->noteOff (note);
} else if (_percussion) {
- mc = _player->_midi->getPercussionChannel();
+ mc = _player->getMidiDriver()->getPercussionChannel();
if (mc)
mc->noteOff (note);
}
@@ -1579,24 +1472,17 @@ void Part::init() {
void Part::setup(Player *player) {
_player = player;
- // Insert first into player's list
- _prev = NULL;
- _next = player->_parts;
- if (player->_parts)
- player->_parts->_prev = this;
- player->_parts = this;
-
- _percussion = (player->_isGM && _chan == 9); // true;
+ _percussion = (player->isGM() && _chan == 9); // true;
_on = true;
- _pri_eff = player->_priority;
+ _pri_eff = player->getPriority();
_pri = 0;
_vol = 127;
- _vol_eff = player->_vol_eff;
- _pan = clamp(player->_pan, -64, 63);
- _transpose_eff = player->_transpose;
+ _vol_eff = player->getEffectiveVolume();
+ _pan = clamp (player->getPan(), -64, 63);
+ _transpose_eff = player->getTranspose();
_transpose = 0;
_detune = 0;
- _detune_eff = player->_detune;
+ _detune_eff = player->getDetune();
_pitchbend_factor = 2;
_pitchbend = 0;
_effect_level = 64;
@@ -1613,17 +1499,8 @@ void Part::uninit() {
if (!_player)
return;
off();
-
- // Unlink
- if (_next)
- _next->_prev = _prev;
- if (_prev)
- _prev->_next = _next;
- else
- _player->_parts = _next;
+ _player->removePart (this);
_player = NULL;
- _next = NULL;
- _prev = NULL;
}
void Part::off() {
@@ -1637,7 +1514,7 @@ void Part::off() {
bool Part::clearToTransmit() {
if (_mc) return true;
- if (_instrument.isValid()) _player->_se->reallocateMidiChannels (_player->_midi);
+ if (_instrument.isValid()) _player->_se->reallocateMidiChannels (_player->getMidiDriver());
return false;
}
@@ -1684,18 +1561,25 @@ int Part::update_actives(uint16 *active) {
void Part::set_program(byte program) {
_bank = 0;
- _instrument.program (program, _player->_mt32emulate);
+ _instrument.program (program, _player->isMT32());
if (clearToTransmit())
_instrument.send (_mc);
}
void Part::set_instrument(uint b) {
_bank = (byte)(b >> 8);
- _instrument.program ((byte) b, _player->_mt32emulate);
+ _instrument.program ((byte) b, _player->isMT32());
if (clearToTransmit())
_instrument.send (_mc);
}
+void Part::silence() {
+ if (!_mc)
+ return;
+ _mc->allNotesOff();
+ memset (_actives, 0, sizeof (_actives));
+}
+
////////////////////////////////////////
//
// Some more IMuseInternal stuff
@@ -1718,7 +1602,7 @@ void IMuseInternal::reallocateMidiChannels (MidiDriver *midi) {
hipri = 0;
hipart = NULL;
for (i = 32, part = _parts; i; i--, part++) {
- if (part->_player && part->_player->_midi == midi &&
+ if (part->_player && part->_player->getMidiDriver() == midi &&
!part->_percussion && part->_on &&
!part->_mc && part->_pri_eff >= hipri)
{
diff --git a/scumm/imuse_internal.h b/scumm/imuse_internal.h
index 6ff5c7b02b..d035e72267 100644
--- a/scumm/imuse_internal.h
+++ b/scumm/imuse_internal.h
@@ -22,8 +22,9 @@
#ifndef DEFINED_IMUSE_INTERNAL
#define DEFINED_IMUSE_INTERNAL
+#include "sound/mididrv.h"
#include "common/scummsys.h"
-#include "instrument.h"
+#include "scumm/instrument.h"
struct HookDatas;
struct ParameterFader;
@@ -32,13 +33,12 @@ struct ImTrigger;
struct SustainingNotes;
struct CommandQueue;
struct IsNoteCmdData;
-struct Player;
+class Player;
struct Part;
-class IMuseInternal;
+class IMuseInternal;
// Some entities also referenced
-class MidiDriver;
-class MidiChannel;
+class MidiParser;
class Scumm;
class OSystem;
@@ -70,7 +70,7 @@ class OSystem;
//
////////////////////////////////////////
-static int clamp(int val, int min, int max) {
+inline int clamp(int val, int min, int max) {
if (val < min)
return min;
if (val > max)
@@ -78,7 +78,7 @@ static int clamp(int val, int min, int max) {
return val;
}
-static int transpose_clamp(int a, int b, int c) {
+inline int transpose_clamp(int a, int b, int c) {
if (b > a)
a += (b - a + 11) / 12 * 12;
if (c < a)
@@ -104,6 +104,7 @@ struct HookDatas {
int query_param(int param, byte chan);
int set(byte cls, byte value, byte chan);
+ HookDatas() { memset (this, 0, sizeof (HookDatas)); }
};
struct ParameterFader {
@@ -127,6 +128,7 @@ struct DeferredCommand {
MidiDriver *midi;
uint32 time_left;
int a, b, c, d, e, f;
+ DeferredCommand() { memset (this, 0, sizeof (DeferredCommand)); }
};
struct ImTrigger {
@@ -134,31 +136,18 @@ struct ImTrigger {
byte id;
uint16 expire;
byte command [4];
-};
-
-struct SustainingNotes {
- SustainingNotes *next;
- SustainingNotes *prev;
- Player *player;
- byte note, chan;
- uint32 off_pos;
- uint32 pos;
- uint16 counter;
+ ImTrigger() { memset (this, 0, sizeof (ImTrigger)); }
};
struct CommandQueue {
uint16 array[8];
+ CommandQueue() { memset (this, 0, sizeof (CommandQueue)); }
};
-struct IsNoteCmdData {
- byte chan;
- byte note;
- byte vel;
-};
-
-struct Player {
- IMuseInternal *_se;
+class Player : public MidiDriver {
+protected:
MidiDriver *_midi;
+ MidiParser *_parser;
Part *_parts;
bool _active;
@@ -169,12 +158,10 @@ struct Player {
int8 _pan;
int8 _transpose;
int8 _detune;
- uint _vol_chan;
byte _vol_eff;
uint _song_index;
uint _track_index;
- uint _timer_counter;
uint _loop_to_beat;
uint _loop_from_beat;
uint _loop_counter;
@@ -182,26 +169,24 @@ struct Player {
uint _loop_from_tick;
uint32 _tempo;
uint32 _tempo_eff; // No Save
- uint32 _cur_pos;
- uint32 _next_pos;
- uint32 _song_offset;
- uint32 _timer_speed; // No Save
- uint _tick_index;
- uint _beat_index;
- uint _ticks_per_beat;
- byte _speed; // No Save
+ 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 _mt32emulate;
+ bool _isMT32;
bool _isGM;
+protected:
// Player part
void hook_clear();
- void clear();
- bool startSound (int sound, MidiDriver *midi);
void uninit_parts();
byte *parse_midi(byte *s);
void key_off(uint8 chan, byte data);
@@ -215,20 +200,12 @@ struct Player {
void maybe_set_program(byte *data);
void maybe_set_transpose_part(byte *data);
uint update_actives();
- Part *get_part(uint8 part);
void turn_off_pedals();
- int set_vol(byte vol);
- int get_param(int param, byte chan);
- int query_part_param(int param, byte chan);
- int set_transpose(byte relative, int b);
- void set_priority(int pri);
- void set_pan(int pan);
- void set_detune(int detune);
+ int query_part_param(int param, byte chan);
void turn_off_parts();
void play_active_notes();
void cancel_volume_fade();
- int addParameterFader (int param, int target, int time);
void transitionParameters();
static void decode_sysex_bytes(byte *src, byte *dst, int len);
@@ -238,24 +215,68 @@ struct Player {
void clear_active_notes();
// Sequencer part
- bool set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick);
- void clear_loop();
- void set_speed(byte speed);
- bool jump(uint track, uint beat, uint tick);
void uninit_seq();
- void set_tempo(uint32 data);
+ void setTempo(uint32 data);
int start_seq_sound(int sound);
- void find_sustaining_notes(byte *a, byte *b, uint32 l);
- int scan(uint totrack, uint tobeat, uint totick);
int query_param(int param);
- bool is_fading_out();
- void sequencer_timer();
+public:
+ IMuseInternal *_se;
+ uint _vol_chan;
- Player() {
- memset(this,0,sizeof(Player));
- }
+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() { return _detune; }
+ byte getEffectiveVolume() { return _vol_eff; }
+ int getID() { return _id; }
+ MidiDriver *getMidiDriver() { return _midi; }
+ int getParam (int param, byte chan);
+ int8 getPan() { return _pan; }
+ Part * getPart (uint8 part);
+ byte getPriority() { return _priority; }
+ uint32 getTempo() { return _tempo; }
+ uint getTicksPerBeat() { return TICKS_PER_BEAT; }
+ int8 getTranspose() { return _transpose; }
+ byte getVolume() { return _volume; }
+ bool isActive() { return _active; }
+ bool isFadingOut();
+ bool isGM() { return _isGM; }
+ bool isMT32() { return _isMT32; }
+ bool jump (uint track, uint beat, uint tick);
+ void onTimer();
+ void removePart (Part *part);
+ int scan (uint totrack, uint tobeat, uint totick);
+ int save_or_load (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);
+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 {
@@ -300,7 +321,7 @@ struct Part {
void load_global_instrument (byte b);
void set_transpose(int8 transpose);
- void set_vol(uint8 volume);
+ void setVolume(uint8 volume);
void set_detune(int8 detune);
void set_pri(int8 pri);
void set_pan(int8 pan);
@@ -329,7 +350,7 @@ struct Part {
// imuse.h contains a public version of the same class.
// the public version, only contains a set of methods.
class IMuseInternal {
- friend struct Player;
+ friend class Player;
private:
bool _old_adlib_instruments;
@@ -348,10 +369,6 @@ private:
uint _queue_end, _queue_pos, _queue_sound;
byte _queue_adding;
- SustainingNotes *_sustain_notes_used;
- SustainingNotes *_sustain_notes_free;
- SustainingNotes *_sustain_notes_head;
-
byte _queue_marker;
byte _queue_cleared;
byte _master_volume; // Master volume. 0-255
@@ -366,7 +383,6 @@ private:
uint16 _volchan_table[8];
Player _players[8];
- SustainingNotes _sustaining_notes[24];
Part _parts[32];
uint16 _active_notes[128];
@@ -383,11 +399,9 @@ private:
void initMidiDriver (MidiDriver *midi);
void init_players();
void init_parts();
- void init_sustaining_notes();
void init_queue();
void sequencer_timers (MidiDriver *midi);
- void expire_sustain_notes (MidiDriver *midi);
MidiDriver *getBestMidiDriver (int sound);
Player *allocate_player(byte priority);
@@ -403,7 +417,7 @@ private:
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 *get_player_byid(int id);
+ Player *findActivePlayer (int id);
int get_volchan_entry(uint a);
int set_volchan_entry(uint a, uint b);
@@ -422,9 +436,7 @@ private:
static void midiTimerCallback (void *data);
public:
- IMuseInternal() {
- memset(this,0,sizeof(IMuseInternal));
- }
+ IMuseInternal();
~IMuseInternal();
int initialize(OSystem *syst, MidiDriver *midi);
diff --git a/scumm/imuse_player.cpp b/scumm/imuse_player.cpp
index 0b89070659..82255a5763 100644
--- a/scumm/imuse_player.cpp
+++ b/scumm/imuse_player.cpp
@@ -21,7 +21,8 @@
#include "stdafx.h"
#include "scumm/scumm.h"
-#include "sound/mididrv.h"
+#include "sound/midiparser.h"
+#include "scumm/saveload.h"
#include "common/util.h"
#include "common/engine.h"
#include "imuse_internal.h"
@@ -34,88 +35,10 @@
//
////////////////////////////////////////
-static uint32 get_delta_time(byte **s) {
- byte *d = *s, b;
- uint32 time = 0;
- do {
- b = *d++;
- time = (time << 7) | (b & 0x7F);
- } while (b & 0x80);
- *s = d;
- return time;
-}
-
static uint read_word(byte *a) {
return (a[0] << 8) + a[1];
}
-static void skip_midi_cmd(byte **song_ptr) {
- byte *s, code;
-
- const byte num_skip[] = {
- 2, 2, 2, 2, 1, 1, 2
- };
-
- s = *song_ptr;
-
- code = *s++;
-
- if (code < 0x80) {
- s = NULL;
- } else if (code < 0xF0) {
- s += num_skip[(code & 0x70) >> 4];
- } else {
- if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
- s += get_delta_time(&s);
- } else {
- s = NULL;
- }
- }
- *song_ptr = s;
-}
-
-static int is_note_cmd(byte **a, IsNoteCmdData * isnote) {
- byte *s = *a;
- byte code;
-
- code = *s++;
-
- switch (code >> 4) {
- case 8: // Key Off
- isnote->chan = code & 0xF;
- isnote->note = *s++;
- isnote->vel = *s++;
- *a = s;
- return 1;
- case 9: // Key On
- isnote->chan = code & 0xF;
- isnote->note = *s++;
- isnote->vel = *s++;
- *a = s;
- if (isnote->vel)
- return 2;
- return 1;
- case 0xA:
- case 0xB:
- case 0xE:
- s++;
- case 0xC:
- case 0xD:
- s++;
- break;
- case 0xF:
- if (code == 0xF0 || code == 0xF7 || code == 0xFF && *s++ != 0x2F) {
- s += get_delta_time(&s);
- break;
- }
- return -1;
- default:
- return -1;
- }
- *a = s;
- return 0;
-}
-
//////////////////////////////////////////////////
@@ -124,26 +47,41 @@ static int is_note_cmd(byte **a, IsNoteCmdData * isnote) {
//
//////////////////////////////////////////////////
-bool Player::is_fading_out() {
- int i;
- for (i = 0; i < ARRAYSIZE(_parameterFaders); ++i) {
- if (_parameterFaders[i].param == ParameterFader::pfVolume &&
- _parameterFaders[i].end == 0)
- {
- return true;
- }
+Player::Player() :
+_midi (0),
+_parser (0),
+_parts (0),
+_active (false),
+_scanning (false),
+_id (0),
+_priority (0),
+_volume (0),
+_pan (0),
+_transpose (0),
+_detune (0),
+_vol_eff (0),
+_song_index (0),
+_track_index (0),
+_loop_to_beat (0),
+_loop_from_beat (0),
+_loop_counter (0),
+_loop_to_tick (0),
+_loop_from_tick (0),
+_tempo (0),
+_tempo_eff (0),
+_speed (128),
+_abort (false),
+_isMT32 (false),
+_isGM (false),
+_se (0),
+_vol_chan (0)
+{ }
+
+Player::~Player() {
+ if (_parser) {
+ delete _parser;
+ _parser = 0;
}
- return false;
-}
-
-void Player::clear() {
- uninit_seq();
- cancel_volume_fade();
- uninit_parts();
- _se->ImFireAllTriggers (_id);
- _active = false;
- _ticks_per_beat = TICKS_PER_BEAT;
- _midi = NULL;
}
bool Player::startSound (int sound, MidiDriver *midi) {
@@ -159,7 +97,7 @@ bool Player::startSound (int sound, MidiDriver *midi) {
}
}
- _mt32emulate = _se->isMT32(sound);
+ _isMT32 = _se->isMT32(sound);
_isGM = _se->isGM(sound);
_parts = NULL;
@@ -186,15 +124,35 @@ bool Player::startSound (int sound, MidiDriver *midi) {
return true;
}
+bool Player::isFadingOut() {
+ 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() {
+ uninit_seq();
+ cancel_volume_fade();
+ uninit_parts();
+ _se->ImFireAllTriggers (_id);
+ _active = false;
+ _midi = NULL;
+}
+
void Player::hook_clear() {
memset(&_hook, 0, sizeof(_hook));
}
int Player::start_seq_sound(int sound) {
- byte *ptr, *track_ptr;
+ byte *ptr;
_song_index = sound;
- _timer_counter = 0;
_loop_to_beat = 1;
_loop_from_beat = 1;
_track_index = 0;
@@ -202,28 +160,27 @@ int Player::start_seq_sound(int sound) {
_loop_to_tick = 0;
_loop_from_tick = 0;
- set_tempo(500000);
- set_speed(128);
- ptr = _se->findTag(sound, "MTrk", _track_index);
+ setTempo(500000);
+ setSpeed(128);
+
+ ptr = _se->findTag (sound, "MThd", 0);
if (ptr == NULL)
return -1;
+ if (_parser)
+ delete _parser;
- track_ptr = ptr;
- _cur_pos = _next_pos = get_delta_time(&track_ptr);
- _song_offset = track_ptr - ptr;
-
- _tick_index = _cur_pos;
- _beat_index = 1;
-
- if (_tick_index >= _ticks_per_beat) {
- _beat_index += _tick_index / _ticks_per_beat;
- _tick_index %= _ticks_per_beat;
- }
+ ptr -= 8; // findTag puts us past the tag and length
+ _parser = MidiParser::createParser_SMF();
+ _parser->setTimerRate ((_midi->getBaseTempo() * _speed) >> 7);
+ _parser->setMidiDriver (this);
+ _parser->property (MidiParser::mpSmartJump, 1);
+ _parser->loadMusic (ptr, 0);
+ _parser->setTrack (_track_index);
return 0;
}
-void Player::set_tempo(uint32 b) {
+void Player::setTempo(uint32 b) {
uint32 i, j;
i = _midi->getBaseTempo();
@@ -237,8 +194,6 @@ void Player::set_tempo(uint32 b) {
}
_tempo_eff = (i << 16) / j;
-
- set_speed(_speed);
}
void Player::cancel_volume_fade() {
@@ -266,149 +221,110 @@ void Player::uninit_seq() {
_abort = true;
}
-void Player::set_speed(byte speed) {
+void Player::setSpeed(byte speed) {
_speed = speed;
- _timer_speed = (_tempo_eff * speed >> 7);
+ if (_parser)
+ _parser->setTimerRate ((_midi->getBaseTempo() * speed) >> 7);
}
-byte *Player::parse_midi(byte *s) {
- byte cmd, chan, note, velocity, control;
- uint value;
+void Player::send (uint32 b) {
+ byte cmd = (byte) (b & 0xF0);
+ byte chan = (byte) (b & 0x0F);
+ byte param1 = (byte) ((b >> 8) & 0xFF);
+ byte param2 = (byte) ((b >> 16) & 0xFF);
Part *part;
- cmd = *s++;
-
- chan = cmd & 0xF;
-
switch (cmd >> 4) {
case 0x8: // Key Off
- note = *s++;
- if (!_scanning) {
- key_off(chan, note);
- } else {
- clear_active_note(chan, note);
- }
- s++; // Skip velocity
+ if (!_scanning)
+ key_off (chan, param1);
+ else
+ clear_active_note (chan, param1);
break;
case 0x9: // Key On
- note = *s++;
- velocity = *s++;
- if (velocity) {
- if (!_scanning)
- key_on(chan, note, velocity);
- else
- set_active_note(chan, note);
- } else {
- if (!_scanning)
- key_off(chan, note);
- else
- clear_active_note(chan, note);
- }
- break;
-
- case 0xA: // Aftertouch
- s += 2;
+ if (!_scanning)
+ key_on (chan, param1, param2);
+ else
+ set_active_note (chan, param1);
break;
case 0xB: // Control Change
- control = *s++;
- value = *s++;
- part = get_part(chan);
+ part = (param1 == 123 ? getActivePart (chan) : getPart (chan));
if (!part)
break;
- switch (control) {
+ switch (param1) {
case 1: // Modulation Wheel
- part->set_modwheel(value);
+ part->set_modwheel (param2);
break;
case 7: // Volume
- part->set_vol(value);
+ part->setVolume (param2);
break;
case 10: // Pan Position
- part->set_pan(value - 0x40);
+ part->set_pan (param2 - 0x40);
break;
case 16: // Pitchbend Factor (non-standard)
- part->set_pitchbend_factor(value);
+ part->set_pitchbend_factor (param2);
break;
case 17: // GP Slider 2
- part->set_detune(value - 0x40);
+ part->set_detune (param2 - 0x40);
break;
case 18: // GP Slider 3
- part->set_pri(value - 0x40);
+ part->set_pri (param2 - 0x40);
_se->reallocateMidiChannels (_midi);
break;
case 64: // Sustain Pedal
- part->set_pedal(value != 0);
+ part->set_pedal (param2 != 0);
break;
case 91: // Effects Level
- part->set_effect_level(value);
+ part->set_effect_level (param2);
break;
case 93: // Chorus Level
- part->set_chorus(value);
+ part->set_chorus(param2);
+ break;
+ case 123: // All Notes Off
+ part->silence();
break;
default:
- warning("parse_midi: invalid control %d", control);
+ warning("Player::send(): Invalid control change %d", param1);
}
break;
case 0xC: // Program Change
- value = *s++;
- part = get_part(chan);
+ part = getPart (chan);
if (part) {
if (_isGM) {
- if (value < 128)
- part->set_program(value);
+ if (param1 < 128)
+ part->set_program (param1);
} else {
- if (value < 32)
- part->load_global_instrument(value);
+ if (param1 < 32)
+ part->load_global_instrument (param1);
}
}
break;
- case 0xD: // Channel Pressure
- s++;
- break;
-
case 0xE: // Pitch Bend
- part = get_part(chan);
+ part = getPart (chan);
if (part)
- part->set_pitchbend(((s[1] << 7) | s[0]) - 0x2000);
- s += 2;
+ part->set_pitchbend (((param2 << 7) | param1) - 0x2000);
break;
- case 0xF:
- if (chan == 0) {
- uint size = get_delta_time(&s);
- parse_sysex(s, size);
- s += size;
- } else if (chan == 0xF) {
- cmd = *s++;
- if (cmd == 47)
- goto Error; // End of song
- if (cmd == 81) {
- set_tempo((s[1] << 16) | (s[2] << 8) | s[3]);
- s += 4;
- break;
- }
- s += get_delta_time(&s);
- } else if (chan == 0x7) {
- s += get_delta_time(&s);
- } else {
- goto Error;
- }
+ case 0xA: // Aftertouch
+ case 0xD: // Channel Pressure
+ case 0xF: // Sequence Controls
break;
default:
- Error:;
- if (!_scanning)
+ if (!_scanning) {
+ warning ("Player::send(): Invalid command %d", cmd);
clear();
- return NULL;
+ }
}
- return s;
+ return;
}
-void Player::parse_sysex(byte *p, uint len) {
+void Player::sysEx (byte *p, uint16 len) {
byte code;
byte a;
uint b;
@@ -422,7 +338,7 @@ void Player::parse_sysex(byte *p, uint len) {
if (a != IMUSE_SYSEX_ID) {
if (a == ROLAND_SYSEX_ID) {
// Roland custom instrument definition.
- part = get_part (p[0] & 0x0F);
+ part = getPart (p[0] & 0x0F);
if (part) {
part->_instrument.roland (p - 1);
if (part->clearToTransmit())
@@ -464,10 +380,10 @@ void Player::parse_sysex(byte *p, uint len) {
// BYTE 09: BIT 04 (0x08): Percussion? (1 = yes)
// BYTE 15: Program (upper 4 bits)
// BYTE 16: Program (lower 4 bits)
- part = get_part (p[0] & 0x0F);
+ part = getPart (p[0] & 0x0F);
if (part) {
part->set_onoff (p[2] & 0x01);
- part->set_vol ((p[5] & 0x0F) << 4 | (p[6] & 0x0F));
+ part->setVolume ((p[5] & 0x0F) << 4 | (p[6] & 0x0F));
part->_percussion = _isGM ? ((p[9] & 0x08) > 0) : false;
if (part->_percussion) {
if (part->_mc) {
@@ -481,7 +397,7 @@ void Player::parse_sysex(byte *p, uint len) {
// cases, a regular program change message always seems to follow
// anyway.
if (_isGM)
- part->_instrument.program ((p[15] & 0x0F) << 4 | (p[16] & 0x0F), _mt32emulate);
+ part->_instrument.program ((p[15] & 0x0F) << 4 | (p[16] & 0x0F), _isMT32);
part->sendAll();
}
}
@@ -511,7 +427,7 @@ void Player::parse_sysex(byte *p, uint len) {
// This SysEx is used in Sam & Max for maybe_jump.
if (_scanning)
break;
- maybe_jump (p[0], p[1] - 1, (read_word (p + 2) - 1) * 4 + p[4], ((p[5] * _ticks_per_beat) >> 2) + p[6]);
+ maybe_jump (p[0], p[1] - 1, (read_word (p + 2) - 1) * 4 + p[4], ((p[5] * TICKS_PER_BEAT) >> 2) + p[6]);
break;
case 2: // Start of song. Ignore for now.
@@ -520,7 +436,7 @@ void Player::parse_sysex(byte *p, uint len) {
case 16: // Adlib instrument definition (Part)
a = *p++ & 0x0F;
++p; // Skip hardware type
- part = get_part(a);
+ part = getPart(a);
if (part) {
if (len == 63) {
decode_sysex_bytes(p, buf, len - 3);
@@ -543,7 +459,7 @@ void Player::parse_sysex(byte *p, uint len) {
a = *p++ & 0x0F;
++p; // Skip hardware type
decode_sysex_bytes(p, buf, len - 3);
- part = get_part(a);
+ part = getPart(a);
if (part)
part->set_param(read_word(buf), read_word(buf + 2));
break;
@@ -594,17 +510,17 @@ void Player::parse_sysex(byte *p, uint len) {
case 80: // Loop
decode_sysex_bytes(p + 1, buf, len - 2);
- set_loop(read_word(buf),
+ setLoop(read_word(buf),
read_word(buf + 2), read_word(buf + 4), read_word(buf + 6), read_word(buf + 8)
);
break;
case 81: // End loop
- clear_loop();
+ clearLoop();
break;
case 96: // Set instrument
- part = get_part(p[0] & 0x0F);
+ 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);
@@ -650,7 +566,7 @@ void Player::maybe_set_transpose(byte *data) {
if (cmd != 0 && cmd < 0x80)
_hook._transpose = 0;
- set_transpose(data[1], (int8)data[2]);
+ setTranspose(data[1], (int8)data[2]);
}
void Player::maybe_part_onoff(byte *data) {
@@ -670,7 +586,7 @@ void Player::maybe_part_onoff(byte *data) {
if (cmd != 0 && cmd < 0x80)
*p = 0;
- part = get_part(chan);
+ part = getPart(chan);
if (part)
part->set_onoff(data[2] != 0);
}
@@ -694,9 +610,9 @@ void Player::maybe_set_volume(byte *data) {
if (cmd != 0 && cmd < 0x80)
*p = 0;
- part = get_part(chan);
+ part = getPart(chan);
if (part)
- part->set_vol(data[2]);
+ part->setVolume(data[2]);
}
void Player::maybe_set_program(byte *data) {
@@ -717,7 +633,7 @@ void Player::maybe_set_program(byte *data) {
if (cmd != 0 && cmd < 0x80)
*p = 0;
- part = get_part(chan);
+ part = getPart(chan);
if (part)
part->set_program(data[2]);
}
@@ -743,7 +659,7 @@ void Player::maybe_set_transpose_part(byte *data) {
part_set_transpose(chan, data[2], (int8)data[3]);
}
-int Player::set_transpose(byte relative, int b) {
+int Player::setTranspose(byte relative, int b) {
Part *part;
if (b > 24 || b < -24 || relative > 1)
@@ -778,7 +694,7 @@ void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
if (b > 24 || b < -24)
return;
- part = get_part(chan);
+ part = getPart(chan);
if (!part)
return;
if (relative)
@@ -789,7 +705,7 @@ void Player::part_set_transpose(uint8 chan, byte relative, int8 b) {
void Player::key_on(uint8 chan, uint8 note, uint8 velocity) {
Part *part;
- part = get_part(chan);
+ part = getPart(chan);
if (!part || !part->_on)
return;
@@ -806,61 +722,16 @@ void Player::key_off(uint8 chan, uint8 note) {
}
bool Player::jump(uint track, uint beat, uint tick) {
- byte *mtrk, *cur_mtrk, *scanpos;
- uint32 topos, curpos, track_offs;
-
- if (!_active)
- return false;
-
- mtrk = _se->findTag(_song_index, "MTrk", track);
- if (!mtrk)
+ if (!_parser)
return false;
-
- cur_mtrk = _se->findTag(_song_index, "MTrk", _track_index);
- if (!cur_mtrk)
+ _parser->setTrack (track);
+ if (!_parser->jumpToTick ((beat - 1) * TICKS_PER_BEAT + tick))
return false;
-
- if (beat == 0)
- beat = 1;
-
- topos = (beat - 1) * _ticks_per_beat + tick;
-
- if (track == _track_index && topos >= _next_pos) {
- scanpos = _song_offset + mtrk;
- curpos = _next_pos;
- } else {
- scanpos = mtrk;
- curpos = get_delta_time(&scanpos);
- }
-
- while (curpos < topos) {
- skip_midi_cmd(&scanpos);
- if (!scanpos)
- return false;
- curpos += get_delta_time(&scanpos);
- }
-
- track_offs = scanpos - mtrk;
-
turn_off_pedals();
-
- find_sustaining_notes(cur_mtrk + _song_offset, mtrk + track_offs, curpos - topos);
-
- _beat_index = beat;
- _tick_index = tick;
- _cur_pos = topos;
- _next_pos = curpos;
- _timer_counter = 0;
- _song_offset = track_offs;
- if (track != _track_index) {
- _track_index = track;
- _loop_counter = 0;
- }
- _abort = true;
return true;
}
-bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
+bool Player::setLoop(uint count, uint tobeat, uint totick, uint frombeat, uint fromtick) {
if (tobeat + 1 >= frombeat)
return false;
@@ -877,7 +748,7 @@ bool Player::set_loop(uint count, uint tobeat, uint totick, uint frombeat, uint
return true;
}
-void Player::clear_loop() {
+void Player::clearLoop() {
_loop_counter = 0;
}
@@ -890,126 +761,20 @@ void Player::turn_off_pedals() {
}
}
-void Player::find_sustaining_notes(byte *a, byte *b, uint32 l) {
- uint32 pos;
- uint16 mask;
- uint16 *bitlist_ptr;
- SustainingNotes *sn, *next;
- IsNoteCmdData isnote;
- int j;
- uint num_active;
- uint max_off_pos;
-
- num_active = update_actives();
-
- // pos contains number of ticks since current position
- pos = _next_pos - _cur_pos;
- if ((int32)pos < 0)
- pos = 0;
-
- // Locate the positions where the notes are turned off.
- // Remember each note that was turned off.
- while (num_active != 0) {
- // Is note off?
- j = is_note_cmd(&a, &isnote);
- if (j == -1)
- break;
- if (j == 1) {
- mask = 1 << isnote.chan;
- bitlist_ptr = _se->_active_notes + isnote.note;
- if (*bitlist_ptr & mask) {
- *bitlist_ptr &= ~mask;
- num_active--;
- // Get a node from the free list
- if ((sn = _se->_sustain_notes_free) == NULL)
- return;
- _se->_sustain_notes_free = sn->next;
-
- // Insert it in the beginning of the used list
- sn->next = _se->_sustain_notes_used;
- _se->_sustain_notes_used = sn;
- sn->prev = NULL;
- if (sn->next)
- sn->next->prev = sn;
-
- sn->note = isnote.note;
- sn->chan = isnote.chan;
- sn->player = this;
- sn->off_pos = pos;
- sn->pos = 0;
- sn->counter = 0;
- }
- }
- pos += get_delta_time(&a);
- }
-
- // Find the maximum position where a note was turned off
- max_off_pos = 0;
- for (sn = _se->_sustain_notes_used; sn; sn = sn->next) {
- _se->_active_notes[sn->note] |= (1 << sn->chan);
- if (sn->off_pos > max_off_pos) {
- max_off_pos = sn->off_pos;
- }
- }
-
- // locate positions where notes are turned on
- pos = l;
- while (pos < max_off_pos) {
- j = is_note_cmd(&b, &isnote);
- if (j == -1)
- break;
- if (j == 2) {
- mask = 1 << isnote.chan;
- bitlist_ptr = _se->_active_notes + isnote.note;
-
- if (*bitlist_ptr & mask) {
- sn = _se->_sustain_notes_used;
- while (sn) {
- next = sn->next;
- if (sn->note == isnote.note && sn->chan == isnote.chan && pos < sn->off_pos) {
- *bitlist_ptr &= ~mask;
- // Unlink from the sustain list
- if (next)
- next->prev = sn->prev;
- if (sn->prev)
- sn->prev->next = next;
- else
- _se->_sustain_notes_used = next;
- // Insert into the free list
- sn->next = _se->_sustain_notes_free;
- _se->_sustain_notes_free = sn;
- }
- sn = next;
- }
- }
- }
- pos += get_delta_time(&b);
- }
-
- // Concatenate head and used list
- if (!_se->_sustain_notes_head) {
- _se->_sustain_notes_head = _se->_sustain_notes_used;
- _se->_sustain_notes_used = NULL;
- return;
- }
- sn = _se->_sustain_notes_head;
- while (sn->next)
- sn = sn->next;
- sn->next = _se->_sustain_notes_used;
- _se->_sustain_notes_used = NULL;
- if (sn->next)
- sn->next->prev = sn;
-}
-
-Part *Player::get_part(uint8 chan) {
- Part *part;
-
- part = _parts;
+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) {
@@ -1017,6 +782,14 @@ Part *Player::get_part(uint8 chan) {
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);
@@ -1037,7 +810,7 @@ uint Player::update_actives() {
return count;
}
-void Player::set_priority(int pri) {
+void Player::setPriority (int pri) {
Part *part;
_priority = pri;
@@ -1047,7 +820,7 @@ void Player::set_priority(int pri) {
_se->reallocateMidiChannels (_midi);
}
-void Player::set_pan(int pan) {
+void Player::setPan (int pan) {
Part *part;
_pan = pan;
@@ -1056,7 +829,7 @@ void Player::set_pan(int pan) {
}
}
-void Player::set_detune(int detune) {
+void Player::setDetune (int detune) {
Part *part;
_detune = detune;
@@ -1066,15 +839,7 @@ void Player::set_detune(int detune) {
}
int Player::scan(uint totrack, uint tobeat, uint totick) {
- byte *mtrk, *scanptr;
- uint32 curpos, topos;
- uint32 pos;
-
- if (!_active)
- return -1;
-
- mtrk = _se->findTag(_song_index, "MTrk", totrack);
- if (!mtrk)
+ if (!_active || !_parser)
return -1;
if (tobeat == 0)
@@ -1082,31 +847,18 @@ int Player::scan(uint totrack, uint tobeat, uint totick) {
turn_off_parts();
clear_active_notes();
- scanptr = mtrk;
- curpos = get_delta_time(&scanptr);
_scanning = true;
- topos = (tobeat - 1) * _ticks_per_beat + totick;
-
- while (curpos < topos) {
- scanptr = parse_midi(scanptr);
- if (!scanptr) {
- _scanning = false;
- return -1;
- }
- curpos += get_delta_time(&scanptr);
+ _parser->setTrack (totrack);
+ if (!_parser->jumpToTick ((tobeat - 1) * TICKS_PER_BEAT + totick, true)) {
+ _scanning = false;
+ return -1;
}
- pos = scanptr - mtrk;
_scanning = false;
_se->reallocateMidiChannels (_midi);
play_active_notes();
- _beat_index = tobeat;
- _tick_index = totick;
- _cur_pos = topos;
- _next_pos = curpos;
- _timer_counter = 0;
- _song_offset = pos;
+
if (_track_index != totrack) {
_track_index = totrack;
_loop_counter = 0;
@@ -1136,7 +888,7 @@ void Player::play_active_notes() {
}
}
-int Player::set_vol(byte vol) {
+int Player::setVolume(byte vol) {
Part *part;
if (vol > 127)
@@ -1146,13 +898,13 @@ int Player::set_vol(byte vol) {
_vol_eff = _se->get_channel_volume(_vol_chan) * (vol + 1) >> 7;
for (part = _parts; part; part = part->_next) {
- part->set_vol(part->_vol);
+ part->setVolume(part->_vol);
}
return 0;
}
-int Player::get_param(int param, byte chan) {
+int Player::getParam(int param, byte chan) {
switch (param) {
case 0:
return (byte)_priority;
@@ -1169,9 +921,9 @@ int Player::get_param(int param, byte chan) {
case 6:
return _track_index;
case 7:
- return _beat_index;
+ return getBeatIndex();
case 8:
- return _tick_index;
+ return (_parser ? _parser->getTick() % TICKS_PER_BEAT : 0); // _tick_index;
case 9:
return _loop_counter;
case 10:
@@ -1223,11 +975,7 @@ int Player::query_part_param(int param, byte chan) {
return 129;
}
-void Player::sequencer_timer() {
- byte *mtrk;
- uint32 counter;
- byte *song_ptr;
-
+void Player::onTimer() {
// First handle any parameter transitions
// that are occuring.
transitionParameters();
@@ -1235,40 +983,20 @@ void Player::sequencer_timer() {
// Since the volume parameter can cause
// the player to be deactivated, check
// to make sure we're still active.
- if (!_active)
+ if (!_active || !_parser)
return;
- counter = _timer_counter + _timer_speed;
- _timer_counter = counter & 0xFFFF;
- _cur_pos += counter >> 16;
- _tick_index += counter >> 16;
+ uint32 target_tick = _parser->getTick();
+ uint beat_index = target_tick / TICKS_PER_BEAT + 1;
+ uint tick_index = target_tick % TICKS_PER_BEAT;
- if (_tick_index >= _ticks_per_beat) {
- _beat_index += _tick_index / _ticks_per_beat;
- _tick_index %= _ticks_per_beat;
- }
- if (_loop_counter && _beat_index >= _loop_from_beat && _tick_index >= _loop_from_tick) {
+ 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);
- }
- if (_next_pos <= _cur_pos) {
- mtrk = _se->findTag(_song_index, "MTrk", _track_index);
- if (!mtrk) {
- warning("Sound %d was unloaded while active", _song_index);
- clear();
- } else {
- song_ptr = mtrk + _song_offset;
- _abort = false;
-
- while (_next_pos <= _cur_pos) {
- song_ptr = parse_midi(song_ptr);
- if (!song_ptr || _abort)
- return;
- _next_pos += get_delta_time(&song_ptr);
- _song_offset = song_ptr - mtrk;
- }
- }
+ jump (_track_index, _loop_to_beat, _loop_to_tick);
}
+ _parser->onTimer();
}
// "time" is referenced as hundredths of a second.
@@ -1336,7 +1064,7 @@ int Player::addParameterFader (int param, int target, int time) {
}
void Player::transitionParameters() {
- uint32 advance = _midi->getBaseTempo() / 500;
+ uint32 advance = _midi->getBaseTempo();
int value;
ParameterFader *ptr = &_parameterFaders[0];
@@ -1357,17 +1085,17 @@ void Player::transitionParameters() {
clear();
return;
}
- set_vol ((byte) value);
+ setVolume ((byte) value);
break;
case ParameterFader::pfSpeed:
// Speed.
- set_speed ((byte) value);
+ setSpeed ((byte) value);
break;
case ParameterFader::pfTranspose:
// FIXME: Is this really transpose?
- set_transpose (0, value);
+ setTranspose (0, value);
break;
}
@@ -1375,3 +1103,121 @@ void Player::transitionParameters() {
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);
+ setTempo (_tempo);
+ if (_parser) {
+ _parser->setTrack (_track_index);
+ _parser->jumpToTick (_music_tick);
+ }
+ _isMT32 = _se->isMT32 (_id);
+ _isGM = _se->isGM (_id);
+ }
+}
+
+uint32 Player::getBaseTempo() {
+ return (_midi ? _midi->getBaseTempo() : 0);
+}
+
+void Player::metaEvent (byte type, byte *msg, uint16 len) {
+ if (type == 0x2F) {
+ _parser->jumpToTick (0); // That aborts current parsing
+ clear();
+ return;
+ }
+
+ if (type == 0x51)
+ setTempo ((msg[0] << 16) | (msg[1] << 8) | msg[2]);
+}
+
+
+
+////////////////////////////////////////
+//
+// Player save/load functions
+//
+////////////////////////////////////////
+
+enum {
+ TYPE_PART = 1,
+ TYPE_PLAYER = 2
+};
+
+int Player::save_or_load(Serializer *ser) {
+ static const SaveLoadEntry playerEntries[] = {
+ MKREF(Player, _parts, TYPE_PART, VER_V8),
+ MKLINE(Player, _active, sleByte, VER_V8),
+ MKLINE(Player, _id, sleUint16, VER_V8),
+ MKLINE(Player, _priority, sleByte, VER_V8),
+ MKLINE(Player, _volume, sleByte, VER_V8),
+ MKLINE(Player, _pan, sleInt8, VER_V8),
+ MKLINE(Player, _transpose, sleByte, VER_V8),
+ MKLINE(Player, _detune, sleInt8, VER_V8),
+ MKLINE(Player, _vol_chan, sleUint16, VER_V8),
+ MKLINE(Player, _vol_eff, sleByte, VER_V8),
+ MKLINE(Player, _speed, sleByte, VER_V8),
+ MKLINE(Player, _song_index, sleUint16, VER_V8),
+ MKLINE(Player, _track_index, sleUint16, VER_V8),
+ MK_OBSOLETE(Player, _timer_counter, sleUint16, VER_V8, VER_V17),
+ MKLINE(Player, _loop_to_beat, sleUint16, VER_V8),
+ MKLINE(Player, _loop_from_beat, sleUint16, VER_V8),
+ MKLINE(Player, _loop_counter, sleUint16, VER_V8),
+ MKLINE(Player, _loop_to_tick, sleUint16, VER_V8),
+ MKLINE(Player, _loop_from_tick, sleUint16, VER_V8),
+ MKLINE(Player, _tempo, sleUint32, VER_V8),
+ MK_OBSOLETE(Player, _cur_pos, sleUint32, VER_V8, VER_V17),
+ MK_OBSOLETE(Player, _next_pos, sleUint32, VER_V8, VER_V17),
+ MK_OBSOLETE(Player, _song_offset, sleUint32, VER_V8, VER_V17),
+ MK_OBSOLETE(Player, _tick_index, sleUint16, VER_V8, VER_V17),
+ MK_OBSOLETE(Player, _beat_index, sleUint16, VER_V8, VER_V17),
+ MK_OBSOLETE(Player, _ticks_per_beat, sleUint16, VER_V8, VER_V17),
+ MKLINE(Player, _music_tick, sleUint32, VER_V19),
+ MKLINE(Player, _hook._jump[0], sleByte, VER_V8),
+ MKLINE(Player, _hook._transpose, sleByte, VER_V8),
+ MKARRAY(Player, _hook._part_onoff[0], sleByte, 16, VER_V8),
+ MKARRAY(Player, _hook._part_volume[0], sleByte, 16, VER_V8),
+ MKARRAY(Player, _hook._part_program[0], sleByte, 16, VER_V8),
+ MKARRAY(Player, _hook._part_transpose[0], sleByte, 16, VER_V8),
+ MKEND()
+ };
+
+ const SaveLoadEntry parameterFaderEntries[] = {
+ MKLINE(ParameterFader, param, sleInt16, VER_V17),
+ MKLINE(ParameterFader, start, sleInt16, VER_V17),
+ MKLINE(ParameterFader, end, sleInt16, VER_V17),
+ MKLINE(ParameterFader, total_time, sleUint32, VER_V17),
+ MKLINE(ParameterFader, current_time, sleUint32, VER_V17),
+ MKEND()
+ };
+
+ if (!ser->isSaving() && _parser) {
+ delete _parser;
+ _parser = 0;
+ }
+ _music_tick = _parser ? _parser->getTick() : 0;
+
+ ser->saveLoadEntries (this, playerEntries);
+ ser->saveLoadArrayOf (_parameterFaders, ARRAYSIZE(_parameterFaders),
+ sizeof(ParameterFader), parameterFaderEntries);
+ return 0;
+}
diff --git a/scumm/saveload.h b/scumm/saveload.h
index 66114eac90..efc0228c56 100644
--- a/scumm/saveload.h
+++ b/scumm/saveload.h
@@ -37,10 +37,11 @@ enum {
VER_V15,
VER_V16,
VER_V17,
- VER_V18
+ VER_V18,
+ VER_V19
};
-#define CURRENT_VER VER_V18
+#define CURRENT_VER VER_V19
// To work around a warning in GCC 3.2 (and 3.1 ?) regarding non-POD types,
diff --git a/scumm/script_v6.cpp b/scumm/script_v6.cpp
index fbe380ff5c..4443e432b3 100644
--- a/scumm/script_v6.cpp
+++ b/scumm/script_v6.cpp
@@ -2647,7 +2647,7 @@ void Scumm_v6::o6_kernelSetFunctions() {
break;
case 124: /* samnmax */
- warning("o6_kernelSetFunctions: _saveSound=%d", args[1]);
+// warning("o6_kernelSetFunctions: _saveSound=%d", args[1]);
_saveSound = args[1];
break;
diff --git a/sound/mididrv.h b/sound/mididrv.h
index 55b5a8dad2..58bbe0dcdf 100644
--- a/sound/mididrv.h
+++ b/sound/mididrv.h
@@ -29,8 +29,9 @@ class MidiChannel;
// Abstract MIDI Driver Class
class MidiDriver {
-
public:
+ virtual ~MidiDriver() { }
+
// Error codes returned by open.
// Can be converted to a string with getErrorName()
enum {
@@ -41,7 +42,7 @@ public:
};
enum {
- PROP_TIMEDIV = 1,
+// PROP_TIMEDIV = 1,
PROP_OLD_ADLIB = 2
};
diff --git a/sound/midiparser.cpp b/sound/midiparser.cpp
index e781238d3b..4680d8295c 100644
--- a/sound/midiparser.cpp
+++ b/sound/midiparser.cpp
@@ -26,6 +26,8 @@
#include <stdio.h>
#include <memory.h>
+
+
//////////////////////////////////////////////////
//
// MidiParser implementation
@@ -42,15 +44,10 @@ _autoLoop (false),
_smartJump (false),
_num_tracks (0),
_active_track (255),
-_play_pos (0),
-_play_time (0),
-_play_tick (0),
-_last_event_time (0),
-_last_event_tick (0),
-_running_status (0),
+_abort_parse (0),
_hanging_notes_count (0)
{
- memset (_active_notes, 0, 128);
+ memset (_active_notes, 0, sizeof(_active_notes));
}
void MidiParser::property (int prop, int value) {
@@ -62,6 +59,12 @@ void MidiParser::property (int prop, int value) {
}
}
+void MidiParser::setTempo (uint32 tempo) {
+ _tempo = tempo;
+ if (_ppqn)
+ _psec_per_tick = (tempo + (_ppqn >> 2)) / _ppqn;
+}
+
// This is the conventional (i.e. SMF) variable length quantity
uint32 MidiParser::readVLQ (byte * &data) {
byte str;
@@ -137,10 +140,11 @@ void MidiParser::onTimer() {
uint32 end_time;
uint32 event_time;
- if (!_play_pos || !_driver)
+ if (!_position._play_pos || !_driver)
return;
- end_time = _play_time + _timer_rate;
+ _abort_parse = false;
+ end_time = _position._play_time + _timer_rate;
// Scan our hanging notes for any
// that should be turned off.
@@ -160,18 +164,18 @@ void MidiParser::onTimer() {
}
}
- while (true) {
+ while (!_abort_parse) {
EventInfo &info = _next_event;
- event_time = _last_event_time + info.delta * _psec_per_tick;
+ event_time = _position._last_event_time + info.delta * _psec_per_tick;
if (event_time > end_time)
break;
// Process the next info.
- _last_event_tick += info.delta;
+ _position._last_event_tick += info.delta;
if (info.event < 0x80) {
printf ("ERROR! Bad command or running status %02X\n", info.event);
- _play_pos = 0;
+ _position._play_pos = 0;
return;
}
@@ -185,17 +189,16 @@ void MidiParser::onTimer() {
// as well as sending it to the output device.
allNotesOff();
if (_autoLoop) {
- _play_pos = _tracks[_active_track];
+ _position._play_pos = _tracks[_active_track];
parseNextEvent (_next_event);
} else {
- _play_pos = 0;
+ _position._play_pos = 0;
_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.length);
}
return;
} else if (info.ext.type == 0x51) {
if (info.length >= 3) {
- _tempo = info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2];
- _psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn;
+ setTempo (info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
}
}
_driver->metaEvent (info.ext.type, info.ext.data, (uint16) info.length);
@@ -212,12 +215,16 @@ void MidiParser::onTimer() {
}
- _last_event_time = event_time;
- parseNextEvent (_next_event);
+ if (!_abort_parse) {
+ _position._last_event_time = event_time;
+ parseNextEvent (_next_event);
+ }
}
- _play_time = end_time;
- _play_tick = (_play_time - _last_event_time) / _psec_per_tick + _last_event_tick;
+ if (!_abort_parse) {
+ _position._play_time = end_time;
+ _position._play_tick = (_position._play_time - _position._last_event_time) / _psec_per_tick + _position._last_event_tick;
+ }
}
void MidiParser::allNotesOff() {
@@ -230,18 +237,11 @@ void MidiParser::allNotesOff() {
for (i = 0; i < ARRAYSIZE(_hanging_notes); ++i)
_hanging_notes[i].time_left = 0;
_hanging_notes_count = 0;
- memset (_active_notes, 0, 128);
+ memset (_active_notes, 0, sizeof(_active_notes));
}
void MidiParser::resetTracking() {
- _play_pos = 0;
- _tempo = 500000;
- _psec_per_tick = 500000 / _ppqn;
- _play_time = 0;
- _play_tick = 0;
- _last_event_time = 0;
- _last_event_tick = 0;
- _running_status = 0;
+ _position.clear();
}
bool MidiParser::setTrack (int track) {
@@ -250,79 +250,107 @@ bool MidiParser::setTrack (int track) {
else if (track == _active_track)
return true;
+ if (_smartJump)
+ hangAllActiveNotes();
+ else
+ allNotesOff();
+
resetTracking();
- allNotesOff();
_active_track = track;
- _play_pos = _tracks[track];
+ _position._play_pos = _tracks[track];
parseNextEvent (_next_event);
return true;
}
-void MidiParser::jumpToTick (uint32 tick) {
- if (_active_track >= _num_tracks)
- return;
-
- if (!_smartJump) {
- allNotesOff();
- } else {
- // Search for note off events until we have
- // accounted for every active note.
- uint32 advance_tick = _last_event_tick;
- while (true) {
- int i;
- for (i = 0; i < 128; ++i)
- if (_active_notes[i] != 0) break;
- if (i == 128) break;
- parseNextEvent (_next_event);
- advance_tick += _next_event.delta;
- if (_next_event.command() == 0x8) {
- if (_active_notes [_next_event.basic.param1] & (1 << _next_event.channel())) {
- hangingNote (_next_event.channel(), _next_event.basic.param1, (advance_tick - _last_event_tick) * _psec_per_tick);
- _active_notes [_next_event.basic.param1] &= ~ (1 << _next_event.channel());
- }
- } else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) {
- break;
+void MidiParser::hangAllActiveNotes() {
+ // Search for note off events until we have
+ // accounted for every active note.
+ uint32 advance_tick = _position._last_event_tick;
+ while (true) {
+ int i;
+ for (i = 0; i < 128; ++i)
+ if (_active_notes[i] != 0) break;
+ if (i == 128) break;
+ parseNextEvent (_next_event);
+ advance_tick += _next_event.delta;
+ if (_next_event.command() == 0x8) {
+ if (_active_notes [_next_event.basic.param1] & (1 << _next_event.channel())) {
+ hangingNote (_next_event.channel(), _next_event.basic.param1, (advance_tick - _position._last_event_tick) * _psec_per_tick);
+ _active_notes [_next_event.basic.param1] &= ~ (1 << _next_event.channel());
}
+ } else if (_next_event.event == 0xFF && _next_event.ext.type == 0x2F) {
+ printf ("MidiParser::hangAllActiveNotes(): Hit End of Track with active notes left!\n");
+ memset (_active_notes, 0, sizeof (_active_notes));
+ break;
}
}
+}
- resetTracking();
- _play_pos = _tracks[_active_track];
- parseNextEvent (_next_event);
- if (tick == 0)
- return;
-
- while (true) {
- EventInfo &info = _next_event;
- if (_last_event_tick + info.delta >= tick) {
- _play_time += (tick - _last_event_tick) * _psec_per_tick;
- _play_tick = tick;
- break;
- }
+bool MidiParser::jumpToTick (uint32 tick, bool fireEvents) {
+ if (_active_track >= _num_tracks)
+ return false;
- _last_event_tick += info.delta;
- _play_tick = _last_event_tick;
- _play_time += info.delta * _psec_per_tick;
- _last_event_time = _play_time;
+ Tracker currentPos (_position);
+ EventInfo currentEvent (_next_event);
- if (info.event == 0xFF) {
- if (info.ext.type == 0x2F) { // End of track
- if (_autoLoop) {
- _play_pos = _tracks[_active_track];
- parseNextEvent (_next_event);
- } else {
- _play_pos = 0;
- _driver->metaEvent (0x2F, info.ext.data, (uint16) info.length);
- }
+ resetTracking();
+ _position._play_pos = _tracks[_active_track];
+ parseNextEvent (_next_event);
+ if (tick > 0) {
+ while (true) {
+ EventInfo &info = _next_event;
+ if (_position._last_event_tick + info.delta >= tick) {
+ _position._play_time += (tick - _position._last_event_tick) * _psec_per_tick;
+ _position._play_tick = tick;
break;
- } else if (info.ext.type == 0x51) { // Tempo
- if (info.length >= 3) {
- _tempo = info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2];
- _psec_per_tick = (_tempo + (_ppqn >> 2)) / _ppqn;
+ }
+
+ _position._last_event_tick += info.delta;
+ _position._last_event_time += info.delta * _psec_per_tick;
+ _position._play_tick = _position._last_event_tick;
+ _position._play_time = _position._last_event_time;
+
+ if (info.event == 0xFF) {
+ if (info.ext.type == 0x2F) { // End of track
+ if (_autoLoop) {
+ _position._play_pos = _tracks[_active_track];
+ parseNextEvent (_next_event);
+ } else {
+ _position = currentPos;
+ _next_event = currentEvent;
+ return false;
+ }
+ break;
+ } else if (info.ext.type == 0x51) { // Tempo
+ if (info.length >= 3) {
+ setTempo (info.ext.data[0] << 16 | info.ext.data[1] << 8 | info.ext.data[2]);
+ }
}
+ } else if (fireEvents) {
+ if (info.event == 0xF0)
+ _driver->sysEx (info.ext.data, (uint16) info.length);
+ else
+ _driver->send (info.event | info.basic.param1 << 8 | info.basic.param2 << 16);
}
+
+ parseNextEvent (_next_event);
}
+ }
- parseNextEvent (_next_event);
+ if (!_smartJump) {
+ allNotesOff();
+ } else {
+ EventInfo targetEvent (_next_event);
+ Tracker targetPosition (_position);
+
+ _position = currentPos;
+ _next_event = currentEvent;
+ hangAllActiveNotes();
+
+ _next_event = targetEvent;
+ _position = targetPosition;
}
+
+ _abort_parse = true;
+ return true;
}
diff --git a/sound/midiparser.h b/sound/midiparser.h
index 8958891894..d8c2c373e4 100644
--- a/sound/midiparser.h
+++ b/sound/midiparser.h
@@ -28,6 +28,43 @@ class MidiParser;
class MidiDriver;
+
+
+//////////////////////////////////////////////////
+//
+// Support entities
+//
+//////////////////////////////////////////////////
+
+struct Tracker {
+ byte * _play_pos;
+ uint32 _play_time;
+ uint32 _play_tick;
+ uint32 _last_event_time;
+ uint32 _last_event_tick;
+ byte _running_status;
+
+ Tracker() { clear(); }
+
+ Tracker (const Tracker &copy) :
+ _play_pos (copy._play_pos),
+ _play_time (copy._play_time),
+ _play_tick (copy._play_tick),
+ _last_event_time (copy._last_event_time),
+ _last_event_tick (copy._last_event_tick),
+ _running_status (copy._running_status)
+ { }
+
+ void clear() {
+ _play_pos = 0;
+ _play_time = 0;
+ _play_tick = 0;
+ _last_event_time = 0;
+ _last_event_tick = 0;
+ _running_status = 0;
+ }
+};
+
struct EventInfo {
byte * start; // Points to delta
uint32 delta;
@@ -55,6 +92,15 @@ struct NoteTimer {
NoteTimer() : channel(0), note(0), time_left(0) {}
};
+
+
+
+//////////////////////////////////////////////////
+//
+// MidiParser declaration
+//
+//////////////////////////////////////////////////
+
class MidiParser {
private:
uint16 _active_notes[128]; // Each uint16 is a bit mask for channels that have that note on
@@ -70,17 +116,13 @@ protected:
bool _autoLoop; // For lightweight clients that don't monitor events
bool _smartJump; // Support smart expiration of hanging notes when jumping
- byte * _tracks[16];
+ byte * _tracks[32];
byte _num_tracks;
byte _active_track;
- byte * _play_pos;
- uint32 _play_time;
- uint32 _play_tick;
- uint32 _last_event_time;
- uint32 _last_event_tick;
- byte _running_status;
+ Tracker _position;
EventInfo _next_event;
+ bool _abort_parse; // If a jump or some other interruption occurs
protected:
static uint32 readVLQ (byte * &data);
@@ -90,6 +132,7 @@ protected:
void activeNote (byte channel, byte note, bool active);
void hangingNote (byte channel, byte note, uint32 ticks_left);
+ void hangAllActiveNotes();
// Multi-byte read helpers
uint32 read4high (byte * &data) {
@@ -121,11 +164,13 @@ public:
virtual void property (int prop, int value);
void setMidiDriver (MidiDriver *driver) { _driver = driver; }
- void setTimerRate (uint32 rate) { _timer_rate = rate / 500; }
+ void setTimerRate (uint32 rate) { _timer_rate = rate; }
+ void setTempo (uint32 tempo);
void onTimer();
bool setTrack (int track);
- void jumpToTick (uint32 tick);
+ bool jumpToTick (uint32 tick, bool fireEvents = false);
+ uint32 getTick() { return _position._play_tick; }
static MidiParser *createParser_SMF();
static MidiParser *createParser_XMIDI();
diff --git a/sound/midiparser_smf.cpp b/sound/midiparser_smf.cpp
index d2e56d9dfc..0b9379ab33 100644
--- a/sound/midiparser_smf.cpp
+++ b/sound/midiparser_smf.cpp
@@ -80,8 +80,8 @@ void MidiParser_SMF::property (int prop, int value) {
}
void MidiParser_SMF::parseNextEvent (EventInfo &info) {
- info.start = _play_pos;
- info.delta = readVLQ (_play_pos);
+ info.start = _position._play_pos;
+ info.delta = readVLQ (_position._play_pos);
// Process the next info. If mpMalformedPitchBends
// was set, we must skip over any pitch bend events
@@ -89,23 +89,24 @@ void MidiParser_SMF::parseNextEvent (EventInfo &info) {
// real pitch bend events, they're just two-byte
// prefixes before the real info.
do {
- if ((_play_pos[0] & 0xF0) >= 0x80)
- info.event = *(_play_pos++);
+ if ((_position._play_pos[0] & 0xF0) >= 0x80)
+ info.event = *(_position._play_pos++);
else
- info.event = _running_status;
- } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _play_pos++);
+ info.event = _position._running_status;
+ } while (_malformedPitchBends && (info.event & 0xF0) == 0xE0 && _position._play_pos++);
if (info.event < 0x80)
return;
+ _position._running_status = info.event;
switch (info.command()) {
case 0xC: case 0xD:
- info.basic.param1 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
info.basic.param2 = 0;
break;
case 0x8: case 0x9: case 0xA: case 0xB: case 0xE:
- info.basic.param1 = *(_play_pos++);
- info.basic.param2 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
if (info.command() == 0x9 && info.basic.param2 == 0)
info.event = info.channel() | 0x80;
info.length = 0;
@@ -114,12 +115,12 @@ void MidiParser_SMF::parseNextEvent (EventInfo &info) {
case 0xF: // System Common, Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_play_pos++);
- info.basic.param2 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
info.basic.param2 = 0;
break;
@@ -128,16 +129,16 @@ void MidiParser_SMF::parseNextEvent (EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ (_play_pos);
- info.ext.data = _play_pos;
- _play_pos += info.length;
+ info.length = readVLQ (_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_play_pos++);
- info.length = readVLQ (_play_pos);
- info.ext.data = _play_pos;
- _play_pos += info.length;
+ info.ext.type = *(_position._play_pos++);
+ info.length = readVLQ (_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
break;
}
}
@@ -192,8 +193,8 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) {
}
// Now we identify and store the location for each track.
- if (_num_tracks > 16) {
- printf ("Can only handle 16 tracks but was handed %d\n", (int) _num_tracks);
+ if (_num_tracks > ARRAYSIZE(_tracks)) {
+ printf ("Can only handle %d tracks but was handed %d\n", (int) ARRAYSIZE(_tracks), (int) _num_tracks);
return false;
}
diff --git a/sound/midiparser_xmidi.cpp b/sound/midiparser_xmidi.cpp
index acaccff0b2..0d651a3349 100644
--- a/sound/midiparser_xmidi.cpp
+++ b/sound/midiparser_xmidi.cpp
@@ -76,17 +76,17 @@ uint32 MidiParser_XMIDI::readVLQ2 (byte * &pos) {
}
void MidiParser_XMIDI::parseNextEvent (EventInfo &info) {
- info.start = _play_pos;
- info.delta = readVLQ2 (_play_pos) - _inserted_delta;
+ info.start = _position._play_pos;
+ info.delta = readVLQ2 (_position._play_pos) - _inserted_delta;
// Process the next event.
_inserted_delta = 0;
- info.event = *(_play_pos++);
+ info.event = *(_position._play_pos++);
switch (info.event >> 4) {
case 0x9: // Note On
- info.basic.param1 = *(_play_pos++);
- info.basic.param2 = *(_play_pos++);
- info.length = readVLQ (_play_pos);
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
+ info.length = readVLQ (_position._play_pos);
if (info.basic.param2 == 0) {
info.event = info.channel() | 0x80;
info.length = 0;
@@ -94,24 +94,24 @@ void MidiParser_XMIDI::parseNextEvent (EventInfo &info) {
break;
case 0xC: case 0xD:
- info.basic.param1 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
info.basic.param2 = 0;
break;
case 0x8: case 0xA: case 0xB: case 0xE:
- info.basic.param1 = *(_play_pos++);
- info.basic.param2 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
break;
case 0xF: // Meta or SysEx event
switch (info.event & 0x0F) {
case 0x2: // Song Position Pointer
- info.basic.param1 = *(_play_pos++);
- info.basic.param2 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
+ info.basic.param2 = *(_position._play_pos++);
break;
case 0x3: // Song Select
- info.basic.param1 = *(_play_pos++);
+ info.basic.param1 = *(_position._play_pos++);
info.basic.param2 = 0;
break;
@@ -120,16 +120,16 @@ void MidiParser_XMIDI::parseNextEvent (EventInfo &info) {
break;
case 0x0: // SysEx
- info.length = readVLQ (_play_pos);
- info.ext.data = _play_pos;
- _play_pos += info.length;
+ info.length = readVLQ (_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
break;
case 0xF: // META event
- info.ext.type = *(_play_pos++);
- info.length = readVLQ (_play_pos);
- info.ext.data = _play_pos;
- _play_pos += info.length;
+ info.ext.type = *(_position._play_pos++);
+ info.length = readVLQ (_position._play_pos);
+ info.ext.data = _position._play_pos;
+ _position._play_pos += info.length;
if (info.ext.type == 0x51 && info.length == 3) {
// Tempo event. We want to make these constant 500,000.
info.ext.data[0] = 0x07;
@@ -236,8 +236,8 @@ bool MidiParser_XMIDI::loadMusic (byte *data, uint32 size) {
// Ok it's an XMIDI.
// We're going to identify and store the location for each track.
- if (_num_tracks > 16) {
- printf ("Can only handle 16 tracks but was handed %d\n", (int) _num_tracks);
+ if (_num_tracks > ARRAYSIZE(_tracks)) {
+ printf ("Can only handle %d tracks but was handed %d\n", (int) ARRAYSIZE(_tracks), (int) _num_tracks);
return false;
}
diff --git a/sound/mpu401.h b/sound/mpu401.h
index 3f4cfecfee..cd495b5040 100644
--- a/sound/mpu401.h
+++ b/sound/mpu401.h
@@ -88,7 +88,7 @@ public:
MidiDriver_MPU401();
void setTimerCallback(void *timer_param, void (*timer_proc) (void *));
- uint32 getBaseTempo(void) { return 0x4A0000; }
+ uint32 getBaseTempo(void) { return 10000; } // 0x4A0000; } // Now referenced in microseconds between callbacks
virtual void sysEx_customInstrument (byte channel, uint32 type, byte *instr);