From cfe6a2b640e91cf11a32017e79966121bf357cc7 Mon Sep 17 00:00:00 2001 From: Matthew Hoops Date: Sun, 9 Sep 2012 14:59:05 -0400 Subject: [PATCH] AUDIO: Fix QuickTime MIDI with extra info in the header The 11th Hour Mac MIDI's now play --- audio/midiparser_qt.cpp | 126 ++++++++++++++++------------------------ audio/midiparser_qt.h | 34 +---------- 2 files changed, 52 insertions(+), 108 deletions(-) diff --git a/audio/midiparser_qt.cpp b/audio/midiparser_qt.cpp index 65ca64a7f41a..5148ed74b13e 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 &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 f9378f9c3d66..5ab89bd4e76d 100644 --- a/audio/midiparser_qt.h +++ b/audio/midiparser_qt.h @@ -56,38 +56,13 @@ class MidiParser_QT : public MidiParser, public Common::QuickTimeParser { 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 NoteRequestList; - struct MIDITrackInfo { - NoteRequestList noteRequests; byte *data; uint32 size; uint32 timeScale; @@ -98,19 +73,16 @@ class MidiParser_QT : public MidiParser, public Common::QuickTimeParser { 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 _trackInfo; - uint32 _loadedInstruments; void initFromContainerTracks(); void initCommon();