diff options
-rw-r--r-- | backends/midi/adlib.cpp | 4 | ||||
-rw-r--r-- | scumm/imuse.cpp | 366 | ||||
-rw-r--r-- | scumm/imuse_internal.h | 156 | ||||
-rw-r--r-- | scumm/imuse_player.cpp | 764 | ||||
-rw-r--r-- | scumm/saveload.h | 5 | ||||
-rw-r--r-- | scumm/script_v6.cpp | 2 | ||||
-rw-r--r-- | sound/mididrv.h | 5 | ||||
-rw-r--r-- | sound/midiparser.cpp | 202 | ||||
-rw-r--r-- | sound/midiparser.h | 63 | ||||
-rw-r--r-- | sound/midiparser_smf.cpp | 43 | ||||
-rw-r--r-- | sound/midiparser_xmidi.cpp | 42 | ||||
-rw-r--r-- | sound/mpu401.h | 2 |
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 ©) : + _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); |