aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);