[Scummvm-cvs-logs] scummvm master -> 91317c3630f9bff164d8c747ef886b52a85e3a9b
clone2727
clone2727 at gmail.com
Fri Dec 14 00:50:49 CET 2012
This automated email contains information about 30 new commits which have been
pushed to the 'scummvm' repo located at https://github.com/scummvm/scummvm .
Summary:
bbf0bbcd64 COMMON: Allow for parsing QuickTime MIDI files
ac4c8cd335 AUDIO: Add parsing of the QuickTime MIDI sample description
c5ab2fc95f AUDIO: Clean up note request list reading
834ca0e045 AUDIO: Allow for parsing QuickTime 'Tune' files
cc309f4d16 AUDIO: Read in all QuickTime MIDI track data
c22f76dbbc AUDIO: Fill in _tracks from MidiParser_QT
7e2f7099c8 AUDIO: Keep track of the QuickTime MIDI time scale
c01dfba093 AUDIO: Begin basic playback of QuickTime MIDI files
f333e63397 AUDIO: Fix QuickTime MIDI end of track
342cc027c4 AUDIO: Fix QuickTime MIDI tempo
c882ef9dab AUDIO: Make MidiParser_QT::loadMusic() detect the file type
c50d40b7bf AUDIO: Add some documentation to MidiParser_QT
8259d3cd9e Merge remote branch 'upstream/master' into qtmidi
2cb301337a AUDIO: Fix QuickTime MIDI pitch bend
9780c8e56e GROOVIE: Add StuffIt archive code for 11H Mac
72a1140d57 GROOVIE: Rename MusicPlayerMac to MusicPlayerMac_t7g
4a458236f6 COMMON: Make QuickTimeParser::readSampleDesc take the desc size
cfe6a2b640 AUDIO: Fix QuickTime MIDI with extra info in the header
c023651cb3 AUDIO: Implement QuickTime MIDI channel remapping
bb45b24f88 AUDIO: Implement simple dynamic QuickTime MIDI channel remapping
7d684d1166 GROOVIE: Add a MusicPlayerMac_v2 for 11H Mac
1d98435d34 GROOVIE: Add detection for 11H Mac
77ef097723 GROOVIE: Load the 11H Mac installer file when present
b996a6a270 SAGA: Add support for IHNM Mac music
ca6fdb0807 AUDIO: Ignore QT MIDI control change 0
b285db4db3 AUDIO: Cleanup MidiParser_QT a bit
a396e18097 AUDIO: Finish comment
f65b229234 GROOVIE: Fix some indentation in StuffIt
17f9235325 AUDIO: Add some general documentation on MidiParser_QT
91317c3630 Merge pull request #293 from clone2727/qtmidi
Commit: bbf0bbcd646873bf89516ec13fc8c671531e3c00
https://github.com/scummvm/scummvm/commit/bbf0bbcd646873bf89516ec13fc8c671531e3c00
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-05T19:57:10-07:00
Commit Message:
COMMON: Allow for parsing QuickTime MIDI files
Changed paths:
common/quicktime.cpp
common/quicktime.h
diff --git a/common/quicktime.cpp b/common/quicktime.cpp
index 173d3c6..5ac9bee 100644
--- a/common/quicktime.cpp
+++ b/common/quicktime.cpp
@@ -165,6 +165,8 @@ void QuickTimeParser::initParseTable() {
{ &QuickTimeParser::readWAVE, MKTAG('w', 'a', 'v', 'e') },
{ &QuickTimeParser::readESDS, MKTAG('e', 's', 'd', 's') },
{ &QuickTimeParser::readSMI, MKTAG('S', 'M', 'I', ' ') },
+ { &QuickTimeParser::readDefault, MKTAG('g', 'm', 'h', 'd') },
+ { &QuickTimeParser::readLeaf, MKTAG('g', 'm', 'i', 'n') },
{ 0, 0 }
};
@@ -477,6 +479,8 @@ int QuickTimeParser::readHDLR(Atom atom) {
track->codecType = CODEC_TYPE_VIDEO;
else if (type == MKTAG('s', 'o', 'u', 'n'))
track->codecType = CODEC_TYPE_AUDIO;
+ else if (type == MKTAG('m', 'u', 's', 'i'))
+ track->codecType = CODEC_TYPE_MIDI;
_fd->readUint32BE(); // component manufacture
_fd->readUint32BE(); // component flags
diff --git a/common/quicktime.h b/common/quicktime.h
index 08ca35a..5a02fc8 100644
--- a/common/quicktime.h
+++ b/common/quicktime.h
@@ -120,7 +120,8 @@ protected:
enum CodecType {
CODEC_TYPE_MOV_OTHER,
CODEC_TYPE_VIDEO,
- CODEC_TYPE_AUDIO
+ CODEC_TYPE_AUDIO,
+ CODEC_TYPE_MIDI
};
struct Track {
Commit: ac4c8cd335cf1118481453961c8705a01983ebc6
https://github.com/scummvm/scummvm/commit/ac4c8cd335cf1118481453961c8705a01983ebc6
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-05T20:04:29-07:00
Commit Message:
AUDIO: Add parsing of the QuickTime MIDI sample description
Changed paths:
A audio/midiparser_qt.cpp
audio/module.mk
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
new file mode 100644
index 0000000..ca0bb85
--- /dev/null
+++ b/audio/midiparser_qt.cpp
@@ -0,0 +1,127 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/midiparser.h"
+#include "common/debug.h"
+#include "common/quicktime.h"
+
+class MidiParser_QT : /* public MidiParser, */ public Common::QuickTimeParser {
+public:
+ MidiParser_QT() {}
+ ~MidiParser_QT() {}
+
+protected:
+ SampleDesc *readSampleDesc(Track *track, uint32 format);
+
+private:
+ struct NoteRequestInfo {
+ byte flags;
+ byte reserved;
+ uint16 polyphony;
+ Common::Rational typicalPolyphony;
+ };
+
+ struct ToneDescription {
+ uint32 synthesizerType;
+ Common::String synthesizerName;
+ Common::String instrumentName;
+ uint32 instrumentNumber;
+ uint32 gmNumber;
+ };
+
+ struct NoteRequest {
+ uint16 part;
+ NoteRequestInfo info;
+ ToneDescription tone;
+ };
+
+ class MIDISampleDesc : public SampleDesc {
+ public:
+ MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
+ ~MIDISampleDesc() {}
+
+ Common::Array<NoteRequest> _noteRequests;
+ };
+
+ Common::String readString31();
+ Common::Rational readFixed();
+};
+
+Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
+ if (track->codecType == CODEC_TYPE_MIDI) {
+ debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
+
+ /* uint32 flags = */ _fd->readUint32BE(); // always 0
+
+ MIDISampleDesc *entry = new MIDISampleDesc(track, format);
+
+ for (;;) {
+ uint32 event = _fd->readUint32BE();
+
+ if ((event & 0xF000FFFF) != 0xF0000017) // note request event
+ break;
+
+ NoteRequest request;
+ request.part = (event >> 16) & 0xFFF;
+ request.info.flags = _fd->readByte();
+ request.info.reserved = _fd->readByte();
+ request.info.polyphony = _fd->readUint16BE();
+ request.info.typicalPolyphony = readFixed();
+ request.tone.synthesizerType = _fd->readUint32BE();
+ request.tone.synthesizerName = readString31();
+ request.tone.instrumentName = readString31();
+ request.tone.instrumentNumber = _fd->readUint32BE();
+ request.tone.gmNumber = _fd->readUint32BE();
+
+ if (_fd->readUint32BE() != 0xC0010017) // general event note request
+ error("Invalid instrument end event");
+
+ entry->_noteRequests.push_back(request);
+ }
+
+ return entry;
+ }
+
+ return 0;
+}
+
+MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) :
+ Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
+}
+
+Common::String MidiParser_QT::readString31() {
+ byte size = _fd->readByte();
+ assert(size < 32);
+
+ Common::String string;
+ for (byte i = 0; i < size; i++)
+ string += (char)_fd->readByte();
+
+ _fd->skip(31 - size);
+ return string;
+}
+
+Common::Rational MidiParser_QT::readFixed() {
+ int16 integerPart = _fd->readSint16BE();
+ uint16 fractionalPart = _fd->readUint16BE();
+ return integerPart + Common::Rational(fractionalPart, 0x10000);
+}
diff --git a/audio/module.mk b/audio/module.mk
index e3aa0aa..4e1c031 100644
--- a/audio/module.mk
+++ b/audio/module.mk
@@ -4,6 +4,7 @@ MODULE_OBJS := \
audiostream.o \
fmopl.o \
mididrv.o \
+ midiparser_qt.o \
midiparser_smf.o \
midiparser_xmidi.o \
midiparser.o \
Commit: c5ab2fc95f61d7cce1d3d255ed9c991a020d72ac
https://github.com/scummvm/scummvm/commit/c5ab2fc95f61d7cce1d3d255ed9c991a020d72ac
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-05T20:20:12-07:00
Commit Message:
AUDIO: Clean up note request list reading
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index ca0bb85..0d42e6c 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -54,50 +54,27 @@ private:
ToneDescription tone;
};
+ typedef Common::Array<NoteRequest> NoteRequestList;
+
class MIDISampleDesc : public SampleDesc {
public:
MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
~MIDISampleDesc() {}
- Common::Array<NoteRequest> _noteRequests;
+ NoteRequestList _noteRequests;
};
Common::String readString31();
Common::Rational readFixed();
+ NoteRequestList readNoteRequestList();
};
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
if (track->codecType == CODEC_TYPE_MIDI) {
debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
- /* uint32 flags = */ _fd->readUint32BE(); // always 0
-
MIDISampleDesc *entry = new MIDISampleDesc(track, format);
-
- for (;;) {
- uint32 event = _fd->readUint32BE();
-
- if ((event & 0xF000FFFF) != 0xF0000017) // note request event
- break;
-
- NoteRequest request;
- request.part = (event >> 16) & 0xFFF;
- request.info.flags = _fd->readByte();
- request.info.reserved = _fd->readByte();
- request.info.polyphony = _fd->readUint16BE();
- request.info.typicalPolyphony = readFixed();
- request.tone.synthesizerType = _fd->readUint32BE();
- request.tone.synthesizerName = readString31();
- request.tone.instrumentName = readString31();
- request.tone.instrumentNumber = _fd->readUint32BE();
- request.tone.gmNumber = _fd->readUint32BE();
-
- if (_fd->readUint32BE() != 0xC0010017) // general event note request
- error("Invalid instrument end event");
-
- entry->_noteRequests.push_back(request);
- }
-
+ entry->_noteRequests = readNoteRequestList();
return entry;
}
@@ -125,3 +102,37 @@ Common::Rational MidiParser_QT::readFixed() {
uint16 fractionalPart = _fd->readUint16BE();
return integerPart + Common::Rational(fractionalPart, 0x10000);
}
+
+MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList() {
+ NoteRequestList requests;
+
+ /* uint32 flags = */ _fd->readUint32BE(); // always 0
+
+ for (;;) {
+ uint32 event = _fd->readUint32BE();
+
+ if (event == 0x60000000) // marker event
+ break;
+ else if ((event & 0xF000FFFF) != 0xF0000017) // note request event
+ error("Invalid note request event");
+
+ NoteRequest request;
+ request.part = (event >> 16) & 0xFFF;
+ request.info.flags = _fd->readByte();
+ request.info.reserved = _fd->readByte();
+ request.info.polyphony = _fd->readUint16BE();
+ request.info.typicalPolyphony = readFixed();
+ request.tone.synthesizerType = _fd->readUint32BE();
+ request.tone.synthesizerName = readString31();
+ request.tone.instrumentName = readString31();
+ request.tone.instrumentNumber = _fd->readUint32BE();
+ request.tone.gmNumber = _fd->readUint32BE();
+
+ if (_fd->readUint32BE() != 0xC0010017) // general event note request
+ error("Invalid instrument end event");
+
+ requests.push_back(request);
+ }
+
+ return requests;
+}
Commit: 834ca0e04561c42d82f7bd1e5c645ded2a13b038
https://github.com/scummvm/scummvm/commit/834ca0e04561c42d82f7bd1e5c645ded2a13b038
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-06T06:38:13-07:00
Commit Message:
AUDIO: Allow for parsing QuickTime 'Tune' files
Changed paths:
A audio/midiparser_qt.h
audio/midiparser.h
audio/midiparser_qt.cpp
diff --git a/audio/midiparser.h b/audio/midiparser.h
index c935969..f26da45 100644
--- a/audio/midiparser.h
+++ b/audio/midiparser.h
@@ -394,6 +394,7 @@ public:
static MidiParser *createParser_SMF();
static MidiParser *createParser_XMIDI(XMidiCallbackProc proc = defaultXMidiCallback, void *refCon = 0);
+ static MidiParser *createParser_QT();
static void timerCallback(void *data) { ((MidiParser *) data)->onTimer(); }
};
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 0d42e6c..ed4bb22 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -20,61 +20,76 @@
*
*/
-#include "audio/midiparser.h"
+#include "audio/midiparser_qt.h"
#include "common/debug.h"
-#include "common/quicktime.h"
-
-class MidiParser_QT : /* public MidiParser, */ public Common::QuickTimeParser {
-public:
- MidiParser_QT() {}
- ~MidiParser_QT() {}
-
-protected:
- SampleDesc *readSampleDesc(Track *track, uint32 format);
-
-private:
- struct NoteRequestInfo {
- byte flags;
- byte reserved;
- uint16 polyphony;
- Common::Rational typicalPolyphony;
- };
-
- struct ToneDescription {
- uint32 synthesizerType;
- Common::String synthesizerName;
- Common::String instrumentName;
- uint32 instrumentNumber;
- uint32 gmNumber;
- };
-
- struct NoteRequest {
- uint16 part;
- NoteRequestInfo info;
- ToneDescription tone;
- };
-
- typedef Common::Array<NoteRequest> NoteRequestList;
-
- class MIDISampleDesc : public SampleDesc {
- public:
- MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
- ~MIDISampleDesc() {}
-
- NoteRequestList _noteRequests;
- };
-
- Common::String readString31();
- Common::Rational readFixed();
- NoteRequestList readNoteRequestList();
-};
+#include "common/memstream.h"
+
+bool MidiParser_QT::loadMusic(byte *data, uint32 size) {
+ // Assume that this is a Tune and not a QuickTime container
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(data, size, DisposeAfterUse::NO);
+
+ if (!loadFromTune(stream)) {
+ delete stream;
+ return false;
+ }
+
+ return true;
+}
+
+void MidiParser_QT::unloadMusic() {
+ MidiParser::unloadMusic();
+ close();
+ // TODO
+}
+
+bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ unloadMusic();
+
+ // a tune starts off with a sample description
+ stream->readUint32BE(); // header size
+
+ if (stream->readUint32BE() != MKTAG('m', 'u', 's', 'i'))
+ return false;
+
+ stream->readUint32BE(); // reserved
+ stream->readUint16BE(); // reserved
+ stream->readUint16BE(); // index
+
+ // TODO
+ readNoteRequestList(stream);
+ return true;
+}
+
+bool MidiParser_QT::loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ unloadMusic();
+
+ if (!parseStream(stream, disposeAfterUse))
+ return false;
+
+ initFromContainerTracks();
+ return true;
+}
+
+bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
+ unloadMusic();
+
+ if (!parseFile(fileName))
+ return false;
+
+ initFromContainerTracks();
+ return true;
+}
+
+void MidiParser_QT::parseNextEvent(EventInfo &info) {
+ // TODO
+}
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
if (track->codecType == CODEC_TYPE_MIDI) {
debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
MIDISampleDesc *entry = new MIDISampleDesc(track, format);
- entry->_noteRequests = readNoteRequestList();
+ entry->_noteRequests = readNoteRequestList(_fd);
return entry;
}
@@ -85,31 +100,31 @@ MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *pa
Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
}
-Common::String MidiParser_QT::readString31() {
- byte size = _fd->readByte();
+Common::String MidiParser_QT::readString31(Common::SeekableReadStream *stream) {
+ byte size = stream->readByte();
assert(size < 32);
Common::String string;
for (byte i = 0; i < size; i++)
- string += (char)_fd->readByte();
+ string += (char)stream->readByte();
- _fd->skip(31 - size);
+ stream->skip(31 - size);
return string;
}
-Common::Rational MidiParser_QT::readFixed() {
- int16 integerPart = _fd->readSint16BE();
- uint16 fractionalPart = _fd->readUint16BE();
+Common::Rational MidiParser_QT::readFixed(Common::SeekableReadStream *stream) {
+ int16 integerPart = stream->readSint16BE();
+ uint16 fractionalPart = stream->readUint16BE();
return integerPart + Common::Rational(fractionalPart, 0x10000);
}
-MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList() {
+MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList(Common::SeekableReadStream *stream) {
NoteRequestList requests;
- /* uint32 flags = */ _fd->readUint32BE(); // always 0
+ /* uint32 flags = */ stream->readUint32BE(); // always 0
for (;;) {
- uint32 event = _fd->readUint32BE();
+ uint32 event = stream->readUint32BE();
if (event == 0x60000000) // marker event
break;
@@ -118,17 +133,17 @@ MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList() {
NoteRequest request;
request.part = (event >> 16) & 0xFFF;
- request.info.flags = _fd->readByte();
- request.info.reserved = _fd->readByte();
- request.info.polyphony = _fd->readUint16BE();
- request.info.typicalPolyphony = readFixed();
- request.tone.synthesizerType = _fd->readUint32BE();
- request.tone.synthesizerName = readString31();
- request.tone.instrumentName = readString31();
- request.tone.instrumentNumber = _fd->readUint32BE();
- request.tone.gmNumber = _fd->readUint32BE();
-
- if (_fd->readUint32BE() != 0xC0010017) // general event note request
+ request.info.flags = stream->readByte();
+ request.info.reserved = stream->readByte();
+ request.info.polyphony = stream->readUint16BE();
+ request.info.typicalPolyphony = readFixed(stream);
+ request.tone.synthesizerType = stream->readUint32BE();
+ request.tone.synthesizerName = readString31(stream);
+ request.tone.instrumentName = readString31(stream);
+ request.tone.instrumentNumber = stream->readUint32BE();
+ request.tone.gmNumber = stream->readUint32BE();
+
+ if (stream->readUint32BE() != 0xC0010017) // general event note request
error("Invalid instrument end event");
requests.push_back(request);
@@ -136,3 +151,11 @@ MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList() {
return requests;
}
+
+void MidiParser_QT::initFromContainerTracks() {
+ // TODO
+}
+
+MidiParser *MidiParser::createParser_QT() {
+ return new MidiParser_QT();
+}
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
new file mode 100644
index 0000000..34abe4c
--- /dev/null
+++ b/audio/midiparser_qt.h
@@ -0,0 +1,90 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef AUDIO_MIDIPARSER_QT_H
+#define AUDIO_MIDIPARSER_QT_H
+
+#include "audio/midiparser.h"
+#include "common/array.h"
+#include "common/quicktime.h"
+
+class MidiParser_QT : public MidiParser, public Common::QuickTimeParser {
+public:
+ MidiParser_QT() {}
+ ~MidiParser_QT() {}
+
+ // MidiParser
+ bool loadMusic(byte *data, uint32 size);
+ void unloadMusic();
+
+ // Custom
+ bool loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+ bool loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+ bool loadFromContainerFile(const Common::String &fileName);
+
+protected:
+ // MidiParser
+ void parseNextEvent(EventInfo &info);
+
+ // QuickTimeParser
+ SampleDesc *readSampleDesc(Track *track, uint32 format);
+
+private:
+ struct NoteRequestInfo {
+ byte flags;
+ byte reserved;
+ uint16 polyphony;
+ Common::Rational typicalPolyphony;
+ };
+
+ struct ToneDescription {
+ uint32 synthesizerType;
+ Common::String synthesizerName;
+ Common::String instrumentName;
+ uint32 instrumentNumber;
+ uint32 gmNumber;
+ };
+
+ struct NoteRequest {
+ uint16 part;
+ NoteRequestInfo info;
+ ToneDescription tone;
+ };
+
+ typedef Common::Array<NoteRequest> NoteRequestList;
+
+ class MIDISampleDesc : public SampleDesc {
+ public:
+ MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
+ ~MIDISampleDesc() {}
+
+ NoteRequestList _noteRequests;
+ };
+
+ Common::String readString31(Common::SeekableReadStream *stream);
+ Common::Rational readFixed(Common::SeekableReadStream *stream);
+ NoteRequestList readNoteRequestList(Common::SeekableReadStream *stream);
+
+ void initFromContainerTracks();
+};
+
+#endif
Commit: cc309f4d166ec74e06541145550cc86125f0247c
https://github.com/scummvm/scummvm/commit/cc309f4d166ec74e06541145550cc86125f0247c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-06T11:23:15-07:00
Commit Message:
AUDIO: Read in all QuickTime MIDI track data
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index ed4bb22..0701f7e 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -39,7 +39,12 @@ bool MidiParser_QT::loadMusic(byte *data, uint32 size) {
void MidiParser_QT::unloadMusic() {
MidiParser::unloadMusic();
close();
- // TODO
+
+ // Unlike those lesser formats, we *do* hold track data
+ for (uint i = 0; i < _trackInfo.size(); i++)
+ free(_trackInfo[i].data);
+
+ _trackInfo.clear();
}
bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
@@ -55,8 +60,18 @@ bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfte
stream->readUint16BE(); // reserved
stream->readUint16BE(); // index
- // TODO
- readNoteRequestList(stream);
+ MIDITrackInfo trackInfo;
+ trackInfo.noteRequests = readNoteRequestList(stream);
+
+ uint32 trackSize = stream->size() - stream->pos();
+ assert(trackSize > 0);
+
+ trackInfo.data = (byte *)malloc(trackSize);
+ stream->read(trackInfo.data, trackSize);
+
+ _trackInfo.push_back(trackInfo);
+
+ initCommon();
return true;
}
@@ -153,9 +168,62 @@ MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList(Common::Seekab
}
void MidiParser_QT::initFromContainerTracks() {
+ const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
+
+ for (uint32 i = 0; i < tracks.size(); i++) {
+ if (tracks[i]->codecType == CODEC_TYPE_MIDI) {
+ assert(tracks[i]->sampleDescs.size() == 1);
+
+ if (tracks[i]->editCount != 1)
+ warning("Unhandled QuickTime MIDI edit lists, things may go awry");
+
+ MIDISampleDesc *entry = (MIDISampleDesc *)tracks[i]->sampleDescs[0];
+
+ MIDITrackInfo trackInfo;
+ trackInfo.noteRequests = entry->_noteRequests;
+ trackInfo.data = readWholeTrack(tracks[i]);
+ _trackInfo.push_back(trackInfo);
+ }
+ }
+
+ initCommon();
+}
+
+void MidiParser_QT::initCommon() {
+ // Now we have all our info needed in _trackInfo from whatever container
+ // form, we can fill in the MidiParser tracks.
+
// TODO
}
+byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
+ // This just goes through all chunks and
+
+ Common::MemoryWriteStreamDynamic output;
+ uint32 curSample = 0;
+
+ for (uint i = 0; i < track->chunkCount; i++) {
+ _fd->seek(track->chunkOffsets[i]);
+
+ uint32 sampleCount = 0;
+
+ for (uint32 j = 0; j < track->sampleToChunkCount; j++)
+ if (i >= track->sampleToChunk[j].first)
+ sampleCount = track->sampleToChunk[j].count;
+
+ for (uint32 j = 0; j < sampleCount; j++, curSample++) {
+ uint32 size = (track->sampleSize != 0) ? track->sampleSize : track->sampleSizes[curSample];
+
+ byte *data = new byte[size];
+ _fd->read(data, size);
+ output.write(data, size);
+ delete[] data;
+ }
+ }
+
+ return output.getData();
+}
+
MidiParser *MidiParser::createParser_QT() {
return new MidiParser_QT();
}
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 34abe4c..0047803 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -72,6 +72,11 @@ private:
typedef Common::Array<NoteRequest> NoteRequestList;
+ struct MIDITrackInfo {
+ NoteRequestList noteRequests;
+ byte *data;
+ };
+
class MIDISampleDesc : public SampleDesc {
public:
MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
@@ -84,7 +89,12 @@ private:
Common::Rational readFixed(Common::SeekableReadStream *stream);
NoteRequestList readNoteRequestList(Common::SeekableReadStream *stream);
+ byte *readWholeTrack(Common::QuickTimeParser::Track *track);
+
+ Common::Array<MIDITrackInfo> _trackInfo;
+
void initFromContainerTracks();
+ void initCommon();
};
#endif
Commit: c22f76dbbc487b5a5e89d327bec2c550ce47378c
https://github.com/scummvm/scummvm/commit/c22f76dbbc487b5a5e89d327bec2c550ce47378c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-06T13:27:06-07:00
Commit Message:
AUDIO: Fill in _tracks from MidiParser_QT
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 0701f7e..8ddf6d6 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -193,7 +193,10 @@ void MidiParser_QT::initCommon() {
// Now we have all our info needed in _trackInfo from whatever container
// form, we can fill in the MidiParser tracks.
- // TODO
+ _num_tracks = _trackInfo.size();
+
+ for (uint32 i = 0; i < _trackInfo.size(); i++)
+ MidiParser::_tracks[i] = _trackInfo[i].data;
}
byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
Commit: 7e2f7099c892c212409f1c7b69426cd1a1f00552
https://github.com/scummvm/scummvm/commit/7e2f7099c892c212409f1c7b69426cd1a1f00552
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T06:49:33-07:00
Commit Message:
AUDIO: Keep track of the QuickTime MIDI time scale
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 8ddf6d6..7b20f62 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -69,6 +69,7 @@ bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfte
trackInfo.data = (byte *)malloc(trackSize);
stream->read(trackInfo.data, trackSize);
+ trackInfo.timeScale = 600; // the default
_trackInfo.push_back(trackInfo);
initCommon();
@@ -182,6 +183,7 @@ void MidiParser_QT::initFromContainerTracks() {
MIDITrackInfo trackInfo;
trackInfo.noteRequests = entry->_noteRequests;
trackInfo.data = readWholeTrack(tracks[i]);
+ trackInfo.timeScale = tracks[i]->timeScale;
_trackInfo.push_back(trackInfo);
}
}
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 0047803..6e4ded8 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -75,6 +75,7 @@ private:
struct MIDITrackInfo {
NoteRequestList noteRequests;
byte *data;
+ uint32 timeScale;
};
class MIDISampleDesc : public SampleDesc {
Commit: c01dfba093f0a2506c912f644dbb44df7b877d92
https://github.com/scummvm/scummvm/commit/c01dfba093f0a2506c912f644dbb44df7b877d92
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T08:48:22-07:00
Commit Message:
AUDIO: Begin basic playback of QuickTime MIDI files
Sounds almost correct, but too fast
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 7b20f62..b7a201d 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -96,8 +96,103 @@ bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
return true;
}
+void MidiParser_QT::resetTracking() {
+ _loadedInstruments = 0;
+}
+
void MidiParser_QT::parseNextEvent(EventInfo &info) {
- // TODO
+ if (_loadedInstruments < _trackInfo[_active_track].noteRequests.size()) {
+ // Load instruments first
+ info.event = 0xC0 | _loadedInstruments;
+ info.basic.param1 = _trackInfo[_active_track].noteRequests[_loadedInstruments].tone.gmNumber;
+ _loadedInstruments++;
+ return;
+ }
+
+ info.delta = readNextEvent(info);
+}
+
+uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
+ uint32 control = readUint32();
+
+ switch (control >> 28) {
+ case 0x0:
+ case 0x1:
+ // Rest
+ // We handle this by recursively adding up all the rests into the
+ // next event's delta
+ return readNextEvent(info) + (control & 0xFFFFFF);
+ case 0x2:
+ case 0x3:
+ // Note event
+ info.event = 0x90 | ((control >> 24) & 0x1F);
+ info.basic.param1 = ((control >> 18) & 0x3F) + 32;
+ info.basic.param2 = (control >> 11) & 0x7F;
+ info.length = (info.basic.param2 == 0) ? 0 : (control & 0x7FF);
+ break;
+ case 0x4:
+ case 0x5:
+ // Controller
+ info.event = 0xB0 | ((control >> 24) & 0x1F);
+ info.basic.param1 = (control >> 16) & 0xFF;
+ info.basic.param2 = (control >> 8) & 0xFF;
+ break;
+ case 0x6:
+ case 0x7:
+ // Marker
+ switch ((control >> 16) & 0xFF) {
+ case 0:
+ // End
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ break;
+ case 1:
+ // Beat
+ warning("Encountered beat marker");
+ break;
+ case 2:
+ // Tempo
+ warning("Encountered tempo marker");
+ break;
+ default:
+ warning("Unknown marker");
+ }
+ break;
+ case 0x9: {
+ // Extended note event
+ uint32 extra = readUint32();
+ info.event = 0x90 | ((control >> 16) & 0xFFF);
+ info.basic.param1 = (control >> 8) & 0xFF;
+ info.basic.param2 = (extra >> 22) & 0x7F;
+ info.length = (info.basic.param2 == 0) ? 0 : (extra & 0x3FFFFF);
+ break;
+ }
+ case 0xA: {
+ // Extended controller
+ uint32 extra = readUint32();
+ info.event = 0xB0 | ((control >> 16) & 0xFFF);
+ info.basic.param1 = (extra >> 16) & 0x3FFF;
+ info.basic.param2 = (extra >> 8) & 0xFF; // ???
+ break;
+ }
+ case 0xB:
+ // Knob
+ error("Encountered knob event in QuickTime MIDI");
+ break;
+ case 0x8:
+ case 0xC:
+ case 0xD:
+ case 0xE:
+ // Reserved
+ readUint32();
+ break;
+ case 0xF:
+ // General
+ error("Encountered general event in QuickTime MIDI");
+ break;
+ }
+
+ return 0;
}
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
@@ -196,9 +291,15 @@ void MidiParser_QT::initCommon() {
// form, we can fill in the MidiParser tracks.
_num_tracks = _trackInfo.size();
+ assert(_num_tracks > 0);
for (uint32 i = 0; i < _trackInfo.size(); i++)
MidiParser::_tracks[i] = _trackInfo[i].data;
+
+ _ppqn = _trackInfo[0].timeScale;
+ resetTracking();
+ setTempo(500000);
+ setTrack(0);
}
byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
@@ -229,6 +330,12 @@ byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
return output.getData();
}
+uint32 MidiParser_QT::readUint32() {
+ uint32 value = READ_BE_UINT32(_position._play_pos);
+ _position._play_pos += 4;
+ return value;
+}
+
MidiParser *MidiParser::createParser_QT() {
return new MidiParser_QT();
}
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 6e4ded8..2dcd61b 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -43,6 +43,7 @@ public:
protected:
// MidiParser
+ void resetTracking();
void parseNextEvent(EventInfo &info);
// QuickTimeParser
@@ -86,6 +87,8 @@ private:
NoteRequestList _noteRequests;
};
+ uint32 readNextEvent(EventInfo &info);
+
Common::String readString31(Common::SeekableReadStream *stream);
Common::Rational readFixed(Common::SeekableReadStream *stream);
NoteRequestList readNoteRequestList(Common::SeekableReadStream *stream);
@@ -93,9 +96,11 @@ private:
byte *readWholeTrack(Common::QuickTimeParser::Track *track);
Common::Array<MIDITrackInfo> _trackInfo;
+ uint32 _loadedInstruments;
void initFromContainerTracks();
void initCommon();
+ uint32 readUint32();
};
#endif
Commit: f333e633977a3dd17ce272edb5eb697e62c1af19
https://github.com/scummvm/scummvm/commit/f333e633977a3dd17ce272edb5eb697e62c1af19
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T11:33:39-07:00
Commit Message:
AUDIO: Fix QuickTime MIDI end of track
The end marker is used for editing only
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index b7a201d..f8113af 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -63,11 +63,11 @@ bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfte
MIDITrackInfo trackInfo;
trackInfo.noteRequests = readNoteRequestList(stream);
- uint32 trackSize = stream->size() - stream->pos();
- assert(trackSize > 0);
+ trackInfo.size = stream->size() - stream->pos();
+ assert(trackInfo.size > 0);
- trackInfo.data = (byte *)malloc(trackSize);
- stream->read(trackInfo.data, trackSize);
+ trackInfo.data = (byte *)malloc(trackInfo.size);
+ stream->read(trackInfo.data, trackInfo.size);
trackInfo.timeScale = 600; // the default
_trackInfo.push_back(trackInfo);
@@ -101,6 +101,13 @@ void MidiParser_QT::resetTracking() {
}
void MidiParser_QT::parseNextEvent(EventInfo &info) {
+ if (_position._play_pos >= _trackInfo[_active_track].data + _trackInfo[_active_track].size) {
+ // Manually insert end of track when we reach the end
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ return;
+ }
+
if (_loadedInstruments < _trackInfo[_active_track].noteRequests.size()) {
// Load instruments first
info.event = 0xC0 | _loadedInstruments;
@@ -140,23 +147,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0x6:
case 0x7:
// Marker
- switch ((control >> 16) & 0xFF) {
- case 0:
- // End
- info.event = 0xFF;
- info.ext.type = 0x2F;
- break;
- case 1:
- // Beat
- warning("Encountered beat marker");
- break;
- case 2:
- // Tempo
- warning("Encountered tempo marker");
- break;
- default:
- warning("Unknown marker");
- }
+ // Used for editing only, so we don't need to care about this
break;
case 0x9: {
// Extended note event
@@ -277,7 +268,7 @@ void MidiParser_QT::initFromContainerTracks() {
MIDITrackInfo trackInfo;
trackInfo.noteRequests = entry->_noteRequests;
- trackInfo.data = readWholeTrack(tracks[i]);
+ trackInfo.data = readWholeTrack(tracks[i], trackInfo.size);
trackInfo.timeScale = tracks[i]->timeScale;
_trackInfo.push_back(trackInfo);
}
@@ -302,7 +293,7 @@ void MidiParser_QT::initCommon() {
setTrack(0);
}
-byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
+byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize) {
// This just goes through all chunks and
Common::MemoryWriteStreamDynamic output;
@@ -327,6 +318,7 @@ byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track) {
}
}
+ trackSize = output.size();
return output.getData();
}
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 2dcd61b..5023402 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -76,6 +76,7 @@ private:
struct MIDITrackInfo {
NoteRequestList noteRequests;
byte *data;
+ uint32 size;
uint32 timeScale;
};
@@ -93,7 +94,7 @@ private:
Common::Rational readFixed(Common::SeekableReadStream *stream);
NoteRequestList readNoteRequestList(Common::SeekableReadStream *stream);
- byte *readWholeTrack(Common::QuickTimeParser::Track *track);
+ byte *readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize);
Common::Array<MIDITrackInfo> _trackInfo;
uint32 _loadedInstruments;
Commit: 342cc027c4d360590753a129f0c622fc1757cc0e
https://github.com/scummvm/scummvm/commit/342cc027c4d360590753a129f0c622fc1757cc0e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T11:41:54-07:00
Commit Message:
AUDIO: Fix QuickTime MIDI tempo
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index f8113af..5500c28 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -289,7 +289,7 @@ void MidiParser_QT::initCommon() {
_ppqn = _trackInfo[0].timeScale;
resetTracking();
- setTempo(500000);
+ setTempo(1000000);
setTrack(0);
}
Commit: c882ef9dabbb69e569bd5861712cf4117794a9ae
https://github.com/scummvm/scummvm/commit/c882ef9dabbb69e569bd5861712cf4117794a9ae
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T11:47:30-07:00
Commit Message:
AUDIO: Make MidiParser_QT::loadMusic() detect the file type
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 5500c28..d644050 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -25,10 +25,19 @@
#include "common/memstream.h"
bool MidiParser_QT::loadMusic(byte *data, uint32 size) {
- // Assume that this is a Tune and not a QuickTime container
+ if (size < 8)
+ return false;
+
Common::SeekableReadStream *stream = new Common::MemoryReadStream(data, size, DisposeAfterUse::NO);
- if (!loadFromTune(stream)) {
+ // Attempt to detect what format we have
+ bool result;
+ if (READ_BE_UINT32(data + 4) == MKTAG('m', 'u', 's', 'i'))
+ result = loadFromTune(stream);
+ else
+ result = loadFromContainerStream(stream);
+
+ if (!result) {
delete stream;
return false;
}
Commit: c50d40b7bf0914c5a1a5da221cee146ab4aa2f63
https://github.com/scummvm/scummvm/commit/c50d40b7bf0914c5a1a5da221cee146ab4aa2f63
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T11:50:21-07:00
Commit Message:
AUDIO: Add some documentation to MidiParser_QT
Changed paths:
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 5023402..ac5f45a 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -27,6 +27,9 @@
#include "common/array.h"
#include "common/quicktime.h"
+/**
+ * The QuickTime MIDI version of MidiParser.
+ */
class MidiParser_QT : public MidiParser, public Common::QuickTimeParser {
public:
MidiParser_QT() {}
@@ -36,9 +39,19 @@ public:
bool loadMusic(byte *data, uint32 size);
void unloadMusic();
- // Custom
+ /**
+ * Load the MIDI from a 'Tune' resource
+ */
bool loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+
+ /**
+ * Load the MIDI from a QuickTime stream
+ */
bool loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse = DisposeAfterUse::YES);
+
+ /**
+ * Load the MIDI from a QuickTime file
+ */
bool loadFromContainerFile(const Common::String &fileName);
protected:
Commit: 8259d3cd9e68288dc622302fe84a924d6f94b00c
https://github.com/scummvm/scummvm/commit/8259d3cd9e68288dc622302fe84a924d6f94b00c
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-07T16:03:01-07:00
Commit Message:
Merge remote branch 'upstream/master' into qtmidi
Changed paths:
R engines/wintermute/readme.txt
AUTHORS
audio/midiparser.cpp
audio/midiparser.h
audio/midiparser_qt.cpp
audio/midiparser_smf.cpp
audio/midiparser_xmidi.cpp
audio/softsynth/mt32/TVA.cpp
backends/platform/maemo/debian/rules
devtools/create_project/config.h
devtools/create_project/create_project.cpp
devtools/create_project/create_project.h
devtools/create_project/msbuild.cpp
devtools/create_project/msvc.cpp
devtools/create_project/msvc.h
devtools/create_project/scripts/postbuild.cmd
devtools/create_project/visualstudio.cpp
devtools/credits.pl
dists/scummvm.rc
engines/agos/midiparser_s1d.cpp
engines/cine/anim.cpp
engines/cine/cine.cpp
engines/cine/cine.h
engines/cine/console.cpp
engines/cine/gfx.cpp
engines/cine/gfx.h
engines/cine/main_loop.cpp
engines/cine/object.cpp
engines/cine/pal.cpp
engines/cine/part.cpp
engines/cine/saveload.cpp
engines/cine/saveload.h
engines/cine/script_fw.cpp
engines/cine/sound.cpp
engines/cine/texte.cpp
engines/cine/texte.h
engines/cine/various.cpp
engines/parallaction/sound_br.cpp
engines/sci/sound/midiparser_sci.cpp
engines/sci/sound/midiparser_sci.h
engines/scumm/midiparser_ro.cpp
engines/tony/custom.cpp
engines/tony/custom.h
engines/tony/font.cpp
engines/tony/game.cpp
engines/tony/gfxengine.cpp
engines/tony/mpal/mpal.cpp
gui/credits.h
video/bink_decoder.cpp
video/bink_decoder.h
diff --cc audio/midiparser_qt.cpp
index d644050,0000000..2b8a02b
mode 100644,000000..100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@@ -1,342 -1,0 +1,342 @@@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#include "audio/midiparser_qt.h"
+#include "common/debug.h"
+#include "common/memstream.h"
+
+bool MidiParser_QT::loadMusic(byte *data, uint32 size) {
+ if (size < 8)
+ return false;
+
+ Common::SeekableReadStream *stream = new Common::MemoryReadStream(data, size, DisposeAfterUse::NO);
+
+ // Attempt to detect what format we have
+ bool result;
+ if (READ_BE_UINT32(data + 4) == MKTAG('m', 'u', 's', 'i'))
+ result = loadFromTune(stream);
+ else
+ result = loadFromContainerStream(stream);
+
+ if (!result) {
+ delete stream;
+ return false;
+ }
+
+ return true;
+}
+
+void MidiParser_QT::unloadMusic() {
+ MidiParser::unloadMusic();
+ close();
+
+ // Unlike those lesser formats, we *do* hold track data
+ for (uint i = 0; i < _trackInfo.size(); i++)
+ free(_trackInfo[i].data);
+
+ _trackInfo.clear();
+}
+
+bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ unloadMusic();
+
+ // a tune starts off with a sample description
+ stream->readUint32BE(); // header size
+
+ if (stream->readUint32BE() != MKTAG('m', 'u', 's', 'i'))
+ return false;
+
+ stream->readUint32BE(); // reserved
+ stream->readUint16BE(); // reserved
+ stream->readUint16BE(); // index
+
+ MIDITrackInfo trackInfo;
+ trackInfo.noteRequests = readNoteRequestList(stream);
+
+ trackInfo.size = stream->size() - stream->pos();
+ assert(trackInfo.size > 0);
+
+ trackInfo.data = (byte *)malloc(trackInfo.size);
+ stream->read(trackInfo.data, trackInfo.size);
+
+ trackInfo.timeScale = 600; // the default
+ _trackInfo.push_back(trackInfo);
+
+ initCommon();
+ return true;
+}
+
+bool MidiParser_QT::loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
+ unloadMusic();
+
+ if (!parseStream(stream, disposeAfterUse))
+ return false;
+
+ initFromContainerTracks();
+ return true;
+}
+
+bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
+ unloadMusic();
+
+ if (!parseFile(fileName))
+ return false;
+
+ initFromContainerTracks();
+ return true;
+}
+
+void MidiParser_QT::resetTracking() {
+ _loadedInstruments = 0;
+}
+
+void MidiParser_QT::parseNextEvent(EventInfo &info) {
- if (_position._play_pos >= _trackInfo[_active_track].data + _trackInfo[_active_track].size) {
++ if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
+ // Manually insert end of track when we reach the end
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ return;
+ }
+
- if (_loadedInstruments < _trackInfo[_active_track].noteRequests.size()) {
++ if (_loadedInstruments < _trackInfo[_activeTrack].noteRequests.size()) {
+ // Load instruments first
+ info.event = 0xC0 | _loadedInstruments;
- info.basic.param1 = _trackInfo[_active_track].noteRequests[_loadedInstruments].tone.gmNumber;
++ info.basic.param1 = _trackInfo[_activeTrack].noteRequests[_loadedInstruments].tone.gmNumber;
+ _loadedInstruments++;
+ return;
+ }
+
+ info.delta = readNextEvent(info);
+}
+
+uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
+ uint32 control = readUint32();
+
+ switch (control >> 28) {
+ case 0x0:
+ case 0x1:
+ // Rest
+ // We handle this by recursively adding up all the rests into the
+ // next event's delta
+ return readNextEvent(info) + (control & 0xFFFFFF);
+ case 0x2:
+ case 0x3:
+ // Note event
+ info.event = 0x90 | ((control >> 24) & 0x1F);
+ info.basic.param1 = ((control >> 18) & 0x3F) + 32;
+ info.basic.param2 = (control >> 11) & 0x7F;
+ info.length = (info.basic.param2 == 0) ? 0 : (control & 0x7FF);
+ break;
+ case 0x4:
+ case 0x5:
+ // Controller
+ info.event = 0xB0 | ((control >> 24) & 0x1F);
+ info.basic.param1 = (control >> 16) & 0xFF;
+ info.basic.param2 = (control >> 8) & 0xFF;
+ break;
+ case 0x6:
+ case 0x7:
+ // Marker
+ // Used for editing only, so we don't need to care about this
+ break;
+ case 0x9: {
+ // Extended note event
+ uint32 extra = readUint32();
+ info.event = 0x90 | ((control >> 16) & 0xFFF);
+ info.basic.param1 = (control >> 8) & 0xFF;
+ info.basic.param2 = (extra >> 22) & 0x7F;
+ info.length = (info.basic.param2 == 0) ? 0 : (extra & 0x3FFFFF);
+ break;
+ }
+ case 0xA: {
+ // Extended controller
+ uint32 extra = readUint32();
+ info.event = 0xB0 | ((control >> 16) & 0xFFF);
+ info.basic.param1 = (extra >> 16) & 0x3FFF;
+ info.basic.param2 = (extra >> 8) & 0xFF; // ???
+ break;
+ }
+ case 0xB:
+ // Knob
+ error("Encountered knob event in QuickTime MIDI");
+ break;
+ case 0x8:
+ case 0xC:
+ case 0xD:
+ case 0xE:
+ // Reserved
+ readUint32();
+ break;
+ case 0xF:
+ // General
+ error("Encountered general event in QuickTime MIDI");
+ break;
+ }
+
+ return 0;
+}
+
+Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
+ if (track->codecType == CODEC_TYPE_MIDI) {
+ debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
+
+ MIDISampleDesc *entry = new MIDISampleDesc(track, format);
+ entry->_noteRequests = readNoteRequestList(_fd);
+ return entry;
+ }
+
+ return 0;
+}
+
+MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) :
+ Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
+}
+
+Common::String MidiParser_QT::readString31(Common::SeekableReadStream *stream) {
+ byte size = stream->readByte();
+ assert(size < 32);
+
+ Common::String string;
+ for (byte i = 0; i < size; i++)
+ string += (char)stream->readByte();
+
+ stream->skip(31 - size);
+ return string;
+}
+
+Common::Rational MidiParser_QT::readFixed(Common::SeekableReadStream *stream) {
+ int16 integerPart = stream->readSint16BE();
+ uint16 fractionalPart = stream->readUint16BE();
+ return integerPart + Common::Rational(fractionalPart, 0x10000);
+}
+
+MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList(Common::SeekableReadStream *stream) {
+ NoteRequestList requests;
+
+ /* uint32 flags = */ stream->readUint32BE(); // always 0
+
+ for (;;) {
+ uint32 event = stream->readUint32BE();
+
+ if (event == 0x60000000) // marker event
+ break;
+ else if ((event & 0xF000FFFF) != 0xF0000017) // note request event
+ error("Invalid note request event");
+
+ NoteRequest request;
+ request.part = (event >> 16) & 0xFFF;
+ request.info.flags = stream->readByte();
+ request.info.reserved = stream->readByte();
+ request.info.polyphony = stream->readUint16BE();
+ request.info.typicalPolyphony = readFixed(stream);
+ request.tone.synthesizerType = stream->readUint32BE();
+ request.tone.synthesizerName = readString31(stream);
+ request.tone.instrumentName = readString31(stream);
+ request.tone.instrumentNumber = stream->readUint32BE();
+ request.tone.gmNumber = stream->readUint32BE();
+
+ if (stream->readUint32BE() != 0xC0010017) // general event note request
+ error("Invalid instrument end event");
+
+ requests.push_back(request);
+ }
+
+ return requests;
+}
+
+void MidiParser_QT::initFromContainerTracks() {
+ const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
+
+ for (uint32 i = 0; i < tracks.size(); i++) {
+ if (tracks[i]->codecType == CODEC_TYPE_MIDI) {
+ assert(tracks[i]->sampleDescs.size() == 1);
+
+ if (tracks[i]->editCount != 1)
+ warning("Unhandled QuickTime MIDI edit lists, things may go awry");
+
+ MIDISampleDesc *entry = (MIDISampleDesc *)tracks[i]->sampleDescs[0];
+
+ MIDITrackInfo trackInfo;
+ trackInfo.noteRequests = entry->_noteRequests;
+ trackInfo.data = readWholeTrack(tracks[i], trackInfo.size);
+ trackInfo.timeScale = tracks[i]->timeScale;
+ _trackInfo.push_back(trackInfo);
+ }
+ }
+
+ initCommon();
+}
+
+void MidiParser_QT::initCommon() {
+ // Now we have all our info needed in _trackInfo from whatever container
+ // form, we can fill in the MidiParser tracks.
+
- _num_tracks = _trackInfo.size();
- assert(_num_tracks > 0);
++ _numTracks = _trackInfo.size();
++ assert(_numTracks > 0);
+
+ for (uint32 i = 0; i < _trackInfo.size(); i++)
+ MidiParser::_tracks[i] = _trackInfo[i].data;
+
+ _ppqn = _trackInfo[0].timeScale;
+ resetTracking();
+ setTempo(1000000);
+ setTrack(0);
+}
+
+byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize) {
+ // This just goes through all chunks and
+
+ Common::MemoryWriteStreamDynamic output;
+ uint32 curSample = 0;
+
+ for (uint i = 0; i < track->chunkCount; i++) {
+ _fd->seek(track->chunkOffsets[i]);
+
+ uint32 sampleCount = 0;
+
+ for (uint32 j = 0; j < track->sampleToChunkCount; j++)
+ if (i >= track->sampleToChunk[j].first)
+ sampleCount = track->sampleToChunk[j].count;
+
+ for (uint32 j = 0; j < sampleCount; j++, curSample++) {
+ uint32 size = (track->sampleSize != 0) ? track->sampleSize : track->sampleSizes[curSample];
+
+ byte *data = new byte[size];
+ _fd->read(data, size);
+ output.write(data, size);
+ delete[] data;
+ }
+ }
+
+ trackSize = output.size();
+ return output.getData();
+}
+
+uint32 MidiParser_QT::readUint32() {
- uint32 value = READ_BE_UINT32(_position._play_pos);
- _position._play_pos += 4;
++ uint32 value = READ_BE_UINT32(_position._playPos);
++ _position._playPos += 4;
+ return value;
+}
+
+MidiParser *MidiParser::createParser_QT() {
+ return new MidiParser_QT();
+}
Commit: 2cb301337a32b09449fe64d43fa1031af3d09454
https://github.com/scummvm/scummvm/commit/2cb301337a32b09449fe64d43fa1031af3d09454
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-08T08:05:47-07:00
Commit Message:
AUDIO: Fix QuickTime MIDI pitch bend
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 2b8a02b..e16c73b 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -149,9 +149,31 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0x4:
case 0x5:
// Controller
- info.event = 0xB0 | ((control >> 24) & 0x1F);
- info.basic.param1 = (control >> 16) & 0xFF;
- info.basic.param2 = (control >> 8) & 0xFF;
+ if (((control >> 16) & 0xFF) == 32) {
+ // Pitch bend
+ info.event = 0xE0 | ((control >> 24) & 0x1F);
+
+ // Actually an 8.8 fixed point number
+ int16 value = (int16)(control & 0xFFFF);
+
+ if (value < -0x200 || value > 0x1FF) {
+ warning("QuickTime MIDI pitch bend value (%d) out of range, clipping", value);
+ value = CLIP<int16>(value, -0x200, 0x1FF);
+ }
+
+ // Now convert the value to 'normal' MIDI values
+ value += 0x200;
+ value *= 16;
+
+ // param1 holds the low 7 bits, param2 holds the high 7 bits
+ info.basic.param1 = value & 0x7F;
+ info.basic.param2 = value >> 7;
+ } else {
+ // Regular controller
+ info.event = 0xB0 | ((control >> 24) & 0x1F);
+ info.basic.param1 = (control >> 16) & 0xFF;
+ info.basic.param2 = (control >> 8) & 0xFF;
+ }
break;
case 0x6:
case 0x7:
Commit: 9780c8e56e01308b1b4c5e97079c0320bb308845
https://github.com/scummvm/scummvm/commit/9780c8e56e01308b1b4c5e97079c0320bb308845
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-09T09:32:34-07:00
Commit Message:
GROOVIE: Add StuffIt archive code for 11H Mac
Changed paths:
A engines/groovie/stuffit.cpp
A engines/groovie/stuffit.h
engines/groovie/module.mk
diff --git a/engines/groovie/module.mk b/engines/groovie/module.mk
index 1e89ff6..b47eed9 100644
--- a/engines/groovie/module.mk
+++ b/engines/groovie/module.mk
@@ -15,6 +15,7 @@ MODULE_OBJS := \
roq.o \
saveload.o \
script.o \
+ stuffit.o \
vdx.o
# This module can be built as a plugin
diff --git a/engines/groovie/stuffit.cpp b/engines/groovie/stuffit.cpp
new file mode 100644
index 0000000..03fa300
--- /dev/null
+++ b/engines/groovie/stuffit.cpp
@@ -0,0 +1,537 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+// Based on the StuffIt code in ResidualVM
+// StuffIt parsing based on http://code.google.com/p/theunarchiver/wiki/StuffItFormat
+// Compression 14 based on libxad (http://sourceforge.net/projects/libxad/)
+
+#include "groovie/stuffit.h"
+
+#include "common/archive.h"
+#include "common/bitstream.h"
+#include "common/debug.h"
+#include "common/hash-str.h"
+#include "common/hashmap.h"
+#include "common/memstream.h"
+#include "common/substream.h"
+
+namespace Groovie {
+
+struct SIT14Data;
+
+class StuffItArchive : public Common::Archive {
+public:
+ StuffItArchive();
+ ~StuffItArchive();
+
+ bool open(const Common::String &filename);
+ void close();
+ bool isOpen() const { return _stream != 0; }
+
+ // Common::Archive API implementation
+ bool hasFile(const Common::String &name) const;
+ int listMembers(Common::ArchiveMemberList &list) const;
+ const Common::ArchiveMemberPtr getMember(const Common::String &name) const;
+ Common::SeekableReadStream *createReadStreamForMember(const Common::String &name) const;
+
+private:
+ struct FileEntry {
+ byte compression;
+ uint32 uncompressedSize;
+ uint32 compressedSize;
+ uint32 offset;
+ };
+
+ Common::SeekableReadStream *_stream;
+
+ typedef Common::HashMap<Common::String, FileEntry, Common::IgnoreCase_Hash, Common::IgnoreCase_EqualTo> FileMap;
+ FileMap _map;
+
+ // Decompression Functions
+ Common::SeekableReadStream *decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const;
+
+ // Decompression Helpers
+ void update14(uint16 first, uint16 last, byte *code, uint16 *freq) const;
+ void readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const;
+};
+
+StuffItArchive::StuffItArchive() : Common::Archive() {
+ _stream = 0;
+}
+
+StuffItArchive::~StuffItArchive() {
+ close();
+}
+
+// Some known values of StuffIt FourCC's
+// 11H Mac in particular uses ST46
+static const uint32 s_magicNumbers[] = {
+ MKTAG('S', 'I', 'T', '!'), MKTAG('S', 'T', '6', '5'), MKTAG('S', 'T', '5', '0'),
+ MKTAG('S', 'T', '6', '0'), MKTAG('S', 'T', 'i', 'n'), MKTAG('S', 'T', 'i', '2'),
+ MKTAG('S', 'T', 'i', '3'), MKTAG('S', 'T', 'i', '4'), MKTAG('S', 'T', '4', '6')
+};
+
+bool StuffItArchive::open(const Common::String &filename) {
+ close();
+
+ _stream = SearchMan.createReadStreamForMember(filename);
+
+ if (!_stream)
+ return false;
+
+ uint32 tag = _stream->readUint32BE();
+
+ // Check all the possible FourCC's
+ bool found = false;
+ for (int i = 0; i < ARRAYSIZE(s_magicNumbers); i++) {
+ if (tag == s_magicNumbers[i]) {
+ found = true;
+ break;
+ }
+ }
+
+ // Didn't find one, let's bail out
+ if (!found) {
+ close();
+ return false;
+ }
+
+ /* uint16 fileCount = */ _stream->readUint16BE();
+ /* uint32 archiveSize = */ _stream->readUint32BE();
+
+ // Some sort of second magic number
+ if (_stream->readUint32BE() != MKTAG('r', 'L', 'a', 'u')) {
+ close();
+ return false;
+ }
+
+ /* byte version = */ _stream->readByte(); // meaning not clear
+
+ _stream->skip(7); // unknown
+
+ while (_stream->pos() < _stream->size() && !_stream->eos()) {
+ byte resForkCompression = _stream->readByte();
+ byte dataForkCompression = _stream->readByte();
+
+ byte fileNameLength = _stream->readByte();
+ Common::String name;
+
+ for (byte i = 0; i < fileNameLength; i++)
+ name += (char)_stream->readByte();
+
+ // Skip remaining bytes
+ _stream->skip(63 - fileNameLength);
+
+ /* uint32 fileType = */ _stream->readUint32BE();
+ /* uint32 fileCreator = */ _stream->readUint32BE();
+ /* uint16 finderFlags = */ _stream->readUint16BE();
+ /* uint32 creationDate = */ _stream->readUint32BE();
+ /* uint32 modificationDate = */ _stream->readUint32BE();
+ uint32 resForkUncompressedSize = _stream->readUint32BE();
+ uint32 dataForkUncompressedSize = _stream->readUint32BE();
+ uint32 resForkCompressedSize = _stream->readUint32BE();
+ uint32 dataForkCompressedSize = _stream->readUint32BE();
+ /* uint16 resForkCRC = */ _stream->readUint16BE();
+ /* uint16 dataForkCRC = */ _stream->readUint16BE();
+ _stream->skip(6); // unknown
+ /* uint16 headerCRC = */ _stream->readUint16BE();
+
+ // Ignore directories for now
+ if (dataForkCompression == 32 || dataForkCompression == 33)
+ continue;
+
+ if (dataForkUncompressedSize != 0) {
+ // We have a data fork
+
+ FileEntry entry;
+ entry.compression = dataForkCompression;
+ entry.uncompressedSize = dataForkUncompressedSize;
+ entry.compressedSize = dataForkCompressedSize;
+ entry.offset = _stream->pos() + resForkCompressedSize;
+ _map[name] = entry;
+
+ debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+ }
+
+ if (resForkUncompressedSize != 0) {
+ // We have a resource fork
+
+ // Add a .rsrc extension so we know it's the resource fork
+ name += ".rsrc";
+
+ FileEntry entry;
+ entry.compression = resForkCompression;
+ entry.uncompressedSize = resForkUncompressedSize;
+ entry.compressedSize = resForkCompressedSize;
+ entry.offset = _stream->pos();
+ _map[name] = entry;
+
+ debug(0, "StuffIt file '%s', Compression = %d", name.c_str(), entry.compression);
+ }
+
+ // Go to the next entry
+ _stream->skip(dataForkCompressedSize + resForkCompressedSize);
+ }
+
+ return true;
+}
+
+void StuffItArchive::close() {
+ delete _stream; _stream = 0;
+ _map.clear();
+}
+
+bool StuffItArchive::hasFile(const Common::String &name) const {
+ return _map.contains(name);
+}
+
+int StuffItArchive::listMembers(Common::ArchiveMemberList &list) const {
+ for (FileMap::const_iterator it = _map.begin(); it != _map.end(); it++)
+ list.push_back(getMember(it->_key));
+
+ return _map.size();
+}
+
+const Common::ArchiveMemberPtr StuffItArchive::getMember(const Common::String &name) const {
+ return Common::ArchiveMemberPtr(new Common::GenericArchiveMember(name, this));
+}
+
+Common::SeekableReadStream *StuffItArchive::createReadStreamForMember(const Common::String &name) const {
+ if (!_stream || !_map.contains(name))
+ return 0;
+
+ const FileEntry &entry = _map[name];
+
+ if (entry.compression & 0xF0)
+ error("Unhandled StuffIt encryption");
+
+ Common::SeekableSubReadStream subStream(_stream, entry.offset, entry.offset + entry.compressedSize);
+
+ // We currently only support type 14 compression
+ switch (entry.compression) {
+ case 0: // Uncompressed
+ return subStream.readStream(subStream.size());
+ case 14: // Installer
+ return decompress14(&subStream, entry.uncompressedSize);
+ default:
+ error("Unhandled StuffIt compression %d", entry.compression);
+ }
+
+ return 0;
+}
+
+void StuffItArchive::update14(uint16 first, uint16 last, byte *code, uint16 *freq) const {
+ uint16 i, j;
+
+ while (last - first > 1) {
+ i = first;
+ j = last;
+
+ do {
+ while (++i < last && code[first] > code[i])
+ ;
+
+ while (--j > first && code[first] < code[j])
+ ;
+
+ if (j > i) {
+ SWAP(code[i], code[j]);
+ SWAP(freq[i], freq[j]);
+ }
+ } while (j > i);
+
+ if (first != j) {
+ SWAP(code[first], code[j]);
+ SWAP(freq[first], freq[j]);
+
+ i = j + 1;
+
+ if (last - i <= j - first) {
+ update14(i, last, code, freq);
+ last = j;
+ } else {
+ update14(first, j, code, freq);
+ first = i;
+ }
+ } else {
+ ++first;
+ }
+ }
+}
+
+struct SIT14Data {
+ byte code[308];
+ byte codecopy[308];
+ uint16 freq[308];
+ uint32 buff[308];
+
+ byte var1[52];
+ uint16 var2[52];
+ uint16 var3[75 * 2];
+
+ byte var4[76];
+ uint32 var5[75];
+ byte var6[1024];
+ uint16 var7[308 * 2];
+ byte var8[0x4000];
+
+ byte window[0x40000];
+};
+
+// Realign to a byte boundary
+#define ALIGN_BITS(b) \
+ if (b->pos() & 7) \
+ b->skip(8 - (b->pos() & 7))
+
+void StuffItArchive::readTree14(Common::BitStream *bits, SIT14Data *dat, uint16 codesize, uint16 *result) const {
+ uint32 i, l, n;
+ uint32 k = bits->getBit();
+ uint32 j = bits->getBits(2) + 2;
+ uint32 o = bits->getBits(3) + 1;
+ uint32 size = 1 << j;
+ uint32 m = size - 1;
+ k = k ? (m - 1) : 0xFFFFFFFF;
+
+ if (bits->getBits(2) & 1) { // skip 1 bit!
+ // requirements for this call: dat->buff[32], dat->code[32], dat->freq[32*2]
+ readTree14(bits, dat, size, dat->freq);
+
+ for (i = 0; i < codesize; ) {
+ l = 0;
+
+ do {
+ l = dat->freq[l + bits->getBit()];
+ n = size << 1;
+ } while (n > l);
+
+ l -= n;
+
+ if (k != l) {
+ if (l == m) {
+ l = 0;
+
+ do {
+ l = dat->freq[l + bits->getBit()];
+ n = size << 1;
+ } while (n > l);
+
+ l += 3 - n;
+
+ while (l--) {
+ dat->code[i] = dat->code[i - 1];
+ ++i;
+ }
+ } else {
+ dat->code[i++] = l + o;
+ }
+ } else {
+ dat->code[i++] = 0;
+ }
+ }
+ } else {
+ for (i = 0; i < codesize; ) {
+ l = bits->getBits(j);
+
+ if (k != l) {
+ if (l == m) {
+ l = bits->getBits(j) + 3;
+
+ while (l--) {
+ dat->code[i] = dat->code[i - 1];
+ ++i;
+ }
+ } else {
+ dat->code[i++] = l+o;
+ }
+ } else {
+ dat->code[i++] = 0;
+ }
+ }
+ }
+
+ for (i = 0; i < codesize; ++i) {
+ dat->codecopy[i] = dat->code[i];
+ dat->freq[i] = i;
+ }
+
+ update14(0, codesize, dat->codecopy, dat->freq);
+
+ for (i = 0; i < codesize && !dat->codecopy[i]; ++i)
+ ; // find first nonempty
+
+ for (j = 0; i < codesize; ++i, ++j) {
+ if (i)
+ j <<= (dat->codecopy[i] - dat->codecopy[i - 1]);
+
+ k = dat->codecopy[i]; m = 0;
+
+ for (l = j; k--; l >>= 1)
+ m = (m << 1) | (l & 1);
+
+ dat->buff[dat->freq[i]] = m;
+ }
+
+ for (i = 0; i < (uint32)codesize * 2; ++i)
+ result[i] = 0;
+
+ j = 2;
+
+ for (i = 0; i < codesize; ++i) {
+ l = 0;
+ m = dat->buff[i];
+
+ for (k = 0; k < dat->code[i]; ++k) {
+ l += (m & 1);
+
+ if (dat->code[i] - 1 <= (int32)k) {
+ result[l] = codesize * 2 + i;
+ } else {
+ if (!result[l]) {
+ result[l] = j;
+ j += 2;
+ }
+
+ l = result[l];
+ }
+
+ m >>= 1;
+ }
+ }
+
+ ALIGN_BITS(bits);
+}
+
+#define OUTPUT_VAL(x) \
+ out.writeByte(x); \
+ dat->window[j++] = x; \
+ j &= 0x3FFFF
+
+Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStream *src, uint32 uncompressedSize) const {
+ byte *dst = (byte *)malloc(uncompressedSize);
+ Common::MemoryWriteStream out(dst, uncompressedSize);
+
+ Common::BitStream *bits = new Common::BitStream8LSB(src);
+
+ uint32 i, j, k, l, m, n;
+
+ SIT14Data *dat = new SIT14Data();
+
+ // initialization
+ for (i = k = 0; i < 52; ++i) {
+ dat->var2[i] = k;
+ k += (1 << (dat->var1[i] = ((i >= 4) ? ((i - 4) >> 2) : 0)));
+ }
+
+ for (i = 0; i < 4; ++i)
+ dat->var8[i] = i;
+
+ for (m = 1, l = 4; i < 0x4000; m <<= 1) // i is 4
+ for (n = l+4; l < n; ++l)
+ for (j = 0; j < m; ++j)
+ dat->var8[i++] = l;
+
+ for (i = 0, k = 1; i < 75; ++i) {
+ dat->var5[i] = k;
+ k += (1 << (dat->var4[i] = (i >= 3 ? ((i - 3) >> 2) : 0)));
+ }
+
+ for (i = 0; i < 4; ++i)
+ dat->var6[i] = i - 1;
+
+ for (m = 1, l = 3; i < 0x400; m <<= 1) // i is 4
+ for (n = l + 4; l < n; ++l)
+ for (j = 0; j < m; ++j)
+ dat->var6[i++] = l;
+
+ m = bits->getBits(16); // number of blocks
+ j = 0; // window position
+
+ while (m-- && !bits->eos()) {
+ bits->getBits(16); // skip crunched block size
+ bits->getBits(16);
+ n = bits->getBits(16); // number of uncrunched bytes
+ n |= bits->getBits(16) << 16;
+ readTree14(bits, dat, 308, dat->var7);
+ readTree14(bits, dat, 75, dat->var3);
+
+ while (n && !bits->eos()) {
+ for (i = 0; i < 616;)
+ i = dat->var7[i + bits->getBit()];
+
+ i -= 616;
+
+ if (i < 0x100) {
+ OUTPUT_VAL(i);
+ --n;
+ } else {
+ i -= 0x100;
+ k = dat->var2[i]+4;
+ i = dat->var1[i];
+
+ if (i)
+ k += bits->getBits(i);
+
+ for (i = 0; i < 150;)
+ i = dat->var3[i + bits->getBit()];
+
+ i -= 150;
+ l = dat->var5[i];
+ i = dat->var4[i];
+
+ if (i)
+ l += bits->getBits(i);
+
+ n -= k;
+ l = j + 0x40000 - l;
+
+ while (k--) {
+ l &= 0x3FFFF;
+ OUTPUT_VAL(dat->window[l]);
+ l++;
+ }
+ }
+ }
+
+ ALIGN_BITS(bits);
+ }
+
+ delete dat;
+ delete bits;
+
+ return new Common::MemoryReadStream(dst, uncompressedSize, DisposeAfterUse::YES);
+}
+
+#undef OUTPUT_VAL
+#undef ALIGN_BITS
+
+Common::Archive *createStuffItArchive(const Common::String &fileName) {
+ StuffItArchive *archive = new StuffItArchive();
+
+ if (!archive->open(fileName)) {
+ delete archive;
+ return 0;
+ }
+
+ return archive;
+}
+
+} // End of namespace Groovie
diff --git a/engines/groovie/stuffit.h b/engines/groovie/stuffit.h
new file mode 100644
index 0000000..44f593d
--- /dev/null
+++ b/engines/groovie/stuffit.h
@@ -0,0 +1,43 @@
+/* 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 2
+ * 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ */
+
+#ifndef GROOVIE_STUFFIT_H
+#define GROOVIE_STUFFIT_H
+
+namespace Common {
+class Archive;
+class String;
+}
+
+namespace Groovie {
+
+/**
+ * This factory method creates an Archive instance corresponding to the content
+ * of the StuffIt compressed file.
+ *
+ * May return 0 in case of a failure.
+ */
+Common::Archive *createStuffItArchive(const Common::String &fileName);
+
+} // End of namespace Groovie
+
+#endif
Commit: 72a1140d572ce46a820cddb6ac85b748cb7a066d
https://github.com/scummvm/scummvm/commit/72a1140d572ce46a820cddb6ac85b748cb7a066d
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-09T09:43:29-07:00
Commit Message:
GROOVIE: Rename MusicPlayerMac to MusicPlayerMac_t7g
Changed paths:
engines/groovie/groovie.cpp
engines/groovie/music.cpp
engines/groovie/music.h
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 726e7cb..42501af 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -163,7 +163,7 @@ Common::Error GroovieEngine::run() {
// TODO: The 11th Hour Mac uses QuickTime MIDI files
// Right now, since the XMIDI are present and it is still detected as
// the DOS version, we don't have to do anything here.
- _musicPlayer = new MusicPlayerMac(this);
+ _musicPlayer = new MusicPlayerMac_t7g(this);
break;
case Common::kPlatformIOS:
_musicPlayer = new MusicPlayerIOS(this);
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index af929d4..b4afca5 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -678,9 +678,9 @@ void MusicPlayerXMI::setTimbreMT(byte channel, const Timbre &timbre) {
}
-// MusicPlayerMac
+// MusicPlayerMac_t7g
-MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
+MusicPlayerMac_t7g::MusicPlayerMac_t7g(GroovieEngine *vm) : MusicPlayerMidi(vm) {
// Create the parser
_midiParser = MidiParser::createParser_SMF();
@@ -701,7 +701,7 @@ MusicPlayerMac::MusicPlayerMac(GroovieEngine *vm) : MusicPlayerMidi(vm) {
assert(_vm->_macResFork);
}
-bool MusicPlayerMac::load(uint32 fileref, bool loop) {
+bool MusicPlayerMac_t7g::load(uint32 fileref, bool loop) {
debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref);
// First try for compressed MIDI
@@ -722,7 +722,7 @@ bool MusicPlayerMac::load(uint32 fileref, bool loop) {
return loadParser(file, loop);
}
-Common::SeekableReadStream *MusicPlayerMac::decompressMidi(Common::SeekableReadStream *stream) {
+Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableReadStream *stream) {
// Initialize an output buffer of the given size
uint32 size = stream->readUint32BE();
byte *output = (byte *)malloc(size);
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index cc852aa..0342bf1 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -150,9 +150,9 @@ private:
void setTimbreMT(byte channel, const Timbre &timbre);
};
-class MusicPlayerMac : public MusicPlayerMidi {
+class MusicPlayerMac_t7g : public MusicPlayerMidi {
public:
- MusicPlayerMac(GroovieEngine *vm);
+ MusicPlayerMac_t7g(GroovieEngine *vm);
protected:
bool load(uint32 fileref, bool loop);
Commit: 4a458236f625d28706cfe4560690770a395ee6e3
https://github.com/scummvm/scummvm/commit/4a458236f625d28706cfe4560690770a395ee6e3
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-09T10:47:40-07:00
Commit Message:
COMMON: Make QuickTimeParser::readSampleDesc take the desc size
Changed paths:
audio/decoders/quicktime.cpp
audio/decoders/quicktime_intern.h
audio/midiparser_qt.cpp
audio/midiparser_qt.h
common/quicktime.cpp
common/quicktime.h
video/qt_decoder.cpp
video/qt_decoder.h
diff --git a/audio/decoders/quicktime.cpp b/audio/decoders/quicktime.cpp
index 5276cfc..3ec9007 100644
--- a/audio/decoders/quicktime.cpp
+++ b/audio/decoders/quicktime.cpp
@@ -134,7 +134,7 @@ void QuickTimeAudioDecoder::init() {
_audioTracks.push_back(new QuickTimeAudioTrack(this, _tracks[i]));
}
-Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format) {
+Common::QuickTimeParser::SampleDesc *QuickTimeAudioDecoder::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_AUDIO) {
debug(0, "Audio Codec FourCC: \'%s\'", tag2str(format));
diff --git a/audio/decoders/quicktime_intern.h b/audio/decoders/quicktime_intern.h
index efc97cb..7e2b0d1 100644
--- a/audio/decoders/quicktime_intern.h
+++ b/audio/decoders/quicktime_intern.h
@@ -131,7 +131,7 @@ protected:
};
// Common::QuickTimeParser API
- virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format);
+ virtual Common::QuickTimeParser::SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
void init();
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index e16c73b..65ca64a 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -217,7 +217,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
return 0;
}
-Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format) {
+Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_MIDI) {
debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index ac5f45a..f9378f9 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -60,7 +60,7 @@ protected:
void parseNextEvent(EventInfo &info);
// QuickTimeParser
- SampleDesc *readSampleDesc(Track *track, uint32 format);
+ SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
private:
struct NoteRequestInfo {
diff --git a/common/quicktime.cpp b/common/quicktime.cpp
index 5ac9bee..5b3659b 100644
--- a/common/quicktime.cpp
+++ b/common/quicktime.cpp
@@ -544,7 +544,7 @@ int QuickTimeParser::readSTSD(Atom atom) {
_fd->readUint16BE(); // reserved
_fd->readUint16BE(); // index
- track->sampleDescs[i] = readSampleDesc(track, format);
+ track->sampleDescs[i] = readSampleDesc(track, format, size - 16);
debug(0, "size=%d 4CC= %s codec_type=%d", size, tag2str(format), track->codecType);
diff --git a/common/quicktime.h b/common/quicktime.h
index 5a02fc8..0948a50 100644
--- a/common/quicktime.h
+++ b/common/quicktime.h
@@ -162,7 +162,7 @@ protected:
byte objectTypeMP4;
};
- virtual SampleDesc *readSampleDesc(Track *track, uint32 format) = 0;
+ virtual SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize) = 0;
uint32 _timeScale;
uint32 _duration;
diff --git a/video/qt_decoder.cpp b/video/qt_decoder.cpp
index b4dab9d..a105a96 100644
--- a/video/qt_decoder.cpp
+++ b/video/qt_decoder.cpp
@@ -110,7 +110,7 @@ const Graphics::Surface *QuickTimeDecoder::decodeNextFrame() {
return frame;
}
-Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format) {
+Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_VIDEO) {
debug(0, "Video Codec FourCC: \'%s\'", tag2str(format));
@@ -205,7 +205,7 @@ Common::QuickTimeParser::SampleDesc *QuickTimeDecoder::readSampleDesc(Common::Qu
}
// Pass it on up
- return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format);
+ return Audio::QuickTimeAudioDecoder::readSampleDesc(track, format, descSize);
}
void QuickTimeDecoder::init() {
diff --git a/video/qt_decoder.h b/video/qt_decoder.h
index 71d3371..b973a36 100644
--- a/video/qt_decoder.h
+++ b/video/qt_decoder.h
@@ -69,7 +69,7 @@ public:
Audio::Timestamp getDuration() const { return Audio::Timestamp(0, _duration, _timeScale); }
protected:
- Common::QuickTimeParser::SampleDesc *readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format);
+ Common::QuickTimeParser::SampleDesc *readSampleDesc(Common::QuickTimeParser::Track *track, uint32 format, uint32 descSize);
private:
void init();
Commit: cfe6a2b640e91cf11a32017e79966121bf357cc7
https://github.com/scummvm/scummvm/commit/cfe6a2b640e91cf11a32017e79966121bf357cc7
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-09T11:59:05-07:00
Commit Message:
AUDIO: Fix QuickTime MIDI with extra info in the header
The 11th Hour Mac MIDI's now play
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 65ca64a..5148ed7 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -69,9 +69,9 @@ bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfte
stream->readUint16BE(); // reserved
stream->readUint16BE(); // index
- MIDITrackInfo trackInfo;
- trackInfo.noteRequests = readNoteRequestList(stream);
+ stream->readUint32BE(); // flags, ignore
+ MIDITrackInfo trackInfo;
trackInfo.size = stream->size() - stream->pos();
assert(trackInfo.size > 0);
@@ -105,27 +105,19 @@ bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
return true;
}
-void MidiParser_QT::resetTracking() {
- _loadedInstruments = 0;
-}
-
void MidiParser_QT::parseNextEvent(EventInfo &info) {
- if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
- // Manually insert end of track when we reach the end
- info.event = 0xFF;
- info.ext.type = 0x2F;
- return;
- }
+ info.event = 0;
+
+ while (info.event == 0) {
+ if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
+ // Manually insert end of track when we reach the end
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ return;
+ }
- if (_loadedInstruments < _trackInfo[_activeTrack].noteRequests.size()) {
- // Load instruments first
- info.event = 0xC0 | _loadedInstruments;
- info.basic.param1 = _trackInfo[_activeTrack].noteRequests[_loadedInstruments].tone.gmNumber;
- _loadedInstruments++;
- return;
+ info.delta = readNextEvent(info);
}
-
- info.delta = readNextEvent(info);
}
uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
@@ -210,19 +202,50 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
break;
case 0xF:
// General
- error("Encountered general event in QuickTime MIDI");
+ handleGeneralEvent(info, control);
break;
}
return 0;
}
+void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
+ uint32 part = (control >> 16) & 0xFFF;
+ uint32 dataSize = ((control & 0xFFFF) - 2) * 4;
+ byte subType = READ_BE_UINT16(_position._playPos + dataSize) & 0x3FFF;
+
+ switch (subType) {
+ case 1:
+ // Note Request
+ // Currently we're only using the GM number from the request
+ assert(dataSize == 84);
+ info.event = 0xC0 | part;
+ info.basic.param1 = READ_BE_UINT32(_position._playPos + 80);
+ break;
+ case 5: // Tune Difference
+ case 8: // MIDI Channel
+ case 10: // No-op
+ case 11: // Used Notes
+ // Should be safe to skip these
+ break;
+ default:
+ warning("Unhandled general event %d", subType);
+ }
+
+ _position._playPos += dataSize + 4;
+}
+
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_MIDI) {
debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
+ _fd->readUint32BE(); // flags, ignore
+ descSize -= 4;
+
MIDISampleDesc *entry = new MIDISampleDesc(track, format);
- entry->_noteRequests = readNoteRequestList(_fd);
+ entry->_requestSize = descSize;
+ entry->_requestData = (byte *)malloc(descSize);
+ _fd->read(entry->_requestData, descSize);
return entry;
}
@@ -233,58 +256,6 @@ MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *pa
Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
}
-Common::String MidiParser_QT::readString31(Common::SeekableReadStream *stream) {
- byte size = stream->readByte();
- assert(size < 32);
-
- Common::String string;
- for (byte i = 0; i < size; i++)
- string += (char)stream->readByte();
-
- stream->skip(31 - size);
- return string;
-}
-
-Common::Rational MidiParser_QT::readFixed(Common::SeekableReadStream *stream) {
- int16 integerPart = stream->readSint16BE();
- uint16 fractionalPart = stream->readUint16BE();
- return integerPart + Common::Rational(fractionalPart, 0x10000);
-}
-
-MidiParser_QT::NoteRequestList MidiParser_QT::readNoteRequestList(Common::SeekableReadStream *stream) {
- NoteRequestList requests;
-
- /* uint32 flags = */ stream->readUint32BE(); // always 0
-
- for (;;) {
- uint32 event = stream->readUint32BE();
-
- if (event == 0x60000000) // marker event
- break;
- else if ((event & 0xF000FFFF) != 0xF0000017) // note request event
- error("Invalid note request event");
-
- NoteRequest request;
- request.part = (event >> 16) & 0xFFF;
- request.info.flags = stream->readByte();
- request.info.reserved = stream->readByte();
- request.info.polyphony = stream->readUint16BE();
- request.info.typicalPolyphony = readFixed(stream);
- request.tone.synthesizerType = stream->readUint32BE();
- request.tone.synthesizerName = readString31(stream);
- request.tone.instrumentName = readString31(stream);
- request.tone.instrumentNumber = stream->readUint32BE();
- request.tone.gmNumber = stream->readUint32BE();
-
- if (stream->readUint32BE() != 0xC0010017) // general event note request
- error("Invalid instrument end event");
-
- requests.push_back(request);
- }
-
- return requests;
-}
-
void MidiParser_QT::initFromContainerTracks() {
const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
@@ -295,10 +266,7 @@ void MidiParser_QT::initFromContainerTracks() {
if (tracks[i]->editCount != 1)
warning("Unhandled QuickTime MIDI edit lists, things may go awry");
- MIDISampleDesc *entry = (MIDISampleDesc *)tracks[i]->sampleDescs[0];
-
MIDITrackInfo trackInfo;
- trackInfo.noteRequests = entry->_noteRequests;
trackInfo.data = readWholeTrack(tracks[i], trackInfo.size);
trackInfo.timeScale = tracks[i]->timeScale;
_trackInfo.push_back(trackInfo);
@@ -330,6 +298,10 @@ byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint3
Common::MemoryWriteStreamDynamic output;
uint32 curSample = 0;
+ // Read in the note request data first
+ MIDISampleDesc *entry = (MIDISampleDesc *)track->sampleDescs[0];
+ output.write(entry->_requestData, entry->_requestSize);
+
for (uint i = 0; i < track->chunkCount; i++) {
_fd->seek(track->chunkOffsets[i]);
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index f9378f9..5ab89bd 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -56,38 +56,13 @@ public:
protected:
// MidiParser
- void resetTracking();
void parseNextEvent(EventInfo &info);
// QuickTimeParser
SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
private:
- struct NoteRequestInfo {
- byte flags;
- byte reserved;
- uint16 polyphony;
- Common::Rational typicalPolyphony;
- };
-
- struct ToneDescription {
- uint32 synthesizerType;
- Common::String synthesizerName;
- Common::String instrumentName;
- uint32 instrumentNumber;
- uint32 gmNumber;
- };
-
- struct NoteRequest {
- uint16 part;
- NoteRequestInfo info;
- ToneDescription tone;
- };
-
- typedef Common::Array<NoteRequest> NoteRequestList;
-
struct MIDITrackInfo {
- NoteRequestList noteRequests;
byte *data;
uint32 size;
uint32 timeScale;
@@ -98,19 +73,16 @@ private:
MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
~MIDISampleDesc() {}
- NoteRequestList _noteRequests;
+ byte *_requestData;
+ uint32 _requestSize;
};
uint32 readNextEvent(EventInfo &info);
-
- Common::String readString31(Common::SeekableReadStream *stream);
- Common::Rational readFixed(Common::SeekableReadStream *stream);
- NoteRequestList readNoteRequestList(Common::SeekableReadStream *stream);
+ void handleGeneralEvent(EventInfo &info, uint32 control);
byte *readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize);
Common::Array<MIDITrackInfo> _trackInfo;
- uint32 _loadedInstruments;
void initFromContainerTracks();
void initCommon();
Commit: c023651cb37752a7dd5e53a7bb27f20dbc9fd41e
https://github.com/scummvm/scummvm/commit/c023651cb37752a7dd5e53a7bb27f20dbc9fd41e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-10T05:23:11-07:00
Commit Message:
AUDIO: Implement QuickTime MIDI channel remapping
Needed to support channels with a drum kit
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 5148ed7..5ea94a4 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -133,7 +133,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0x2:
case 0x3:
// Note event
- info.event = 0x90 | ((control >> 24) & 0x1F);
+ info.event = 0x90 | getChannel((control >> 24) & 0x1F);
info.basic.param1 = ((control >> 18) & 0x3F) + 32;
info.basic.param2 = (control >> 11) & 0x7F;
info.length = (info.basic.param2 == 0) ? 0 : (control & 0x7FF);
@@ -143,7 +143,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
// Controller
if (((control >> 16) & 0xFF) == 32) {
// Pitch bend
- info.event = 0xE0 | ((control >> 24) & 0x1F);
+ info.event = 0xE0 | getChannel((control >> 24) & 0x1F);
// Actually an 8.8 fixed point number
int16 value = (int16)(control & 0xFFFF);
@@ -162,7 +162,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
info.basic.param2 = value >> 7;
} else {
// Regular controller
- info.event = 0xB0 | ((control >> 24) & 0x1F);
+ info.event = 0xB0 | getChannel((control >> 24) & 0x1F);
info.basic.param1 = (control >> 16) & 0xFF;
info.basic.param2 = (control >> 8) & 0xFF;
}
@@ -175,7 +175,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0x9: {
// Extended note event
uint32 extra = readUint32();
- info.event = 0x90 | ((control >> 16) & 0xFFF);
+ info.event = 0x90 | getChannel((control >> 16) & 0xFFF);
info.basic.param1 = (control >> 8) & 0xFF;
info.basic.param2 = (extra >> 22) & 0x7F;
info.length = (info.basic.param2 == 0) ? 0 : (extra & 0x3FFFFF);
@@ -184,7 +184,7 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0xA: {
// Extended controller
uint32 extra = readUint32();
- info.event = 0xB0 | ((control >> 16) & 0xFFF);
+ info.event = 0xB0 | getChannel((control >> 16) & 0xFFF);
info.basic.param1 = (extra >> 16) & 0x3FFF;
info.basic.param2 = (extra >> 8) & 0xFF; // ???
break;
@@ -219,7 +219,12 @@ void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
// Note Request
// Currently we're only using the GM number from the request
assert(dataSize == 84);
- info.event = 0xC0 | part;
+
+ // We have to remap channels because GM needs percussion to be on the
+ // percussion channel but QuickTime can have that anywhere.
+ allocateChannel(part, READ_BE_UINT32(_position._playPos + 80));
+
+ info.event = 0xC0 | getChannel(part);
info.basic.param1 = READ_BE_UINT32(_position._playPos + 80);
break;
case 5: // Tune Difference
@@ -235,6 +240,44 @@ void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
_position._playPos += dataSize + 4;
}
+void MidiParser_QT::allocateChannel(uint32 part, uint32 instrument) {
+ if (instrument == 0x4001) {
+ // Drum Kit -> Percussion Channel
+ if (isChannelAllocated(9))
+ warning("Multiple QuickTime MIDI percussion channels");
+
+ _channelMap[part] = 9;
+ } else {
+ // Normal Instrument -> First Free Channel
+ for (uint32 i = 0; i < 16; i++) {
+ // 9 is reserved for Percussion
+ if (i == 9 || isChannelAllocated(i))
+ continue;
+
+ _channelMap[part] = i;
+ return;
+ }
+
+ error("Failed to allocate channel for QuickTime MIDI");
+ }
+}
+
+byte MidiParser_QT::getChannel(uint32 part) const {
+ return _channelMap[part];
+}
+
+bool MidiParser_QT::isChannelAllocated(byte channel) const {
+ for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++)
+ if (it->_value == channel)
+ return true;
+
+ return false;
+}
+
+void MidiParser_QT::resetTracking() {
+ _channelMap.clear();
+}
+
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
if (track->codecType == CODEC_TYPE_MIDI) {
debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 5ab89bd..25c2ae9 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -25,6 +25,7 @@
#include "audio/midiparser.h"
#include "common/array.h"
+#include "common/hashmap.h"
#include "common/quicktime.h"
/**
@@ -57,6 +58,7 @@ public:
protected:
// MidiParser
void parseNextEvent(EventInfo &info);
+ void resetTracking();
// QuickTimeParser
SampleDesc *readSampleDesc(Track *track, uint32 format, uint32 descSize);
@@ -80,10 +82,17 @@ private:
uint32 readNextEvent(EventInfo &info);
void handleGeneralEvent(EventInfo &info, uint32 control);
+ void allocateChannel(uint32 part, uint32 instrument);
+ byte getChannel(uint32 part) const;
+ bool isChannelAllocated(byte channel) const;
+
byte *readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize);
Common::Array<MIDITrackInfo> _trackInfo;
+ typedef Common::HashMap<uint, uint> ChannelMap;
+ ChannelMap _channelMap;
+
void initFromContainerTracks();
void initCommon();
uint32 readUint32();
Commit: bb45b24f8877865e47f793d254371728aa399849
https://github.com/scummvm/scummvm/commit/bb45b24f8877865e47f793d254371728aa399849
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-12T11:36:17-07:00
Commit Message:
AUDIO: Implement simple dynamic QuickTime MIDI channel remapping
Needed for IHNM Mac sounds
Changed paths:
audio/midiparser_qt.cpp
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 5ea94a4..226495b 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -106,21 +106,25 @@ bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
}
void MidiParser_QT::parseNextEvent(EventInfo &info) {
- info.event = 0;
+ uint32 delta = 0;
- while (info.event == 0) {
- if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
- // Manually insert end of track when we reach the end
- info.event = 0xFF;
- info.ext.type = 0x2F;
- return;
- }
+ while (_queuedEvents.empty())
+ delta = readNextEvent();
- info.delta = readNextEvent(info);
- }
+ info = _queuedEvents.pop();
+ info.delta = delta;
}
-uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
+uint32 MidiParser_QT::readNextEvent() {
+ if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
+ // Manually insert end of track when we reach the end
+ EventInfo info;
+ info.event = 0xFF;
+ info.ext.type = 0x2F;
+ _queuedEvents.push(info);
+ return 0;
+ }
+
uint32 control = readUint32();
switch (control >> 28) {
@@ -129,43 +133,16 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
// Rest
// We handle this by recursively adding up all the rests into the
// next event's delta
- return readNextEvent(info) + (control & 0xFFFFFF);
+ return readNextEvent() + (control & 0xFFFFFF);
case 0x2:
case 0x3:
// Note event
- info.event = 0x90 | getChannel((control >> 24) & 0x1F);
- info.basic.param1 = ((control >> 18) & 0x3F) + 32;
- info.basic.param2 = (control >> 11) & 0x7F;
- info.length = (info.basic.param2 == 0) ? 0 : (control & 0x7FF);
+ handleNoteEvent((control >> 24) & 0x1F, ((control >> 18) & 0x3F) + 32, (control >> 11) & 0x7F, control & 0x7FF);
break;
case 0x4:
case 0x5:
// Controller
- if (((control >> 16) & 0xFF) == 32) {
- // Pitch bend
- info.event = 0xE0 | getChannel((control >> 24) & 0x1F);
-
- // Actually an 8.8 fixed point number
- int16 value = (int16)(control & 0xFFFF);
-
- if (value < -0x200 || value > 0x1FF) {
- warning("QuickTime MIDI pitch bend value (%d) out of range, clipping", value);
- value = CLIP<int16>(value, -0x200, 0x1FF);
- }
-
- // Now convert the value to 'normal' MIDI values
- value += 0x200;
- value *= 16;
-
- // param1 holds the low 7 bits, param2 holds the high 7 bits
- info.basic.param1 = value & 0x7F;
- info.basic.param2 = value >> 7;
- } else {
- // Regular controller
- info.event = 0xB0 | getChannel((control >> 24) & 0x1F);
- info.basic.param1 = (control >> 16) & 0xFF;
- info.basic.param2 = (control >> 8) & 0xFF;
- }
+ handleControllerEvent((control >> 16) & 0xFF, (control >> 24) & 0x1F, (control >> 8) & 0xFF, control & 0xFF);
break;
case 0x6:
case 0x7:
@@ -175,18 +152,13 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
case 0x9: {
// Extended note event
uint32 extra = readUint32();
- info.event = 0x90 | getChannel((control >> 16) & 0xFFF);
- info.basic.param1 = (control >> 8) & 0xFF;
- info.basic.param2 = (extra >> 22) & 0x7F;
- info.length = (info.basic.param2 == 0) ? 0 : (extra & 0x3FFFFF);
+ handleNoteEvent((control >> 16) & 0xFFF, (control >> 8) & 0xFF, (extra >> 22) & 0x7F, extra & 0x3FFFFF);
break;
}
case 0xA: {
// Extended controller
uint32 extra = readUint32();
- info.event = 0xB0 | getChannel((control >> 16) & 0xFFF);
- info.basic.param1 = (extra >> 16) & 0x3FFF;
- info.basic.param2 = (extra >> 8) & 0xFF; // ???
+ handleControllerEvent((extra >> 16) & 0x3FFF, (control >> 16) & 0xFFF, (extra >> 8) & 0xFF, extra & 0xFF);
break;
}
case 0xB:
@@ -202,14 +174,70 @@ uint32 MidiParser_QT::readNextEvent(EventInfo &info) {
break;
case 0xF:
// General
- handleGeneralEvent(info, control);
+ handleGeneralEvent(control);
break;
}
return 0;
}
-void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
+void MidiParser_QT::handleNoteEvent(uint32 part, byte pitch, byte velocity, uint32 length) {
+ byte channel = getChannel(part);
+
+ EventInfo info;
+ info.event = 0x90 | channel;
+ info.basic.param1 = pitch;
+ info.basic.param2 = velocity;
+ info.length = (velocity == 0) ? 0 : length;
+ _queuedEvents.push(info);
+}
+
+void MidiParser_QT::handleControllerEvent(uint32 control, uint32 part, byte intPart, byte fracPart) {
+ byte channel = getChannel(part);
+ EventInfo info;
+
+ if (control == 32) {
+ // Pitch bend
+ info.event = 0xE0 | channel;
+
+ // Actually an 8.8 fixed point number
+ int16 value = (int16)((intPart << 8) | fracPart);
+
+ if (value < -0x200 || value > 0x1FF) {
+ warning("QuickTime MIDI pitch bend value (%d) out of range, clipping", value);
+ value = CLIP<int16>(value, -0x200, 0x1FF);
+ }
+
+ // Now convert the value to 'normal' MIDI values
+ value += 0x200;
+ value *= 16;
+
+ // param1 holds the low 7 bits, param2 holds the high 7 bits
+ info.basic.param1 = value & 0x7F;
+ info.basic.param2 = value >> 7;
+
+ _partMap[part].pitchBend = value;
+ } else {
+ // Regular controller
+ info.event = 0xB0 | channel;
+ info.basic.param1 = control;
+ info.basic.param2 = intPart;
+
+ // TODO: Parse more controls to hold their status
+ switch (control) {
+ case 7:
+ _partMap[part].volume = intPart;
+ break;
+ case 10:
+ _partMap[part].pan = intPart;
+ break;
+ }
+ }
+
+ _queuedEvents.push(info);
+}
+
+void MidiParser_QT::handleGeneralEvent(uint32 control) {
uint32 part = (control >> 16) & 0xFFF;
uint32 dataSize = ((control & 0xFFFF) - 2) * 4;
byte subType = READ_BE_UINT16(_position._playPos + dataSize) & 0x3FFF;
@@ -222,10 +250,7 @@ void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
// We have to remap channels because GM needs percussion to be on the
// percussion channel but QuickTime can have that anywhere.
- allocateChannel(part, READ_BE_UINT32(_position._playPos + 80));
-
- info.event = 0xC0 | getChannel(part);
- info.basic.param1 = READ_BE_UINT32(_position._playPos + 80);
+ definePart(part, READ_BE_UINT32(_position._playPos + 80));
break;
case 5: // Tune Difference
case 8: // MIDI Channel
@@ -240,30 +265,67 @@ void MidiParser_QT::handleGeneralEvent(EventInfo &info, uint32 control) {
_position._playPos += dataSize + 4;
}
-void MidiParser_QT::allocateChannel(uint32 part, uint32 instrument) {
- if (instrument == 0x4001) {
- // Drum Kit -> Percussion Channel
- if (isChannelAllocated(9))
- warning("Multiple QuickTime MIDI percussion channels");
+void MidiParser_QT::definePart(uint32 part, uint32 instrument) {
+ if (_partMap.contains(part))
+ warning("QuickTime MIDI part %d being redefined", part);
- _channelMap[part] = 9;
- } else {
+ PartStatus partStatus;
+ partStatus.instrument = instrument;
+ partStatus.volume = 127;
+ partStatus.pan = 64;
+ partStatus.pitchBend = 0x2000;
+ _partMap[part] = partStatus;
+}
+
+byte MidiParser_QT::getChannel(uint32 part) {
+ // If we already mapped it, just go with it
+ if (!_channelMap.contains(part)) {
+ byte newChannel = findFreeChannel(part);
+ _channelMap[part] = newChannel;
+ setupPart(part);
+ }
+
+ return _channelMap[part];
+}
+
+byte MidiParser_QT::findFreeChannel(uint32 part) {
+ if (_partMap[part].instrument != 0x4001) {
// Normal Instrument -> First Free Channel
- for (uint32 i = 0; i < 16; i++) {
- // 9 is reserved for Percussion
- if (i == 9 || isChannelAllocated(i))
- continue;
+ if (allChannelsAllocated())
+ deallocateFreeChannel();
+
+ for (int i = 0; i < 16; i++)
+ if (i != 9 && !isChannelAllocated(i)) // 9 is reserved for Percussion
+ return i;
- _channelMap[part] = i;
+ // Can't actually get here
+ }
+
+ // Drum Kit -> Percussion Channel
+ deallocateChannel(9);
+ return 9;
+}
+
+void MidiParser_QT::deallocateFreeChannel() {
+ for (int i = 0; i < 16; i++) {
+ if (i != 9 && !_activeNotes[i]) {
+ // TODO: Improve this by looking for the channel with the longest
+ // time since the last note.
+ deallocateChannel(i);
return;
}
-
- error("Failed to allocate channel for QuickTime MIDI");
}
+
+ error("Exceeded QuickTime MIDI channel polyphony");
}
-byte MidiParser_QT::getChannel(uint32 part) const {
- return _channelMap[part];
+void MidiParser_QT::deallocateChannel(byte channel) {
+ for (ChannelMap::iterator it = _channelMap.begin(); it != _channelMap.end(); it++) {
+ if (it->_value == channel) {
+ _channelMap.erase(it);
+ return;
+ }
+ }
}
bool MidiParser_QT::isChannelAllocated(byte channel) const {
@@ -274,8 +336,57 @@ bool MidiParser_QT::isChannelAllocated(byte channel) const {
return false;
}
+bool MidiParser_QT::allChannelsAllocated() const {
+ // Less than 16? Have room
+ if (_channelMap.size() < 15)
+ return false;
+
+ // 16? See if one of those
+ if (_channelMap.size() == 15)
+ for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++)
+ if (it->_value == 9)
+ return false;
+
+ return true;
+}
+
+void MidiParser_QT::setupPart(uint32 part) {
+ PartStatus &status = _partMap[part];
+ byte channel = _channelMap[part];
+ EventInfo info;
+
+ // First, the program change
+ if (channel != 9) {
+ // 9 is always percussion
+ info.event = 0xC0 | channel;
+ info.basic.param1 = status.instrument;
+ _queuedEvents.push(info);
+ }
+
+ // Volume
+ info.event = 0xB0 | channel;
+ info.basic.param1 = 7;
+ info.basic.param2 = status.volume;
+ _queuedEvents.push(info);
+
+ // Pan
+ info.event = 0xB0 | channel;
+ info.basic.param1 = 10;
+ info.basic.param2 = status.pan;
+ _queuedEvents.push(info);
+
+ // Pitch Bend
+ info.event = 0xE0 | channel;
+ info.basic.param1 = status.pitchBend & 0x7F;
+ info.basic.param2 = status.pitchBend >> 7;
+ _queuedEvents.push(info);
+}
+
void MidiParser_QT::resetTracking() {
+ MidiParser::resetTracking();
_channelMap.clear();
+ _queuedEvents.clear();
+ _partMap.clear();
}
Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index 25c2ae9..c2403f3 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -26,6 +26,7 @@
#include "audio/midiparser.h"
#include "common/array.h"
#include "common/hashmap.h"
+#include "common/queue.h"
#include "common/quicktime.h"
/**
@@ -70,6 +71,13 @@ private:
uint32 timeScale;
};
+ struct PartStatus {
+ uint32 instrument;
+ byte volume;
+ byte pan;
+ uint16 pitchBend;
+ };
+
class MIDISampleDesc : public SampleDesc {
public:
MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag);
@@ -79,18 +87,30 @@ private:
uint32 _requestSize;
};
- uint32 readNextEvent(EventInfo &info);
- void handleGeneralEvent(EventInfo &info, uint32 control);
+ uint32 readNextEvent();
+ void handleGeneralEvent(uint32 control);
+ void handleControllerEvent(uint32 control, uint32 part, byte intPart, byte fracPart);
+ void handleNoteEvent(uint32 part, byte pitch, byte velocity, uint32 length);
- void allocateChannel(uint32 part, uint32 instrument);
- byte getChannel(uint32 part) const;
+ void definePart(uint32 part, uint32 instrument);
+ void setupPart(uint32 part);
+
+ byte getChannel(uint32 part);
bool isChannelAllocated(byte channel) const;
+ byte findFreeChannel(uint32 part);
+ void deallocateFreeChannel();
+ void deallocateChannel(byte channel);
+ bool allChannelsAllocated() const;
byte *readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize);
Common::Array<MIDITrackInfo> _trackInfo;
+ Common::Queue<EventInfo> _queuedEvents;
+
+ typedef Common::HashMap<uint, PartStatus> PartMap;
+ PartMap _partMap;
- typedef Common::HashMap<uint, uint> ChannelMap;
+ typedef Common::HashMap<uint, byte> ChannelMap;
ChannelMap _channelMap;
void initFromContainerTracks();
Commit: 7d684d1166744cffd04e7e016fdf05de0b984a8e
https://github.com/scummvm/scummvm/commit/7d684d1166744cffd04e7e016fdf05de0b984a8e
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-12T15:02:58-07:00
Commit Message:
GROOVIE: Add a MusicPlayerMac_v2 for 11H Mac
Changed paths:
engines/groovie/groovie.cpp
engines/groovie/music.cpp
engines/groovie/music.h
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 42501af..0f05d73 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -56,6 +56,7 @@ GroovieEngine::GroovieEngine(OSystem *syst, const GroovieGameDescription *gd) :
SearchMan.addSubDirectoryMatching(gameDataDir, "groovie");
SearchMan.addSubDirectoryMatching(gameDataDir, "media");
SearchMan.addSubDirectoryMatching(gameDataDir, "system");
+ SearchMan.addSubDirectoryMatching(gameDataDir, "MIDI");
_modeSpeed = kGroovieSpeedNormal;
if (ConfMan.hasKey("t7g_speed")) {
@@ -160,10 +161,10 @@ Common::Error GroovieEngine::run() {
// Create the music player
switch (getPlatform()) {
case Common::kPlatformMacintosh:
- // TODO: The 11th Hour Mac uses QuickTime MIDI files
- // Right now, since the XMIDI are present and it is still detected as
- // the DOS version, we don't have to do anything here.
- _musicPlayer = new MusicPlayerMac_t7g(this);
+ if (_gameDescription->version == kGroovieT7G)
+ _musicPlayer = new MusicPlayerMac_t7g(this);
+ else
+ _musicPlayer = new MusicPlayerMac_v2(this);
break;
case Common::kPlatformIOS:
_musicPlayer = new MusicPlayerIOS(this);
diff --git a/engines/groovie/music.cpp b/engines/groovie/music.cpp
index b4afca5..95637fc 100644
--- a/engines/groovie/music.cpp
+++ b/engines/groovie/music.cpp
@@ -768,6 +768,52 @@ Common::SeekableReadStream *MusicPlayerMac_t7g::decompressMidi(Common::SeekableR
return new Common::MemoryReadStream(output, size, DisposeAfterUse::YES);
}
+// MusicPlayerMac_v2
+
+MusicPlayerMac_v2::MusicPlayerMac_v2(GroovieEngine *vm) : MusicPlayerMidi(vm) {
+ // Create the parser
+ _midiParser = MidiParser::createParser_QT();
+
+ // Create the driver
+ MidiDriver::DeviceHandle dev = MidiDriver::detectDevice(MDT_MIDI | MDT_ADLIB | MDT_PREFER_GM);
+ _driver = MidiDriver::createMidi(dev);
+ assert(_driver);
+
+ _driver->open(); // TODO: Handle return value != 0 (indicating an error)
+
+ // Set the parser's driver
+ _midiParser->setMidiDriver(this);
+
+ // Set the timer rate
+ _midiParser->setTimerRate(_driver->getBaseTempo());
+}
+
+bool MusicPlayerMac_v2::load(uint32 fileref, bool loop) {
+ debugC(1, kGroovieDebugMIDI | kGroovieDebugAll, "Groovie::Music: Starting the playback of song: %04X", fileref);
+
+ // Find correct filename
+ ResInfo info;
+ _vm->_resMan->getResInfo(fileref, info);
+ uint len = info.filename.size();
+ if (len < 4)
+ return false; // This shouldn't actually occur
+
+ // Remove the extension and add ".mov"
+ info.filename.deleteLastChar();
+ info.filename.deleteLastChar();
+ info.filename.deleteLastChar();
+ info.filename += "mov";
+
+ Common::SeekableReadStream *file = SearchMan.createReadStreamForMember(info.filename);
+
+ if (!file) {
+ warning("Could not find file '%s'", info.filename.c_str());
+ return false;
+ }
+
+ return loadParser(file, loop);
+}
+
MusicPlayerIOS::MusicPlayerIOS(GroovieEngine *vm) : MusicPlayer(vm) {
vm->getTimerManager()->installTimerProc(&onTimer, 50 * 1000, this, "groovieMusic");
}
diff --git a/engines/groovie/music.h b/engines/groovie/music.h
index 0342bf1..92e9c8b 100644
--- a/engines/groovie/music.h
+++ b/engines/groovie/music.h
@@ -161,6 +161,14 @@ private:
Common::SeekableReadStream *decompressMidi(Common::SeekableReadStream *stream);
};
+class MusicPlayerMac_v2 : public MusicPlayerMidi {
+public:
+ MusicPlayerMac_v2(GroovieEngine *vm);
+
+protected:
+ bool load(uint32 fileref, bool loop);
+};
+
class MusicPlayerIOS : public MusicPlayer {
public:
MusicPlayerIOS(GroovieEngine *vm);
Commit: 1d98435d34a9a1a1b3e751fb9af4b4ebd106b243
https://github.com/scummvm/scummvm/commit/1d98435d34a9a1a1b3e751fb9af4b4ebd106b243
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-12T15:07:00-07:00
Commit Message:
GROOVIE: Add detection for 11H Mac
Changed paths:
engines/groovie/detection.cpp
diff --git a/engines/groovie/detection.cpp b/engines/groovie/detection.cpp
index 1a3b313..abafbf9 100644
--- a/engines/groovie/detection.cpp
+++ b/engines/groovie/detection.cpp
@@ -137,6 +137,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 1
},
+ // The 11th Hour Macintosh English
+ {
+ {
+ "11h", "",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 1
+ },
+
+ // The 11th Hour Macintosh English (Installed)
+ {
+ {
+ "11h", "Installed",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 1
+ },
+
// The 11th Hour DOS Demo English
{
{
@@ -159,6 +189,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 2
},
+ // The Making of The 11th Hour Macintosh English
+ {
+ {
+ "11h", "Making Of",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 2
+ },
+
+ // The Making of The 11th Hour Macintosh English (Installed)
+ {
+ {
+ "11h", "Making Of (Installed)",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 2
+ },
+
// Clandestiny Trailer DOS English
{
{
@@ -170,6 +230,36 @@ static const GroovieGameDescription gameDescriptions[] = {
kGroovieV2, 3
},
+ // Clandestiny Trailer Macintosh English
+ {
+ {
+ "clandestiny", "Trailer",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "The 11th Hour Installer", 0, "bcdb4040b27f15b18f39fb9e496d384a", 1002987 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 3
+ },
+
+ // Clandestiny Trailer Macintosh English (Installed)
+ {
+ {
+ "clandestiny", "Trailer (Installed)",
+ {
+ { "disk.1", 0, "5c0428cd3659fc7bbcd0aa16485ed5da", 227 },
+ { "el01.mov", 0, "70f42dfc25b1488a08011dc45bb5145d", 6039 },
+ { 0, 0, 0, 0 }
+ },
+ Common::EN_ANY, Common::kPlatformMacintosh, ADGF_UNSTABLE,
+ GUIO4(GUIO_MIDIADLIB, GUIO_MIDIMT32, GUIO_MIDIGM, GUIO_NOASPECT)
+ },
+ kGroovieV2, 3
+ },
+
// Clandestiny DOS English
{
{
@@ -207,6 +297,11 @@ static const GroovieGameDescription gameDescriptions[] = {
{AD_TABLE_END_MARKER, kGroovieT7G, 0}
};
+static const char *directoryGlobs[] = {
+ "MIDI",
+ 0
+};
+
class GroovieMetaEngine : public AdvancedMetaEngine {
public:
GroovieMetaEngine() : AdvancedMetaEngine(gameDescriptions, sizeof(GroovieGameDescription), groovieGames) {
@@ -222,6 +317,10 @@ public:
// replaced with an according explanation.
_flags = kADFlagUseExtraAsHint;
_guioptions = GUIO3(GUIO_NOSUBTITLES, GUIO_NOSFX, GUIO_NOASPECT);
+
+ // Need MIDI directory to detect 11H Mac Installed
+ _maxScanDepth = 2;
+ _directoryGlobs = directoryGlobs;
}
const char *getName() const {
Commit: 77ef097723a869d85c2c7addba6afff85e3447ca
https://github.com/scummvm/scummvm/commit/77ef097723a869d85c2c7addba6afff85e3447ca
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-12T15:17:18-07:00
Commit Message:
GROOVIE: Load the 11H Mac installer file when present
Changed paths:
engines/groovie/groovie.cpp
diff --git a/engines/groovie/groovie.cpp b/engines/groovie/groovie.cpp
index 0f05d73..16358bf 100644
--- a/engines/groovie/groovie.cpp
+++ b/engines/groovie/groovie.cpp
@@ -30,6 +30,7 @@
#include "groovie/music.h"
#include "groovie/resource.h"
#include "groovie/roq.h"
+#include "groovie/stuffit.h"
#include "groovie/vdx.h"
#include "common/config-manager.h"
@@ -94,6 +95,15 @@ GroovieEngine::~GroovieEngine() {
}
Common::Error GroovieEngine::run() {
+ if (_gameDescription->version == kGroovieV2 && getPlatform() == Common::kPlatformMacintosh) {
+ // Load the Mac installer with the lowest priority (in case the user has installed
+ // the game and has the MIDI folder present; faster to just load them)
+ Common::Archive *archive = createStuffItArchive("The 11th Hour Installer");
+
+ if (archive)
+ SearchMan.add("The 11th Hour Installer", archive);
+ }
+
_script = new Script(this, _gameDescription->version);
// Initialize the graphics
Commit: b996a6a27034850b0c6e110e4ca3ac69adab9c84
https://github.com/scummvm/scummvm/commit/b996a6a27034850b0c6e110e4ca3ac69adab9c84
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-09-12T16:31:29-07:00
Commit Message:
SAGA: Add support for IHNM Mac music
Changed paths:
engines/saga/music.cpp
engines/saga/music.h
diff --git a/engines/saga/music.cpp b/engines/saga/music.cpp
index 13850a0..0eebf3f 100644
--- a/engines/saga/music.cpp
+++ b/engines/saga/music.cpp
@@ -30,6 +30,7 @@
#include "audio/audiostream.h"
#include "audio/mididrv.h"
#include "audio/midiparser.h"
+#include "audio/midiparser_qt.h"
#include "audio/decoders/raw.h"
#include "common/config-manager.h"
#include "common/file.h"
@@ -76,19 +77,14 @@ void MusicDriver::play(SagaEngine *vm, ByteArray *buffer, bool loop) {
}
// Check if the game is using XMIDI or SMF music
- if (vm->getGameId() == GID_IHNM && vm->isMacResources()) {
- // Just set an XMIDI parser for Mac IHNM for now
+ if (!memcmp(buffer->getBuffer(), "FORM", 4)) {
_parser = MidiParser::createParser_XMIDI();
+ // ITE had MT32 mapped instruments
+ _isGM = (vm->getGameId() != GID_ITE);
} else {
- if (!memcmp(buffer->getBuffer(), "FORM", 4)) {
- _parser = MidiParser::createParser_XMIDI();
- // ITE had MT32 mapped instruments
- _isGM = (vm->getGameId() != GID_ITE);
- } else {
- _parser = MidiParser::createParser_SMF();
- // ITE with standalone MIDI files is General MIDI
- _isGM = (vm->getGameId() == GID_ITE);
- }
+ _parser = MidiParser::createParser_SMF();
+ // ITE with standalone MIDI files is General MIDI
+ _isGM = (vm->getGameId() == GID_ITE);
}
if (!_parser->loadMusic(buffer->getBuffer(), buffer->size()))
@@ -107,6 +103,27 @@ void MusicDriver::play(SagaEngine *vm, ByteArray *buffer, bool loop) {
_isPlaying = true;
}
+void MusicDriver::playQuickTime(const Common::String &musicName, bool loop) {
+ // IHNM Mac uses QuickTime MIDI
+ _parser = MidiParser::createParser_QT();
+ _isGM = true;
+
+ if (!((MidiParser_QT *)_parser)->loadFromContainerFile(musicName))
+ error("MusicDriver::playQuickTime(): Failed to load file '%s'", musicName.c_str());
+
+ _parser->setTrack(0);
+ _parser->setMidiDriver(this);
+ _parser->setTimerRate(_driver->getBaseTempo());
+ _parser->property(MidiParser::mpCenterPitchWheelOnUnload, 1);
+ _parser->property(MidiParser::mpSendSustainOffOnNotesOff, 1);
+
+ // Handle music looping
+ _parser->property(MidiParser::mpAutoLoop, loop);
+// _isLooping = loop;
+
+ _isPlaying = true;
+}
+
void MusicDriver::pause() {
_isPlaying = false;
}
@@ -343,31 +360,19 @@ void Music::play(uint32 resourceId, MusicFlags flags) {
// Load MIDI/XMI resource data
if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) {
- // Load the external music file for Mac IHNM
-#if 0
- Common::File musicFile;
- char musicFileName[40];
- sprintf(musicFileName, "Music/Music%02x", resourceId);
- musicFile.open(musicFileName);
- resourceSize = musicFile.size();
- resourceData = new byte[resourceSize];
- musicFile.read(resourceData, resourceSize);
- musicFile.close();
-
- // TODO: The Mac music format is unsupported (QuickTime MIDI)
- // so stop here
-#endif
- return;
+ // Load the external music file for Mac IHNM
+ _player->playQuickTime(Common::String::format("Music/Music%02x", resourceId), flags & MUSIC_LOOP);
} else {
if (_currentMusicBuffer == &_musicBuffer[1]) {
_currentMusicBuffer = &_musicBuffer[0];
} else {
_currentMusicBuffer = &_musicBuffer[1];
}
+
_vm->_resource->loadResource(_musicContext, resourceId, *_currentMusicBuffer);
+ _player->play(_vm, _currentMusicBuffer, (flags & MUSIC_LOOP));
}
- _player->play(_vm, _currentMusicBuffer, (flags & MUSIC_LOOP));
setVolume(_vm->_musicVolume);
}
diff --git a/engines/saga/music.h b/engines/saga/music.h
index 5a4e662..081fab2 100644
--- a/engines/saga/music.h
+++ b/engines/saga/music.h
@@ -46,6 +46,7 @@ public:
MusicDriver();
void play(SagaEngine *vm, ByteArray *buffer, bool loop);
+ void playQuickTime(const Common::String &musicName, bool loop);
virtual void pause();
virtual void resume();
Commit: ca6fdb0807bf9ee46b6341685c129e27451db0f6
https://github.com/scummvm/scummvm/commit/ca6fdb0807bf9ee46b6341685c129e27451db0f6
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-11-18T11:20:24-08:00
Commit Message:
AUDIO: Ignore QT MIDI control change 0
QuickTime docs don't list it, and we shouldn't treat it as a bank select
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 226495b..3e95703 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -196,7 +196,12 @@ void MidiParser_QT::handleControllerEvent(uint32 control, uint32 part, byte intP
byte channel = getChannel(part);
EventInfo info;
- if (control == 32) {
+ if (control == 0) {
+ // "Bank select"
+ // QuickTime docs don't list this, but IHNM Mac calls this anyway
+ // We have to ignore this.
+ return;
+ } else if (control == 32) {
// Pitch bend
info.event = 0xE0 | channel;
Commit: b285db4db33943e0355ea33b1504d237ede0aa2a
https://github.com/scummvm/scummvm/commit/b285db4db33943e0355ea33b1504d237ede0aa2a
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-11-18T11:43:35-08:00
Commit Message:
AUDIO: Cleanup MidiParser_QT a bit
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 3e95703..2b7ef63 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -109,7 +109,7 @@ void MidiParser_QT::parseNextEvent(EventInfo &info) {
uint32 delta = 0;
while (_queuedEvents.empty())
- delta = readNextEvent();
+ delta += readNextEvent();
info = _queuedEvents.pop();
info.delta = delta;
@@ -342,16 +342,17 @@ bool MidiParser_QT::isChannelAllocated(byte channel) const {
}
bool MidiParser_QT::allChannelsAllocated() const {
- // Less than 16? Have room
+ // Less than 15? We definitely have room
if (_channelMap.size() < 15)
return false;
- // 16? See if one of those
+ // 15? One of the allocated channels might be the percussion one
if (_channelMap.size() == 15)
for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++)
if (it->_value == 9)
return false;
+ // 16 -> definitely all allocated
return true;
}
Commit: a396e180975c0e2c8fbea44af94cdcaa576a4191
https://github.com/scummvm/scummvm/commit/a396e180975c0e2c8fbea44af94cdcaa576a4191
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-12-03T06:07:33-08:00
Commit Message:
AUDIO: Finish comment
Changed paths:
audio/midiparser_qt.cpp
diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp
index 2b7ef63..6214d28 100644
--- a/audio/midiparser_qt.cpp
+++ b/audio/midiparser_qt.cpp
@@ -453,7 +453,7 @@ void MidiParser_QT::initCommon() {
}
byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize) {
- // This just goes through all chunks and
+ // This just goes through all chunks and appends them together
Common::MemoryWriteStreamDynamic output;
uint32 curSample = 0;
Commit: f65b22923420cad2d7ece7c2c66617585228ffd1
https://github.com/scummvm/scummvm/commit/f65b22923420cad2d7ece7c2c66617585228ffd1
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-12-03T06:16:58-08:00
Commit Message:
GROOVIE: Fix some indentation in StuffIt
Changed paths:
engines/groovie/stuffit.cpp
diff --git a/engines/groovie/stuffit.cpp b/engines/groovie/stuffit.cpp
index 03fa300..37f1258 100644
--- a/engines/groovie/stuffit.cpp
+++ b/engines/groovie/stuffit.cpp
@@ -435,7 +435,7 @@ Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStr
SIT14Data *dat = new SIT14Data();
- // initialization
+ // initialization
for (i = k = 0; i < 52; ++i) {
dat->var2[i] = k;
k += (1 << (dat->var1[i] = ((i >= 4) ? ((i - 4) >> 2) : 0)));
@@ -449,10 +449,10 @@ Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStr
for (j = 0; j < m; ++j)
dat->var8[i++] = l;
- for (i = 0, k = 1; i < 75; ++i) {
+ for (i = 0, k = 1; i < 75; ++i) {
dat->var5[i] = k;
k += (1 << (dat->var4[i] = (i >= 3 ? ((i - 3) >> 2) : 0)));
- }
+ }
for (i = 0; i < 4; ++i)
dat->var6[i] = i - 1;
@@ -462,8 +462,8 @@ Common::SeekableReadStream *StuffItArchive::decompress14(Common::SeekableReadStr
for (j = 0; j < m; ++j)
dat->var6[i++] = l;
- m = bits->getBits(16); // number of blocks
- j = 0; // window position
+ m = bits->getBits(16); // number of blocks
+ j = 0; // window position
while (m-- && !bits->eos()) {
bits->getBits(16); // skip crunched block size
Commit: 17f923532533c8a094e1bf0a0efa7369b20ef48a
https://github.com/scummvm/scummvm/commit/17f923532533c8a094e1bf0a0efa7369b20ef48a
Author: Matthew Hoops (clone2727 at gmail.com)
Date: 2012-12-13T15:30:05-08:00
Commit Message:
AUDIO: Add some general documentation on MidiParser_QT
Changed paths:
audio/midiparser_qt.h
diff --git a/audio/midiparser_qt.h b/audio/midiparser_qt.h
index c2403f3..d6d0f40 100644
--- a/audio/midiparser_qt.h
+++ b/audio/midiparser_qt.h
@@ -30,7 +30,20 @@
#include "common/quicktime.h"
/**
- * The QuickTime MIDI version of MidiParser.
+ * The QuickTime Music version of MidiParser.
+ *
+ * QuickTime Music is actually a superset of MIDI. It has its own custom
+ * instruments and supports more than 15 non-percussion channels. It also
+ * has custom control changes and a more advanced pitch bend (which we
+ * convert to GM pitch bend as best as possible). We then use the fallback
+ * GM instrument that each QuickTime instrument definition has to provide.
+ *
+ * Furthermore, Apple's documentation on this is terrible. You know
+ * documentation is bad when it contradicts itself three times on the same
+ * subject (like about setting the GM instrument field to percussion).
+ *
+ * This is as close to a proper QuickTime Music parser as we can currently
+ * implement using our MidiParser interface.
*/
class MidiParser_QT : public MidiParser, public Common::QuickTimeParser {
public:
Commit: 91317c3630f9bff164d8c747ef886b52a85e3a9b
https://github.com/scummvm/scummvm/commit/91317c3630f9bff164d8c747ef886b52a85e3a9b
Author: clone2727 (clone2727 at gmail.com)
Date: 2012-12-13T15:49:40-08:00
Commit Message:
Merge pull request #293 from clone2727/qtmidi
Add support for QuickTime Music playback
Changed paths:
A audio/midiparser_qt.cpp
A audio/midiparser_qt.h
A engines/groovie/stuffit.cpp
A engines/groovie/stuffit.h
audio/decoders/quicktime.cpp
audio/decoders/quicktime_intern.h
audio/midiparser.h
audio/module.mk
common/quicktime.cpp
common/quicktime.h
engines/groovie/detection.cpp
engines/groovie/groovie.cpp
engines/groovie/module.mk
engines/groovie/music.cpp
engines/groovie/music.h
engines/saga/music.cpp
engines/saga/music.h
video/qt_decoder.cpp
video/qt_decoder.h
More information about the Scummvm-git-logs
mailing list