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:
parent
243bcc74d3
commit
ff41896480
5 changed files with 119 additions and 31 deletions
|
@ -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)) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue