ResidualVM logo ResidualVM website - Forums - Contact us BuildBot - Doxygen - Wiki curved edge

midiparser_qt.cpp

Go to the documentation of this file.
00001 /* ScummVM - Graphic Adventure Engine
00002  *
00003  * ScummVM is the legal property of its developers, whose names
00004  * are too numerous to list here. Please refer to the COPYRIGHT
00005  * file distributed with this source distribution.
00006  *
00007  * This program is free software; you can redistribute it and/or
00008  * modify it under the terms of the GNU General Public License
00009  * as published by the Free Software Foundation; either version 2
00010  * of the License, or (at your option) any later version.
00011  *
00012  * This program is distributed in the hope that it will be useful,
00013  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00014  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00015  * GNU General Public License for more details.
00016  *
00017  * You should have received a copy of the GNU General Public License
00018  * along with this program; if not, write to the Free Software
00019  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00020  *
00021  */
00022 
00023 #include "audio/midiparser_qt.h"
00024 #include "common/debug.h"
00025 #include "common/memstream.h"
00026 
00027 bool MidiParser_QT::loadMusic(byte *data, uint32 size) {
00028     if (size < 8)
00029         return false;
00030 
00031     Common::SeekableReadStream *stream = new Common::MemoryReadStream(data, size, DisposeAfterUse::NO);
00032 
00033     // Attempt to detect what format we have
00034     bool result;
00035     if (READ_BE_UINT32(data + 4) == MKTAG('m', 'u', 's', 'i'))
00036         result = loadFromTune(stream);
00037     else
00038         result = loadFromContainerStream(stream);
00039 
00040     if (!result) {
00041         delete stream;
00042         return false;
00043     }
00044 
00045     return true;
00046 }
00047 
00048 void MidiParser_QT::unloadMusic() {
00049     MidiParser::unloadMusic();
00050     close();
00051 
00052     // Unlike those lesser formats, we *do* hold track data
00053     for (uint i = 0; i < _trackInfo.size(); i++)
00054         free(_trackInfo[i].data);
00055 
00056     _trackInfo.clear();
00057 }
00058 
00059 bool MidiParser_QT::loadFromTune(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
00060     unloadMusic();
00061 
00062     // a tune starts off with a sample description
00063     stream->readUint32BE(); // header size
00064 
00065     if (stream->readUint32BE() != MKTAG('m', 'u', 's', 'i'))
00066         return false;
00067 
00068     stream->readUint32BE(); // reserved
00069     stream->readUint16BE(); // reserved
00070     stream->readUint16BE(); // index
00071 
00072     stream->readUint32BE(); // flags, ignore
00073 
00074     MIDITrackInfo trackInfo;
00075     trackInfo.size = stream->size() - stream->pos();
00076     assert(trackInfo.size > 0);
00077 
00078     trackInfo.data = (byte *)malloc(trackInfo.size);
00079     stream->read(trackInfo.data, trackInfo.size);
00080 
00081     trackInfo.timeScale = 600; // the default
00082     _trackInfo.push_back(trackInfo);
00083 
00084     initCommon();
00085     return true;
00086 }
00087 
00088 bool MidiParser_QT::loadFromContainerStream(Common::SeekableReadStream *stream, DisposeAfterUse::Flag disposeAfterUse) {
00089     unloadMusic();
00090 
00091     if (!parseStream(stream, disposeAfterUse))
00092         return false;
00093 
00094     initFromContainerTracks();
00095     return true;
00096 }
00097 
00098 bool MidiParser_QT::loadFromContainerFile(const Common::String &fileName) {
00099     unloadMusic();
00100 
00101     if (!parseFile(fileName))
00102         return false;
00103 
00104     initFromContainerTracks();
00105     return true;
00106 }
00107 
00108 void MidiParser_QT::parseNextEvent(EventInfo &info) {
00109     uint32 delta = 0;
00110 
00111     while (_queuedEvents.empty())
00112         delta += readNextEvent();
00113 
00114     info = _queuedEvents.pop();
00115     info.delta = delta;
00116 }
00117 
00118 uint32 MidiParser_QT::readNextEvent() {
00119     if (_position._playPos >= _trackInfo[_activeTrack].data + _trackInfo[_activeTrack].size) {
00120         // Manually insert end of track when we reach the end
00121         EventInfo info;
00122         info.event = 0xFF;
00123         info.ext.type = 0x2F;
00124         _queuedEvents.push(info);
00125         return 0;
00126     }
00127 
00128     uint32 control = readUint32();
00129 
00130     switch (control >> 28) {
00131     case 0x0:
00132     case 0x1:
00133         // Rest
00134         // We handle this by recursively adding up all the rests into the
00135         // next event's delta
00136         return readNextEvent() + (control & 0xFFFFFF);
00137     case 0x2:
00138     case 0x3:
00139         // Note event
00140         handleNoteEvent((control >> 24) & 0x1F, ((control >> 18) & 0x3F) + 32, (control >> 11) & 0x7F, control & 0x7FF);
00141         break;
00142     case 0x4:
00143     case 0x5:
00144         // Controller
00145         handleControllerEvent((control >> 16) & 0xFF, (control >> 24) & 0x1F, (control >> 8) & 0xFF, control & 0xFF);
00146         break;
00147     case 0x6:
00148     case 0x7:
00149         // Marker
00150         // Used for editing only, so we don't need to care about this
00151         break;
00152     case 0x9: {
00153         // Extended note event
00154         uint32 extra = readUint32();
00155         handleNoteEvent((control >> 16) & 0xFFF, (control >> 8) & 0xFF, (extra >> 22) & 0x7F, extra & 0x3FFFFF);
00156         break;
00157     }
00158     case 0xA: {
00159         // Extended controller
00160         uint32 extra = readUint32();
00161         handleControllerEvent((extra >> 16) & 0x3FFF, (control >> 16) & 0xFFF, (extra >> 8) & 0xFF, extra & 0xFF);
00162         break;
00163     }
00164     case 0xB:
00165         // Knob
00166         error("Encountered knob event in QuickTime MIDI");
00167         break;
00168     case 0x8:
00169     case 0xC:
00170     case 0xD:
00171     case 0xE:
00172         // Reserved
00173         readUint32();
00174         break;
00175     case 0xF:
00176         // General
00177         handleGeneralEvent(control);
00178         break;
00179     }
00180 
00181     return 0;
00182 }
00183 
00184 void MidiParser_QT::handleNoteEvent(uint32 part, byte pitch, byte velocity, uint32 length) {
00185     byte channel = getChannel(part);
00186 
00187     EventInfo info;
00188     info.event = 0x90 | channel;
00189     info.basic.param1 = pitch;
00190     info.basic.param2 = velocity;
00191     info.length = (velocity == 0) ? 0 : length;
00192     _queuedEvents.push(info);
00193 }
00194 
00195 void MidiParser_QT::handleControllerEvent(uint32 control, uint32 part, byte intPart, byte fracPart) {
00196     byte channel = getChannel(part);
00197     EventInfo info;
00198 
00199     if (control == 0) {
00200         // "Bank select"
00201         // QuickTime docs don't list this, but IHNM Mac calls this anyway
00202         // We have to ignore this.
00203         return;
00204     } else if (control == 32) {
00205         // Pitch bend
00206         info.event = 0xE0 | channel;
00207 
00208         // Actually an 8.8 fixed point number
00209         int16 value = (int16)((intPart << 8) | fracPart);
00210 
00211         if (value < -0x200 || value > 0x1FF) {
00212             warning("QuickTime MIDI pitch bend value (%d) out of range, clipping", value);
00213             value = CLIP<int16>(value, -0x200, 0x1FF);
00214         }
00215 
00216         // Now convert the value to 'normal' MIDI values
00217         value += 0x200;
00218         value *= 16;
00219 
00220         // param1 holds the low 7 bits, param2 holds the high 7 bits
00221         info.basic.param1 = value & 0x7F;
00222         info.basic.param2 = value >> 7;
00223 
00224         _partMap[part].pitchBend = value;
00225     } else {
00226         // Regular controller
00227         info.event = 0xB0 | channel;
00228         info.basic.param1 = control;
00229         info.basic.param2 = intPart;
00230 
00231         // TODO: Parse more controls to hold their status
00232         switch (control) {
00233         case 7:
00234             _partMap[part].volume = intPart;
00235             break;
00236         case 10:
00237             _partMap[part].pan = intPart;
00238             break;
00239         }
00240     }
00241 
00242     _queuedEvents.push(info);
00243 }
00244 
00245 void MidiParser_QT::handleGeneralEvent(uint32 control) {
00246     uint32 part = (control >> 16) & 0xFFF;
00247     uint32 dataSize = ((control & 0xFFFF) - 2) * 4;
00248     byte subType = READ_BE_UINT16(_position._playPos + dataSize) & 0x3FFF;
00249 
00250     switch (subType) {
00251     case 1:
00252         // Note Request
00253         // Currently we're only using the GM number from the request
00254         assert(dataSize == 84);
00255 
00256         // We have to remap channels because GM needs percussion to be on the
00257         // percussion channel but QuickTime can have that anywhere.
00258         definePart(part, READ_BE_UINT32(_position._playPos + 80));
00259         break;
00260     case 5: // Tune Difference
00261     case 8: // MIDI Channel
00262     case 10: // No-op
00263     case 11: // Used Notes
00264         // Should be safe to skip these
00265         break;
00266     default:
00267         warning("Unhandled general event %d", subType);
00268     }
00269 
00270     _position._playPos += dataSize + 4;
00271 }
00272 
00273 void MidiParser_QT::definePart(uint32 part, uint32 instrument) {
00274     if (_partMap.contains(part))
00275         warning("QuickTime MIDI part %d being redefined", part);
00276 
00277     PartStatus partStatus;
00278     partStatus.instrument = instrument;
00279     partStatus.volume = 127;
00280     partStatus.pan = 64;
00281     partStatus.pitchBend = 0x2000;
00282     _partMap[part] = partStatus;
00283 }
00284 
00285 byte MidiParser_QT::getChannel(uint32 part) {
00286     // If we already mapped it, just go with it
00287     if (!_channelMap.contains(part)) {
00288         byte newChannel = findFreeChannel(part);
00289         _channelMap[part] = newChannel;
00290         setupPart(part);
00291     }
00292 
00293     return _channelMap[part];
00294 }
00295 
00296 byte MidiParser_QT::findFreeChannel(uint32 part) {
00297     if (_partMap[part].instrument != 0x4001) {
00298         // Normal Instrument -> First Free Channel
00299         if (allChannelsAllocated())
00300             deallocateFreeChannel();
00301 
00302         for (int i = 0; i < 16; i++)
00303             if (i != 9 && !isChannelAllocated(i)) // 9 is reserved for Percussion
00304                 return i;
00305 
00306         // Can't actually get here
00307     }
00308 
00309     // Drum Kit -> Percussion Channel
00310     deallocateChannel(9);
00311     return 9;
00312 }
00313 
00314 void MidiParser_QT::deallocateFreeChannel() {
00315     for (int i = 0; i < 16; i++) {
00316         if (i != 9 && !_activeNotes[i]) {
00317             // TODO: Improve this by looking for the channel with the longest
00318             // time since the last note.
00319             deallocateChannel(i);
00320             return;
00321         }
00322     }
00323 
00324     error("Exceeded QuickTime MIDI channel polyphony");
00325 }
00326 
00327 void MidiParser_QT::deallocateChannel(byte channel) {
00328     for (ChannelMap::iterator it = _channelMap.begin(); it != _channelMap.end(); it++) {
00329         if (it->_value == channel) {
00330             _channelMap.erase(it);
00331             return;
00332         }
00333     }
00334 }
00335 
00336 bool MidiParser_QT::isChannelAllocated(byte channel) const {
00337     for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++)
00338         if (it->_value == channel)
00339             return true;
00340 
00341     return false;
00342 }
00343 
00344 bool MidiParser_QT::allChannelsAllocated() const {
00345     // Less than 15? We definitely have room
00346     if (_channelMap.size() < 15)
00347         return false;
00348 
00349     // 15? One of the allocated channels might be the percussion one
00350     if (_channelMap.size() == 15)
00351         for (ChannelMap::const_iterator it = _channelMap.begin(); it != _channelMap.end(); it++)
00352             if (it->_value == 9)
00353                 return false;
00354 
00355     // 16 -> definitely all allocated
00356     return true;
00357 }
00358 
00359 void MidiParser_QT::setupPart(uint32 part) {
00360     PartStatus &status = _partMap[part];
00361     byte channel = _channelMap[part];
00362     EventInfo info;
00363 
00364     // First, the program change
00365     if (channel != 9) {
00366         // 9 is always percussion
00367         info.event = 0xC0 | channel;
00368         info.basic.param1 = status.instrument;
00369         _queuedEvents.push(info);
00370     }
00371 
00372     // Volume
00373     info.event = 0xB0 | channel;
00374     info.basic.param1 = 7;
00375     info.basic.param2 = status.volume;
00376     _queuedEvents.push(info);
00377 
00378     // Pan
00379     info.event = 0xB0 | channel;
00380     info.basic.param1 = 10;
00381     info.basic.param2 = status.pan;
00382     _queuedEvents.push(info);
00383 
00384     // Pitch Bend
00385     info.event = 0xE0 | channel;
00386     info.basic.param1 = status.pitchBend & 0x7F;
00387     info.basic.param2 = status.pitchBend >> 7;
00388     _queuedEvents.push(info);
00389 }
00390 
00391 void MidiParser_QT::resetTracking() {
00392     MidiParser::resetTracking();
00393     _channelMap.clear();
00394     _queuedEvents.clear();
00395     _partMap.clear();
00396 }
00397 
00398 Common::QuickTimeParser::SampleDesc *MidiParser_QT::readSampleDesc(Track *track, uint32 format, uint32 descSize) {
00399     if (track->codecType == CODEC_TYPE_MIDI) {
00400         debug(0, "MIDI Codec FourCC '%s'", tag2str(format));
00401 
00402         _fd->readUint32BE(); // flags, ignore
00403         descSize -= 4;
00404 
00405         MIDISampleDesc *entry = new MIDISampleDesc(track, format);
00406         entry->_requestSize = descSize;
00407         entry->_requestData = (byte *)malloc(descSize);
00408         _fd->read(entry->_requestData, descSize);
00409         return entry;
00410     }
00411 
00412     return 0;
00413 }
00414 
00415 MidiParser_QT::MIDISampleDesc::MIDISampleDesc(Common::QuickTimeParser::Track *parentTrack, uint32 codecTag) :
00416         Common::QuickTimeParser::SampleDesc(parentTrack, codecTag) {
00417 }
00418 
00419 void MidiParser_QT::initFromContainerTracks() {
00420     const Common::Array<Common::QuickTimeParser::Track *> &tracks = Common::QuickTimeParser::_tracks;
00421 
00422     for (uint32 i = 0; i < tracks.size(); i++) {
00423         if (tracks[i]->codecType == CODEC_TYPE_MIDI) {
00424             assert(tracks[i]->sampleDescs.size() == 1);
00425 
00426             if (tracks[i]->editList.size() != 1)
00427                 warning("Unhandled QuickTime MIDI edit lists, things may go awry");
00428 
00429             MIDITrackInfo trackInfo;
00430             trackInfo.data = readWholeTrack(tracks[i], trackInfo.size);
00431             trackInfo.timeScale = tracks[i]->timeScale;
00432             _trackInfo.push_back(trackInfo);
00433         }
00434     }
00435 
00436     initCommon();
00437 }
00438 
00439 void MidiParser_QT::initCommon() {
00440     // Now we have all our info needed in _trackInfo from whatever container
00441     // form, we can fill in the MidiParser tracks.
00442 
00443     _numTracks = _trackInfo.size();
00444     assert(_numTracks > 0);
00445 
00446     for (uint32 i = 0; i < _trackInfo.size(); i++)
00447         MidiParser::_tracks[i] = _trackInfo[i].data;
00448 
00449     _ppqn = _trackInfo[0].timeScale;
00450     resetTracking();
00451     setTempo(1000000);
00452     setTrack(0);
00453 }
00454 
00455 byte *MidiParser_QT::readWholeTrack(Common::QuickTimeParser::Track *track, uint32 &trackSize) {
00456     // This just goes through all chunks and appends them together
00457 
00458     Common::MemoryWriteStreamDynamic output(DisposeAfterUse::NO);
00459     uint32 curSample = 0;
00460 
00461     // Read in the note request data first
00462     MIDISampleDesc *entry = (MIDISampleDesc *)track->sampleDescs[0];
00463     output.write(entry->_requestData, entry->_requestSize);
00464 
00465     for (uint i = 0; i < track->chunkCount; i++) {
00466         _fd->seek(track->chunkOffsets[i]);
00467 
00468         uint32 sampleCount = 0;
00469 
00470         for (uint32 j = 0; j < track->sampleToChunkCount; j++)
00471             if (i >= track->sampleToChunk[j].first)
00472                 sampleCount = track->sampleToChunk[j].count;
00473 
00474         for (uint32 j = 0; j < sampleCount; j++, curSample++) {
00475             uint32 size = (track->sampleSize != 0) ? track->sampleSize : track->sampleSizes[curSample];
00476 
00477             byte *data = new byte[size];
00478             _fd->read(data, size);
00479             output.write(data, size);
00480             delete[] data;
00481         }
00482     }
00483 
00484     trackSize = output.size();
00485     return output.getData();
00486 }
00487 
00488 uint32 MidiParser_QT::readUint32() {
00489     uint32 value = READ_BE_UINT32(_position._playPos);
00490     _position._playPos += 4;
00491     return value;
00492 }
00493 
00494 MidiParser *MidiParser::createParser_QT() {
00495     return new MidiParser_QT();
00496 }


Generated on Sat Mar 23 2019 05:01:50 for ResidualVM by doxygen 1.7.1
curved edge   curved edge