[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