/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ #include "common/debug.h" #include "agos/midiparser_simonwin.h" #include "audio/mididrv.h" namespace AGOS { MidiParser_SimonWin::MidiParser_SimonWin(int8 source, bool useDosTempos) : MidiParser_SMF(source), _useDosTempos(useDosTempos) { } void MidiParser_SimonWin::parseNextEvent(EventInfo &info) { byte *parsePos = _position._subtracks[info.subtrack]._playPos; uint8 *start = parsePos; uint32 delta = readVLQ(parsePos); uint8 event = *(parsePos++); // Pitch bend events in the Simon 1 data are broken. They contain just the // command byte; no data bytes follow. We generate an empty event and set // the noop flag to have MidiParser process only the delta and otherwise // ignore the event. if ((event & 0xF0) == MidiDriver::MIDI_COMMAND_PITCH_BEND) { info.start = start; info.delta = delta; info.event = event; info.basic.param1 = 0; info.basic.param2 = 0; info.length = 0; info.noop = true; _position._subtracks[info.subtrack]._playPos = parsePos; } else { // Processing of the other events is the same as the SMF format. info.noop = false; MidiParser_SMF::parseNextEvent(info); } } void MidiParser_SimonWin::setTempo(uint32 tempo) { uint32 newTempo = tempo; if (_useDosTempos && tempo < 750000) { // WORKAROUND The tempos set in the SMF data of Simon 1 Windows are // faster than the DOS version for the faster tempos. These are // corrected here to match the DOS version. The correct tempos have // been determined by measuring the tempos generated by the DOS version // running in DOSBox. newTempo = 330000 + (((tempo / 125000) - 2) * 105000); } MidiParser::setTempo(newTempo); } int32 MidiParser_SimonWin::determineDataSize(Common::SeekableReadStream *stream) { int64 startPos = stream->pos(); // Read the number of tracks. byte numSongs = stream->readByte(); if (numSongs > MAXIMUM_TRACKS) { warning("MidiParser_SimonWin::determineDataSize - Can only handle %d tracks but was handed %d", MAXIMUM_TRACKS, numSongs); return -1; } // Add up the sizes of the individual SMF tracks. int32 totalSize = 1; for (int i = 0; i < numSongs; ++i) { int size = MidiParser_SMF::determineDataSize(stream); if (size < 0) return -1; totalSize += size; stream->seek(startPos + totalSize, SEEK_SET); } return totalSize; } bool MidiParser_SimonWin::loadMusic(byte *data, uint32 size) { assert(size > 7); unloadMusic(); // The first byte indicates the number of tracks in the MIDI data. byte *pos = data; _numTracks = *(pos++); if (_numTracks > MAXIMUM_TRACKS) { warning("MidiParser_SimonWin::loadMusic - Can only handle %d tracks but was handed %d", MAXIMUM_TRACKS, _numTracks); return false; } debug(2, "MidiParser_SimonWin::loadMusic: %d tracks", _numTracks); // Read the tracks. for (int i = 0; i < _numTracks; ++i) { // Read the track header. // Make sure there's a MThd. if (memcmp(pos, "MThd", 4) != 0) { warning("MidiParser_SimonWin::loadMusic - Expected MThd but found '%c%c%c%c' instead", pos[0], pos[1], pos[2], pos[3]); return false; } pos += 4; // Verify correct header length. uint32 len = read4high(pos); if (len != 6) { warning("MidiParser_SimonWin::loadMusic - MThd length 6 expected but found %d", len); return false; } // Verify that this MIDI is type 0 or 1 (it is expected to be type 1). uint16 numSubtracks = pos[2] << 8 | pos[3]; assert(numSubtracks >= 1 && numSubtracks <= MAXIMUM_SUBTRACKS); uint8 subtrackMidiType = pos[1]; if (subtrackMidiType >= 2) { warning("MidiParser_SimonWin::loadMusic - MIDI track contained a type %d subtrack", subtrackMidiType); return false; } _numSubtracks[i] = numSubtracks; // Each track could potentially have a different PPQN, but for Simon 1 // and 2 all tracks have PPQN 192, so this is not a problem. _ppqn = pos[4] << 8 | pos[5]; pos += len; // Now determine all the MTrk (sub)track start offsets. for (int j = 0; j < numSubtracks; j++) { if (memcmp(pos, "MTrk", 4) != 0) { warning("MidiParser_SimonWin::loadMusic - Could not find subtrack header at expected location"); return false; } pos += 4; uint32 subtrackLength = READ_BE_UINT32(pos); pos += 4; // Note that we assume the original data passed in // will persist beyond this call, i.e. we do NOT // copy the data to our own buffer. Take warning.... _tracks[i][j] = pos; pos += subtrackLength; } } _disableAutoStartPlayback = true; resetTracking(); setTempo(500000); setTrack(0); return true; } } // End of namespace AGOS