From a08d06ad353996ee9bf495a84988f3a91cacf51a Mon Sep 17 00:00:00 2001 From: Filippos Karapetis Date: Fri, 11 Jun 2010 14:47:13 +0000 Subject: - Added a new debug command, verify_midi, which can be used to check all the songs of a game for unmapped instruments (still WIP and disabled) - Fixed a bug in the verify_scripts command (it was loading the script resource twice) svn-id: r49597 --- engines/sci/console.cpp | 106 ++++++++++++++++++++++++++++++++++- engines/sci/console.h | 1 + engines/sci/sound/midiparser_sci.cpp | 42 ++++++++------ engines/sci/sound/midiparser_sci.h | 2 + 4 files changed, 132 insertions(+), 19 deletions(-) diff --git a/engines/sci/console.cpp b/engines/sci/console.cpp index f025c63335..ac165f22b1 100644 --- a/engines/sci/console.cpp +++ b/engines/sci/console.cpp @@ -39,6 +39,7 @@ #include "sci/sound/iterator/songlib.h" // for SongLibrary #include "sci/sound/iterator/iterator.h" // for SCI_SONG_ITERATOR_TYPE_SCI0 #else +#include "sci/sound/midiparser_sci.h" #include "sci/sound/music.h" #endif #include "sci/sound/drivers/mididriver.h" @@ -106,6 +107,7 @@ Console::Console(SciEngine *engine) : GUI::Debugger() { DCmd_Register("list", WRAP_METHOD(Console, cmdList)); DCmd_Register("hexgrep", WRAP_METHOD(Console, cmdHexgrep)); DCmd_Register("verify_scripts", WRAP_METHOD(Console, cmdVerifyScripts)); + DCmd_Register("verify_midi", WRAP_METHOD(Console, cmdVerifyMidi)); // Game DCmd_Register("save_game", WRAP_METHOD(Console, cmdSaveGame)); DCmd_Register("restore_game", WRAP_METHOD(Console, cmdRestoreGame)); @@ -326,6 +328,7 @@ bool Console::cmdHelp(int argc, const char **argv) { DebugPrintf(" list - Lists all the resources of a given type\n"); DebugPrintf(" hexgrep - Searches some resources for a particular sequence of bytes, represented as hexadecimal numbers\n"); DebugPrintf(" verify_scripts - Performs sanity checks on SCI1.1-SCI2.1 game scripts (e.g. if they're up to 64KB in total)\n"); + DebugPrintf(" verify_midi - Performs checks on MIDI patches, for unmapped instruments\n"); DebugPrintf("\n"); DebugPrintf("Game:\n"); DebugPrintf(" save_game - Saves the current game state to the hard disk\n"); @@ -832,7 +835,7 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) { if (!script) DebugPrintf("Error: script %d couldn't be loaded\n", itr->number); - heap = _engine->getResMan()->findResource(*itr, false); + heap = _engine->getResMan()->findResource(ResourceId(kResourceTypeHeap, itr->number), false); if (!heap) DebugPrintf("Error: script %d doesn't have a corresponding heap\n", itr->number); @@ -848,6 +851,107 @@ bool Console::cmdVerifyScripts(int argc, const char **argv) { return true; } +bool Console::cmdVerifyMidi(int argc, const char **argv) { + // TODO: Sometimes this goes out of bounds with some songs, as it misses + // the EOT signal +#if 0 + SciVersion doSoundVersion = _engine->_features->detectDoSoundType(); + MidiPlayer *player = MidiPlayer_Midi_create(doSoundVersion); + MidiParser_SCI *parser = new MidiParser_SCI(doSoundVersion); + parser->setMidiDriver(player); + + Common::List *resources = _engine->getResMan()->listResources(kResourceTypeSound); + sort(resources->begin(), resources->end(), ResourceIdLess()); + Common::List::iterator itr = resources->begin(); + + DebugPrintf("%d sounds found, checking their instrument mappings...\n", resources->size()); + + SoundResource *sound; + + while (itr != resources->end()) { + sound = new SoundResource(itr->number, _engine->getResMan(), doSoundVersion); + int channelFilterMask = sound->getChannelFilterMask(player->getPlayId(), player->hasRhythmChannel()); + SoundResource::Track *track = sound->getTrackByType(player->getPlayId()); + if (track->digitalChannelNr != -1) { + // Skip digitized sound effects + delete sound; + continue; + } + + parser->loadMusic(track, NULL, channelFilterMask, doSoundVersion); + const byte *channelData = parser->getMixedData(); + + byte param1 = 0; + byte command = 0, prev = 0; + byte curEvent = 0; + bool endOfTrack = false; + + do { + while (*channelData == 0xF8) + channelData++; + + channelData++; // delta + + if ((*channelData & 0xF0) >= 0x80) + curEvent = *(channelData++); + else + curEvent = prev; + if (curEvent < 0x80) + continue; + + prev = curEvent; + command = curEvent >> 4; + + switch (command) { + case 0xC: // program change + param1 = *channelData++; + // TODO: verify that the instrument is mapped + printf("Song %d, patch %d\n", itr->number, param1); + break; + case 0xD: + case 0xB: + param1 = *channelData++; + break; + case 0x8: + case 0x9: + case 0xA: + case 0xE: + param1 = *channelData++; + *channelData++; // param2 + break; + case 0xF: + if ((curEvent & 0x0F) == 0x2) { + param1 = *channelData++; + *channelData++; // param2 + } else if ((curEvent & 0x0F) == 0x3) { + param1 = *channelData++; + } else if ((curEvent & 0x0F) == 0xF) { // META + byte type = *channelData++; + if (type == 0x2F) {// end of track reached + endOfTrack = true; + } else { + // no further processing necessary + } + } + break; + default: + break; + } + } while (!endOfTrack); + + delete sound; + ++itr; + } + + delete parser; + delete player; + + DebugPrintf("Music check finished\n"); +#endif + + return true; +} + bool Console::cmdList(int argc, const char **argv) { if (argc < 2) { DebugPrintf("Lists all the resources of a given type\n"); diff --git a/engines/sci/console.h b/engines/sci/console.h index 2b13e03ef6..572a62c274 100644 --- a/engines/sci/console.h +++ b/engines/sci/console.h @@ -74,6 +74,7 @@ private: bool cmdList(int argc, const char **argv); bool cmdHexgrep(int argc, const char **argv); bool cmdVerifyScripts(int argc, const char **argv); + bool cmdVerifyMidi(int argc, const char **argv); // Game bool cmdSaveGame(int argc, const char **argv); bool cmdRestoreGame(int argc, const char **argv); diff --git a/engines/sci/sound/midiparser_sci.cpp b/engines/sci/sound/midiparser_sci.cpp index 8f88945888..f852367bfe 100644 --- a/engines/sci/sound/midiparser_sci.cpp +++ b/engines/sci/sound/midiparser_sci.cpp @@ -75,7 +75,8 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _pSnd = psnd; _soundVersion = soundVersion; - setVolume(psnd->volume); + if (_pSnd) + setVolume(psnd->volume); if (channelFilterMask) { // SCI0 only has 1 data stream, but we need to filter out channels depending on music hardware selection @@ -86,30 +87,35 @@ bool MidiParser_SCI::loadMusic(SoundResource::Track *track, MusicEntry *psnd, in _num_tracks = 1; _tracks[0] = _mixedData; - setTrack(0); + if (_pSnd) + setTrack(0); _loopTick = 0; - if (_soundVersion <= SCI_VERSION_0_LATE) { - // Set initial voice count - for (int i = 0; i < 16; ++i) { - byte voiceCount = 0; - if (channelFilterMask & (1 << i)) - voiceCount = psnd->soundRes->getInitialVoiceCount(i); - _driver->send(0xB0 | i, 0x4B, voiceCount); + if (_pSnd) { + if (_soundVersion <= SCI_VERSION_0_LATE) { + // Set initial voice count + for (int i = 0; i < 16; ++i) { + byte voiceCount = 0; + if (channelFilterMask & (1 << i)) + voiceCount = psnd->soundRes->getInitialVoiceCount(i); + _driver->send(0xB0 | i, 0x4B, voiceCount); + } } - } - // Send a velocity off signal to all channels - for (int i = 0; i < 16; ++i) { - _driver->send(0xB0 | i, 0x4E, 0); // Reset velocity + // Send a velocity off signal to all channels + for (int i = 0; i < 16; ++i) { + _driver->send(0xB0 | i, 0x4E, 0); // Reset velocity + } } return true; } void MidiParser_SCI::unloadMusic() { - resetTracking(); - allNotesOff(); + if (_pSnd) { + resetTracking(); + allNotesOff(); + } _num_tracks = 0; _active_track = 255; _resetOnPause = false; @@ -120,7 +126,7 @@ void MidiParser_SCI::unloadMusic() { } // Center the pitch wheels and hold pedal in preparation for the next piece of music - if (_driver) { + if (_driver && _pSnd) { for (int i = 0; i < 16; ++i) { if (isChannelUsed(i)) { _driver->send(0xE0 | i, 0, 0x40); // Reset pitch wheel @@ -359,10 +365,10 @@ byte *MidiParser_SCI::midiMixChannels() { long new_delta; SoundResource::Channel *channel; - while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still active channel + while ((curr = midiGetNextChannel(ticker)) != 0xFF) { // there is still an active channel channel = &_track->channels[curr]; curDelta = *channel->data++; - channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the comamnd is supposed to occur + channel->time += (curDelta == 0xF8 ? 240 : curDelta); // when the command is supposed to occur if (curDelta == 0xF8) continue; new_delta = channel->time - ticker; diff --git a/engines/sci/sound/midiparser_sci.h b/engines/sci/sound/midiparser_sci.h index 9d4b5a39da..8384c74cf6 100644 --- a/engines/sci/sound/midiparser_sci.h +++ b/engines/sci/sound/midiparser_sci.h @@ -79,6 +79,8 @@ public: void clearUsedChannels() { _channelsUsed = 0; } + const byte *getMixedData() const { return _mixedData; } + protected: bool isChannelUsed(byte channel) const { return _channelsUsed & (1 << channel); } void setChannelUsed(byte channel) { _channelsUsed |= (1 << channel); } -- cgit v1.2.3