# qtmidi.cpp

Go to the documentation of this file.

    00001 /* $Id: qtmidi.cpp 17570 2009-09-19 09:51:14Z rubidium $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD 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, version 2.
    00006  * OpenTTD 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.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00029 #ifndef NO_QUICKTIME
    00030
    00031 #include "../stdafx.h"
    00032 #include "qtmidi.h"
    00033 #include "../debug.h"
    00034
    00035 #define Rect  OTTDRect
    00036 #define Point OTTDPoint
    00037 #include <QuickTime/QuickTime.h>
    00038 #undef Rect
    00039 #undef Point
    00040
    00041 static FMusicDriver_QtMidi iFMusicDriver_QtMidi;
    00042
    00043
    00044 enum {
    00045   midiType = 'Midi'
    00046 };
    00047
    00048
    00055 static void SetMIDITypeIfNeeded(const FSRef *ref)
    00056 {
    00057   FSCatalogInfo catalogInfo;
    00058
    00059   assert(ref);
    00060
    00061   if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
    00062   if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
    00063     FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
    00064     if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) {
    00065       OSErr e;
    00066       info->fileType = midiType;
    00067       e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
    00068       if (e == noErr) {
    00069         DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
    00070       } else {
    00071         DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
    00072       }
    00073     }
    00074   }
    00075 }
    00076
    00077
    00085 static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
    00086 {
    00087   int fd;
    00088   int ret;
    00089   char magic[4];
    00090   FSRef fsref;
    00091   FSSpec fsspec;
    00092   short refnum = 0;
    00093   short resid  = 0;
    00094
    00095   assert(path != NULL);
    00096   assert(moov != NULL);
    00097
    00098   DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
    00099
    00100   /*
    00101    * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
    00102    * QuickTime load MIDI files without a .mid suffix without knowing it's
    00103    * a MIDI file and setting the OSType of the file to the 'Midi' value.
    00104    * Perhahaps ugly, but it seems that it does the Right Thing(tm).
    00105    */
    00106   fd = open(path, O_RDONLY, 0);
    00107   if (fd == -1) return false;
    00108   ret = read(fd, magic, 4);
    00109   close(fd);
    00110   if (ret < 4) return false;
    00111
    00112   DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
    00113   if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') {
    00114     return false;
    00115   }
    00116
    00117   if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
    00118   SetMIDITypeIfNeeded(&fsref);
    00119
    00120   if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
    00121   if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
    00122   DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
    00123
    00124   if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
    00125         newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
    00126     CloseMovieFile(refnum);
    00127     return false;
    00128   }
    00129   DEBUG(driver, 3, "qtmidi: movie container created");
    00130
    00131   CloseMovieFile(refnum);
    00132   return true;
    00133 }
    00134
    00135
    00140 static bool _quicktime_started = false;
    00141
    00142
    00148 static void InitQuickTimeIfNeeded()
    00149 {
    00150   OSStatus dummy;
    00151
    00152   if (_quicktime_started) return;
    00153
    00154   DEBUG(driver, 2, "qtmidi: initializing Quicktime");
    00155   /* Be polite: check wether QuickTime is available and initialize it. */
    00156   _quicktime_started =
    00157     (noErr == Gestalt(gestaltQuickTime, &dummy)) &&
    00158     (noErr == EnterMovies());
    00159   if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
    00160 }
    00161
    00162
    00164 enum {
    00165   QT_STATE_IDLE,
    00166   QT_STATE_PLAY,
    00167   QT_STATE_STOP,
    00168 };
    00169
    00170
    00171 static Movie _quicktime_movie;
    00172 static byte  _quicktime_volume = 127;
    00173 static int   _quicktime_state  = QT_STATE_IDLE;
    00174
    00175
    00179 #define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
    00180
    00181
    00189 const char *MusicDriver_QtMidi::Start(const char * const *parm)
    00190 {
    00191   InitQuickTimeIfNeeded();
    00192   return (_quicktime_started) ? NULL : "can't initialize QuickTime";
    00193 }
    00194
    00195
    00202 bool MusicDriver_QtMidi::IsSongPlaying()
    00203 {
    00204   if (!_quicktime_started) return true;
    00205
    00206   switch (_quicktime_state) {
    00207     case QT_STATE_IDLE:
    00208     case QT_STATE_STOP:
    00209       /* Do nothing. */
    00210       break;
    00211
    00212     case QT_STATE_PLAY:
    00213       MoviesTask(_quicktime_movie, 0);
    00214       /* Check wether movie ended. */
    00215       if (IsMovieDone(_quicktime_movie) ||
    00216           (GetMovieTime(_quicktime_movie, NULL) >=
    00217            GetMovieDuration(_quicktime_movie))) {
    00218         _quicktime_state = QT_STATE_STOP;
    00219       }
    00220   }
    00221
    00222   return _quicktime_state == QT_STATE_PLAY;
    00223 }
    00224
    00225
    00232 void MusicDriver_QtMidi::Stop()
    00233 {
    00234   if (!_quicktime_started) return;
    00235
    00236   DEBUG(driver, 2, "qtmidi: stopping driver...");
    00237   switch (_quicktime_state) {
    00238     case QT_STATE_IDLE:
    00239       DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
    00240       /* Do nothing. */
    00241       break;
    00242
    00243     case QT_STATE_PLAY:
    00244       StopSong();
    00245       /* Fall-through */
    00246
    00247     case QT_STATE_STOP:
    00248       DisposeMovie(_quicktime_movie);
    00249   }
    00250
    00251   ExitMovies();
    00252   _quicktime_started = false;
    00253 }
    00254
    00255
    00261 void MusicDriver_QtMidi::PlaySong(const char *filename)
    00262 {
    00263   if (!_quicktime_started) return;
    00264
    00265   DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
    00266   switch (_quicktime_state) {
    00267     case QT_STATE_PLAY:
    00268       StopSong();
    00269       DEBUG(driver, 3, "qtmidi: previous tune stopped");
    00270       /* Fall-through -- no break needed. */
    00271
    00272     case QT_STATE_STOP:
    00273       DisposeMovie(_quicktime_movie);
    00274       DEBUG(driver, 3, "qtmidi: previous tune disposed");
    00275       _quicktime_state = QT_STATE_IDLE;
    00276       /* Fall-through -- no break needed. */
    00277
    00278     case QT_STATE_IDLE:
    00279       LoadMovieForMIDIFile(filename, &_quicktime_movie);
    00280       SetMovieVolume(_quicktime_movie, VOLUME);
    00281       StartMovie(_quicktime_movie);
    00282       _quicktime_state = QT_STATE_PLAY;
    00283   }
    00284   DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
    00285 }
    00286
    00287
    00291 void MusicDriver_QtMidi::StopSong()
    00292 {
    00293   if (!_quicktime_started) return;
    00294
    00295   switch (_quicktime_state) {
    00296     case QT_STATE_IDLE:
    00297       /* Fall-through -- no break needed. */
    00298
    00299     case QT_STATE_STOP:
    00300       DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
    00301       /* Do nothing. */
    00302       break;
    00303
    00304     case QT_STATE_PLAY:
    00305       StopMovie(_quicktime_movie);
    00306       _quicktime_state = QT_STATE_STOP;
    00307       DEBUG(driver, 3, "qtmidi: player stopped");
    00308   }
    00309 }
    00310
    00311
    00321 void MusicDriver_QtMidi::SetVolume(byte vol)
    00322 {
    00323   if (!_quicktime_started) return;
    00324
    00325   _quicktime_volume = vol;
    00326
    00327   DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
    00328   switch (_quicktime_state) {
    00329     case QT_STATE_IDLE:
    00330       /* Do nothing. */
    00331       break;
    00332
    00333     case QT_STATE_PLAY:
    00334     case QT_STATE_STOP:
    00335       SetMovieVolume(_quicktime_movie, VOLUME);
    00336   }
    00337 }
    00338
    00339 #endif /* NO_QUICKTIME */