Fixed Simon 1 music regression.

SMF parser now supports malformed Simon pitch bend events.
Implemented SMF parser jump method.

svn-id: r7669
This commit is contained in:
Jamieson Christian 2003-05-19 05:00:13 +00:00
parent 243bcc74d3
commit ff41896480
5 changed files with 119 additions and 31 deletions

View file

@ -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)) {

View file

@ -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);

View file

@ -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;
}
}

View file

@ -33,11 +33,17 @@ protected:
MidiDriver *_driver;
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

View file

@ -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; }