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 */