# qtmidi.cpp

Go to the documentation of this file.

    00001 /* $Id$ */
    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 static const uint MIDI_TYPE = 'Midi';
    00045
    00046
    00053 static void SetMIDITypeIfNeeded(const FSRef *ref)
    00054 {
    00055   FSCatalogInfo catalogInfo;
    00056
    00057   assert(ref);
    00058
    00059   if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
    00060   if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
    00061     FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
    00062     if (info->fileType != MIDI_TYPE && !(info->finderFlags & kIsAlias)) {
    00063       OSErr e;
    00064       info->fileType = MIDI_TYPE;
    00065       e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
    00066       if (e == noErr) {
    00067         DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
    00068       } else {
    00069         DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
    00070       }
    00071     }
    00072   }
    00073 }
    00074
    00075
    00083 static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
    00084 {
    00085   int fd;
    00086   int ret;
    00087   char magic[4];
    00088   FSRef fsref;
    00089   FSSpec fsspec;
    00090   short refnum = 0;
    00091   short resid  = 0;
    00092
    00093   assert(path != NULL);
    00094   assert(moov != NULL);
    00095
    00096   DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
    00097
    00098   /*
    00099    * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
    00100    * QuickTime load MIDI files without a .mid suffix without knowing it's
    00101    * a MIDI file and setting the OSType of the file to the 'Midi' value.
    00102    * Perhaps ugly, but it seems that it does the Right Thing(tm).
    00103    */
    00104   fd = open(path, O_RDONLY, 0);
    00105   if (fd == -1) return false;
    00106   ret = read(fd, magic, 4);
    00107   close(fd);
    00108   if (ret < 4) return false;
    00109
    00110   DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
    00111   if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') {
    00112     return false;
    00113   }
    00114
    00115   if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
    00116   SetMIDITypeIfNeeded(&fsref);
    00117
    00118   if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
    00119   if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
    00120   DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
    00121
    00122   if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
    00123         newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
    00124     CloseMovieFile(refnum);
    00125     return false;
    00126   }
    00127   DEBUG(driver, 3, "qtmidi: movie container created");
    00128
    00129   CloseMovieFile(refnum);
    00130   return true;
    00131 }
    00132
    00133
    00138 static bool _quicktime_started = false;
    00139
    00140
    00146 static void InitQuickTimeIfNeeded()
    00147 {
    00148   OSStatus dummy;
    00149
    00150   if (_quicktime_started) return;
    00151
    00152   DEBUG(driver, 2, "qtmidi: initializing Quicktime");
    00153   /* Be polite: check wether QuickTime is available and initialize it. */
    00154   _quicktime_started =
    00155     (noErr == Gestalt(gestaltQuickTime, &dummy)) &&
    00156     (noErr == EnterMovies());
    00157   if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
    00158 }
    00159
    00160
    00162 enum QTStates {
    00163   QT_STATE_IDLE,
    00164   QT_STATE_PLAY,
    00165   QT_STATE_STOP,
    00166 };
    00167
    00168
    00169 static Movie _quicktime_movie;
    00170 static byte  _quicktime_volume = 127;
    00171 static int   _quicktime_state  = QT_STATE_IDLE;
    00172
    00173
    00177 #define VOLUME  ((short)((0x00FF & _quicktime_volume) << 1))
    00178
    00179
    00187 const char *MusicDriver_QtMidi::Start(const char * const *parm)
    00188 {
    00189   InitQuickTimeIfNeeded();
    00190   return (_quicktime_started) ? NULL : "can't initialize QuickTime";
    00191 }
    00192
    00193
    00200 bool MusicDriver_QtMidi::IsSongPlaying()
    00201 {
    00202   if (!_quicktime_started) return true;
    00203
    00204   switch (_quicktime_state) {
    00205     case QT_STATE_IDLE:
    00206     case QT_STATE_STOP:
    00207       /* Do nothing. */
    00208       break;
    00209
    00210     case QT_STATE_PLAY:
    00211       MoviesTask(_quicktime_movie, 0);
    00212       /* Check wether movie ended. */
    00213       if (IsMovieDone(_quicktime_movie) ||
    00214           (GetMovieTime(_quicktime_movie, NULL) >=
    00215            GetMovieDuration(_quicktime_movie))) {
    00216         _quicktime_state = QT_STATE_STOP;
    00217       }
    00218   }
    00219
    00220   return _quicktime_state == QT_STATE_PLAY;
    00221 }
    00222
    00223
    00230 void MusicDriver_QtMidi::Stop()
    00231 {
    00232   if (!_quicktime_started) return;
    00233
    00234   DEBUG(driver, 2, "qtmidi: stopping driver...");
    00235   switch (_quicktime_state) {
    00236     case QT_STATE_IDLE:
    00237       DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
    00238       /* Do nothing. */
    00239       break;
    00240
    00241     case QT_STATE_PLAY:
    00242       StopSong();
    00243       /* FALL THROUGH */
    00244
    00245     case QT_STATE_STOP:
    00246       DisposeMovie(_quicktime_movie);
    00247   }
    00248
    00249   ExitMovies();
    00250   _quicktime_started = false;
    00251 }
    00252
    00253
    00259 void MusicDriver_QtMidi::PlaySong(const char *filename)
    00260 {
    00261   if (!_quicktime_started) return;
    00262
    00263   DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
    00264   switch (_quicktime_state) {
    00265     case QT_STATE_PLAY:
    00266       StopSong();
    00267       DEBUG(driver, 3, "qtmidi: previous tune stopped");
    00268       /* FALL THROUGH */
    00269
    00270     case QT_STATE_STOP:
    00271       DisposeMovie(_quicktime_movie);
    00272       DEBUG(driver, 3, "qtmidi: previous tune disposed");
    00273       _quicktime_state = QT_STATE_IDLE;
    00274       /* FALL THROUGH */
    00275
    00276     case QT_STATE_IDLE:
    00277       LoadMovieForMIDIFile(filename, &_quicktime_movie);
    00278       SetMovieVolume(_quicktime_movie, VOLUME);
    00279       StartMovie(_quicktime_movie);
    00280       _quicktime_state = QT_STATE_PLAY;
    00281   }
    00282   DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
    00283 }
    00284
    00285
    00289 void MusicDriver_QtMidi::StopSong()
    00290 {
    00291   if (!_quicktime_started) return;
    00292
    00293   switch (_quicktime_state) {
    00294     case QT_STATE_IDLE:
    00295       /* FALL THROUGH */
    00296
    00297     case QT_STATE_STOP:
    00298       DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
    00299       /* Do nothing. */
    00300       break;
    00301
    00302     case QT_STATE_PLAY:
    00303       StopMovie(_quicktime_movie);
    00304       _quicktime_state = QT_STATE_STOP;
    00305       DEBUG(driver, 3, "qtmidi: player stopped");
    00306   }
    00307 }
    00308
    00309
    00319 void MusicDriver_QtMidi::SetVolume(byte vol)
    00320 {
    00321   if (!_quicktime_started) return;
    00322
    00323   _quicktime_volume = vol;
    00324
    00325   DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
    00326   switch (_quicktime_state) {
    00327     case QT_STATE_IDLE:
    00328       /* Do nothing. */
    00329       break;
    00330
    00331     case QT_STATE_PLAY:
    00332     case QT_STATE_STOP:
    00333       SetMovieVolume(_quicktime_movie, VOLUME);
    00334   }
    00335 }
    00336
    00337 #endif /* NO_QUICKTIME */