mirror of
https://github.com/scummvm/scummvm.git
synced 2025-04-02 10:52:32 -04:00
170 lines
5.3 KiB
C++
170 lines
5.3 KiB
C++
/* 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 <http://www.gnu.org/licenses/>.
|
|
*
|
|
*/
|
|
|
|
#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
|