# qtmidi.cpp

Go to the documentation of this file.

    00001 /* $Id: qtmidi.cpp 15718 2009-03-15 00:32:18Z rubidium $ */
    00002
    00022 #define MAC_OS_X_VERSION_MIN_REQUIRED    MAC_OS_X_VERSION_10_3
    00023 #include <AvailabilityMacros.h>
    00024
    00025 /*
    00026  * OpenTTD includes.
    00027  */
    00028 #define  WindowClass OSX_WindowClass
    00029 #include <QuickTime/QuickTime.h>
    00030 #undef   WindowClass
    00031
    00032 #include "../stdafx.h"
    00033 #include "qtmidi.h"
    00034
    00035 /*
    00036  * System includes. We need to workaround with some defines because there's
    00037  * stuff already defined in QuickTime headers.
    00038  */
    00039 #define  OTTD_Random OSX_OTTD_Random
    00040 #undef   OTTD_Random
    00041 #undef   WindowClass
    00042 #undef   SL_ERROR
    00043 #undef   bool
    00044
    00045 #include <assert.h>
    00046 #include <unistd.h>
    00047 #include <fcntl.h>
    00048
    00049 /* we need to include debug.h after CoreServices because defining DEBUG will break CoreServices in OSX 10.2 */
    00050 #include "../debug.h"
    00051
    00052 static FMusicDriver_QtMidi iFMusicDriver_QtMidi;
    00053
    00054
    00055 enum {
    00056   midiType = 'Midi'
    00057 };
    00058
    00059
    00066 static void SetMIDITypeIfNeeded(const FSRef *ref)
    00067 {
    00068   FSCatalogInfo catalogInfo;
    00069
    00070   assert(ref);
    00071
    00072   if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
    00073   if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
    00074     FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
    00075     if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) {
    00076       OSErr e;
    00077       info->fileType = midiType;
    00078       e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
    00079       if (e == noErr) {
    00080         DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
    00081       } else {
    00082         DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
    00083       }
    00084     }
    00085   }
    00086 }
    00087
    00088
    00096 static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
    00097 {
    00098   int fd;
    00099   int ret;
    00100   char magic[4];
    00101   FSRef fsref;
    00102   FSSpec fsspec;
    00103   short refnum = 0;
    00104   short resid  = 0;
    00105
    00106   assert(path != NULL);
    00107   assert(moov != NULL);
    00108
    00109   DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
    00110
    00111   /*
    00112    * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
    00113    * QuickTime load MIDI files without a .mid suffix without knowing it's
    00114    * a MIDI file and setting the OSType of the file to the 'Midi' value.
    00115    * Perhahaps ugly, but it seems that it does the Right Thing(tm).
    00116    */
    00117   fd = open(path, O_RDONLY, 0);
    00118   if (fd == -1) return false;
    00119   ret = read(fd, magic, 4);
    00120   close(fd);
    00121   if (ret < 4) return false;
    00122
    00123   DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
    00124   if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd')
    00125     return false;
    00126
    00127   if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
    00128   SetMIDITypeIfNeeded(&fsref);
    00129
    00130   if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
    00131   if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
    00132   DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
    00133
    00134   if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
    00135         newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
    00136     CloseMovieFile(refnum);
    00137     return false;
    00138   }
    00139   DEBUG(driver, 3, "qtmidi: movie container created");
    00140
    00141   CloseMovieFile(refnum);
    00142   return true;
    00143 }
    00144
    00145
    00150 static bool _quicktime_started = false;
    00151
    00152
    00158 static void InitQuickTimeIfNeeded()
    00159 {
    00160   OSStatus dummy;
    00161
    00162   if (_quicktime_started) return;
    00163
    00164   DEBUG(driver, 2, "qtmidi: initializing Quicktime");
    00165   /* Be polite: check wether QuickTime is available and initialize it. */
    00166   _quicktime_started =
    00167     (noErr == Gestalt(gestaltQuickTime, &dummy)) &&
    00168     (noErr == EnterMovies());
    00169   if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
    00170 }
    00171
    00172
    00174 enum {
    00175   QT_STATE_IDLE,
    00176   QT_STATE_PLAY,
    00177   QT_STATE_STOP,
    00178 };
    00179
    00180
    00181 static Movie _quicktime_movie;
    00182 static byte  _quicktime_volume = 127;
    00183 static int   _quicktime_state  = QT_STATE_IDLE;
    00184
    00185
    00189 #define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
    00190
    00191
    00199 const char *MusicDriver_QtMidi::Start(const char * const *parm)
    00200 {
    00201   InitQuickTimeIfNeeded();
    00202   return (_quicktime_started) ? NULL : "can't initialize QuickTime";
    00203 }
    00204
    00205
    00212 bool MusicDriver_QtMidi::IsSongPlaying()
    00213 {
    00214   if (!_quicktime_started) return true;
    00215
    00216   switch (_quicktime_state) {
    00217     case QT_STATE_IDLE:
    00218     case QT_STATE_STOP:
    00219       /* Do nothing. */
    00220       break;
    00221     case QT_STATE_PLAY:
    00222       MoviesTask(_quicktime_movie, 0);
    00223       /* Check wether movie ended. */
    00224       if (IsMovieDone(_quicktime_movie) ||
    00225           (GetMovieTime(_quicktime_movie, NULL) >=
    00226            GetMovieDuration(_quicktime_movie)))
    00227         _quicktime_state = QT_STATE_STOP;
    00228   }
    00229
    00230   return _quicktime_state == QT_STATE_PLAY;
    00231 }
    00232
    00233
    00240 void MusicDriver_QtMidi::Stop()
    00241 {
    00242   if (!_quicktime_started) return;
    00243
    00244   DEBUG(driver, 2, "qtmidi: stopping driver...");
    00245   switch (_quicktime_state) {
    00246     case QT_STATE_IDLE:
    00247       DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
    00248       /* Do nothing. */
    00249       break;
    00250     case QT_STATE_PLAY:
    00251       StopSong();
    00252     case QT_STATE_STOP:
    00253       DisposeMovie(_quicktime_movie);
    00254   }
    00255
    00256   ExitMovies();
    00257   _quicktime_started = false;
    00258 }
    00259
    00260
    00266 void MusicDriver_QtMidi::PlaySong(const char *filename)
    00267 {
    00268   if (!_quicktime_started) return;
    00269
    00270   DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
    00271   switch (_quicktime_state) {
    00272     case QT_STATE_PLAY:
    00273       StopSong();
    00274       DEBUG(driver, 3, "qtmidi: previous tune stopped");
    00275       /* XXX Fall-through -- no break needed. */
    00276     case QT_STATE_STOP:
    00277       DisposeMovie(_quicktime_movie);
    00278       DEBUG(driver, 3, "qtmidi: previous tune disposed");
    00279       _quicktime_state = QT_STATE_IDLE;
    00280       /* XXX Fall-through -- no break needed. */
    00281     case QT_STATE_IDLE:
    00282       LoadMovieForMIDIFile(filename, &_quicktime_movie);
    00283       SetMovieVolume(_quicktime_movie, VOLUME);
    00284       StartMovie(_quicktime_movie);
    00285       _quicktime_state = QT_STATE_PLAY;
    00286   }
    00287   DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
    00288 }
    00289
    00290
    00294 void MusicDriver_QtMidi::StopSong()
    00295 {
    00296   if (!_quicktime_started) return;
    00297
    00298   switch (_quicktime_state) {
    00299     case QT_STATE_IDLE:
    00300       /* XXX Fall-through -- no break needed. */
    00301     case QT_STATE_STOP:
    00302       DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
    00303       /* Do nothing. */
    00304       break;
    00305     case QT_STATE_PLAY:
    00306       StopMovie(_quicktime_movie);
    00307       _quicktime_state = QT_STATE_STOP;
    00308       DEBUG(driver, 3, "qtmidi: player stopped");
    00309   }
    00310 }
    00311
    00312
    00322 void MusicDriver_QtMidi::SetVolume(byte vol)
    00323 {
    00324   if (!_quicktime_started) return;
    00325
    00326   _quicktime_volume = vol;
    00327
    00328   DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
    00329   switch (_quicktime_state) {
    00330     case QT_STATE_IDLE:
    00331       /* Do nothing. */
    00332       break;
    00333     case QT_STATE_PLAY:
    00334     case QT_STATE_STOP:
    00335       SetMovieVolume(_quicktime_movie, VOLUME);
    00336   }
    00337 }
    00338