diff options
| -rw-r--r-- | simon/midi.cpp | 23 | ||||
| -rw-r--r-- | simon/midi.h | 2 | ||||
| -rw-r--r-- | simon/simon.cpp | 9 | ||||
| -rw-r--r-- | sound/midiparser.h | 7 | ||||
| -rw-r--r-- | sound/midiparser_smf.cpp | 109 |
5 files changed, 119 insertions, 31 deletions
diff --git a/simon/midi.cpp b/simon/midi.cpp index 0cb9815d31..8ba97f40a3 100644 --- a/simon/midi.cpp +++ b/simon/midi.cpp @@ -71,11 +71,14 @@ void MidiPlayer::close() { void MidiPlayer::send (uint32 b) { byte volume; - // Only thing we care about is volume control changes. if ((b & 0xFFF0) == 0x07B0) { + // Adjust volume changes by master volume. volume = (byte) ((b >> 16) & 0xFF) * _masterVolume / 255; _volumeTable [b & 0xF] = volume; b = (b & 0xFF00FFFF) | (volume << 16); + } else if ((b & 0xF0) == 0xE0) { + // Skip pitch bends completely. They're screwed up in Simon games. + return; } _driver->send (b); @@ -116,6 +119,7 @@ void MidiPlayer::jump (uint16 track, uint16 tick) { } MidiParser *parser = MidiParser::createParser_SMF(); + parser->property (MidiParser::mpMalformedPitchBends, 1); parser->setMidiDriver (this); parser->setTimerRate (_driver->getBaseTempo()); if (!parser->loadMusic (_songs[track], _song_sizes[track])) { @@ -206,7 +210,14 @@ void MidiPlayer::clearConstructs() { } } -void MidiPlayer::playSMF (File *in) { +static int simon1_gmf_size[] = { + 8900, 12166, 2848, 3442, 4034, 4508, 7064, 9730, 6014, 4742, 3138, + 6570, 5384, 8909, 6457, 16321, 2742, 8968, 4804, 8442, 7717, + 9444, 5800, 1381, 5660, 6684, 2456, 4744, 2455, 1177, 1232, + 17256, 5103, 8794, 4884, 16 +}; + +void MidiPlayer::playSMF (File *in, int song) { _system->lock_mutex (_mutex); clearConstructs(); uint32 size = in->size() - in->pos(); @@ -215,7 +226,15 @@ void MidiPlayer::playSMF (File *in) { _data = (byte *) calloc (size, 1); in->read (_data, size); + // For GMF files, we're going to have to use + // hardcoded size tables. + if (!memcmp (_data, "GMF\x1", 4) && size == 64000) { + size = simon1_gmf_size [song]; + _data[size++] = 0x00; // Trailing 0 makes this match the standalone GMF files + } + MidiParser *parser = MidiParser::createParser_SMF(); + parser->property (MidiParser::mpMalformedPitchBends, 1); parser->setMidiDriver (this); parser->setTimerRate (_driver->getBaseTempo()); if (!parser->loadMusic (_data, size)) { diff --git a/simon/midi.h b/simon/midi.h index 9f66419b05..2fc899dd56 100644 --- a/simon/midi.h +++ b/simon/midi.h @@ -54,7 +54,7 @@ public: MidiPlayer (OSystem *system); virtual ~MidiPlayer(); - void playSMF (File *in); + void playSMF (File *in, int song); void playMultipleSMF (File *in); void playXMIDI (File *in); void jump (uint16 track, uint16 tick); diff --git a/simon/simon.cpp b/simon/simon.cpp index 8f5f4f561f..e216d669e4 100644 --- a/simon/simon.cpp +++ b/simon/simon.cpp @@ -862,10 +862,7 @@ void SimonState::playSting(uint a) { // midi.shutdown(); _mus_file->seek(_mus_offsets[a], SEEK_SET); - // midi.read_all_songs_old(_mus_file, a, _mus_offsets[a+1] - _mus_offsets[a]); - // midi.initialize(); - // midi.play(); - midi.playSMF (_mus_file); + midi.playSMF (_mus_file, a); } Subroutine *SimonState::getSubroutineByID(uint subroutine_id) { @@ -5072,7 +5069,7 @@ void SimonState::playMusic(uint music_unk, uint music) { midi.playMultipleSMF (_game_file); } else if (_game & GF_TALKIE) { _game_file->seek(_game_offsets_ptr[gss->MUSIC_INDEX_BASE + music], SEEK_SET); - midi.playSMF (_game_file); + midi.playSMF (_game_file, music); } else { char buf[50]; File *f = new File(); @@ -5082,7 +5079,7 @@ void SimonState::playMusic(uint music_unk, uint music) { warning("Can't load music from '%s'", buf); return; } - midi.playSMF (f); + midi.playSMF (f, music); delete f; } } diff --git a/sound/midiparser.h b/sound/midiparser.h index 3a87bf3ce0..842cc6ed51 100644 --- a/sound/midiparser.h +++ b/sound/midiparser.h @@ -34,10 +34,16 @@ protected: uint32 _timer_rate; public: + enum { + mpMalformedPitchBends = 1 + }; + +public: virtual ~MidiParser() { } virtual bool loadMusic (byte *data, uint32 size) = 0; virtual void unloadMusic() = 0; + virtual void property (int property, int value) { } void setMidiDriver (MidiDriver *driver) { _driver = driver; } void setTimerRate (uint32 rate) { _timer_rate = rate / 500; } @@ -48,6 +54,7 @@ public: static MidiParser *createParser_SMF(); static MidiParser *createParser_XMIDI(); + static void timerCallback (void *data) { ((MidiParser *) data)->onTimer(); } }; #endif diff --git a/sound/midiparser_smf.cpp b/sound/midiparser_smf.cpp index f1a7df8f2b..845f935da8 100644 --- a/sound/midiparser_smf.cpp +++ b/sound/midiparser_smf.cpp @@ -39,6 +39,7 @@ protected: uint16 _num_tracks; byte *_tracks [16]; + bool _malformedPitchBends; byte _active_track; byte *_play_pos; uint32 _play_time; @@ -73,6 +74,7 @@ public: bool loadMusic (byte *data, uint32 size); void unloadMusic(); + void property (int property, int value); void setMidiDriver (MidiDriver *driver) { _driver = driver; } void setTimerRate (uint32 rate) { _timer_rate = rate; } void onTimer(); @@ -92,11 +94,21 @@ public: // ////////////////////////////////////////////////// +static byte command_lengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 }; +static byte special_lengths[16] = { 0, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; + MidiParser_SMF::~MidiParser_SMF() { if (_buffer) free (_buffer); } +void MidiParser_SMF::property (int property, int value) { + switch (property) { + case mpMalformedPitchBends: + _malformedPitchBends = (value > 0); + } +} + // This is the conventional (i.e. SMF) variable length quantity uint32 MidiParser_SMF::readVLQ (byte * &data) { byte str; @@ -141,10 +153,12 @@ void MidiParser_SMF::playToTime (uint32 psec, bool transmit) { } // Process the next event. - if ((pos[0] & 0xF0) >= 0x80) - event = *pos++; - else - event = _running_status; + do { + if ((pos[0] & 0xF0) >= 0x80) + event = *pos++; + else + event = _running_status; + } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && pos++); if (event < 0x80) { printf ("ERROR! Bad command or running status %02X", event); @@ -237,12 +251,13 @@ void MidiParser_SMF::playToTime (uint32 psec, bool transmit) { bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { uint32 len; - bool isGMD = false; // Indicates an older GMD file without block headers byte midi_type; uint32 total_size; + bool isGMF; unloadMusic(); byte *pos = data; + isGMF = false; if (!memcmp (pos, "RIFF", 4)) { // Skip the outer RIFF header. @@ -272,7 +287,7 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { } else if (!memcmp (pos, "GMF\x1", 4)) { // Older GMD/MUS file with no header info. // Assume 1 track, 192 PPQN, and no MTrk headers. - isGMD = true; + isGMF = true; midi_type = 0; _num_tracks = 1; _ppqn = 192; @@ -291,15 +306,15 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { total_size = 0; int tracks_read = 0; while (tracks_read < _num_tracks) { - if (memcmp (pos, "MTrk", 4) && !isGMD) { + if (memcmp (pos, "MTrk", 4) && !isGMF) { printf ("Position: %p ('%c')\n", pos, *pos); printf ("Hit invalid block '%c%c%c%c' while scanning for track locations\n", pos[0], pos[1], pos[2], pos[3]); return false; } // If needed, skip the MTrk and length bytes - _tracks[tracks_read] = pos + (isGMD ? 0 : 8); - if (!isGMD) { + _tracks[tracks_read] = pos + (isGMF ? 0 : 8); + if (!isGMF) { pos += 4; len = read4high (pos); total_size += len; @@ -307,10 +322,10 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { } else { // An SMF End of Track meta event must be placed // at the end of the stream. - data[size] = 0xFF; - data[size+1] = 0x2F; - data[size+2] = 0x00; - data[size+3] = 0x00; + data[size++] = 0xFF; + data[size++] = 0x2F; + data[size++] = 0x00; + data[size++] = 0x00; } ++tracks_read; } @@ -343,8 +358,6 @@ bool MidiParser_SMF::loadMusic (byte *data, uint32 size) { void MidiParser_SMF::compressToType0() { // We assume that _buffer has been allocated // to sufficient size for this operation. - byte command_lengths[8] = { 3, 3, 3, 3, 2, 2, 3, 0 }; - byte special_lengths[16] = { 0, 1, 1, 3, 2, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 }; byte *track_pos[16]; byte running_status[16]; uint32 track_timer[16]; @@ -389,9 +402,11 @@ void MidiParser_SMF::compressToType0() { // Process MIDI event. copy_bytes = 0; pos = track_pos[best_i]; - event = *(pos++); - if (event < 0x80) - event = running_status[best_i]; + do { + event = *(pos++); + if (event < 0x80) + event = running_status[best_i]; + } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && pos++); running_status[best_i] = event; if (command_lengths [(event >> 4) - 8] > 0) { @@ -487,11 +502,61 @@ void MidiParser_SMF::jumpToTick (uint32 tick) { _play_pos = _tracks[_active_track]; _play_time = 0; _last_event_time = 0; - if (tick > 0) { - printf ("jumpToTick (%ld) not completely implemented!\n", (long) tick); - playToTime (tick * _psec_per_tick - 1, false); - } allNotesOff(); + if (tick == 0) + return; + + uint32 current_tick = 0; + byte *start; + uint32 event_count = 0; + + while (current_tick < tick) { + start = _play_pos; + uint32 delta = readVLQ (_play_pos); + + if (current_tick + delta >= tick) { + _play_pos = start; + _play_time += (tick - current_tick) * _psec_per_tick; + break; + } + + ++event_count; + current_tick += delta; + _play_time += delta * _psec_per_tick; + _last_event_time = _play_time; + + byte event; + do { + event = *_play_pos; + if (event < 0x80) + event = _running_status; + } while (_malformedPitchBends && (event & 0xF0) == 0xE0 && _play_pos++); + _running_status = event; + + byte bytes_to_skip = 0; + if (command_lengths[(event >> 4) - 8] > 0) { + _play_pos += command_lengths[(event >> 4) - 8]; + } else if (special_lengths[event & 0xF] > 0) { + _play_pos += special_lengths[event & 0xF]; + } else if (event == 0xF0) { + uint32 length = readVLQ (++_play_pos); + _play_pos += length; + } else if (event == 0xFF) { + event = *(++_play_pos); + uint32 length = readVLQ (++_play_pos); + if (event == 0x2F) { // End of track + _play_pos = 0; + _driver->metaEvent (event, _play_pos, (uint16) length); + break; + } else if (event == 0x51) { // Tempo + if (length >= 3) { + delta = _play_pos[0] << 16 | _play_pos[1] << 8 | _play_pos[2]; + _psec_per_tick = (delta + (_ppqn >> 2)) / _ppqn; + } + } + _play_pos += length; + } + } } MidiParser *MidiParser::createParser_SMF() { return new MidiParser_SMF; } |
