OpenTTD
qtmidi.cpp
Go to the documentation of this file.
1 /* $Id: qtmidi.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2 
3 /*
4  * This file is part of OpenTTD.
5  * 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.
6  * 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.
7  * 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/>.
8  */
9 
29 #ifndef NO_QUICKTIME
30 
31 #include "../stdafx.h"
32 #include "qtmidi.h"
33 #include "../debug.h"
34 
35 #define Rect OTTDRect
36 #define Point OTTDPoint
37 #include <QuickTime/QuickTime.h>
38 #undef Rect
39 #undef Point
40 
41 #include "../safeguards.h"
42 
43 static FMusicDriver_QtMidi iFMusicDriver_QtMidi;
44 
45 
46 static const uint MIDI_TYPE = 'Midi';
47 
48 
55 static void SetMIDITypeIfNeeded(const FSRef *ref)
56 {
57  FSCatalogInfo catalogInfo;
58 
59  assert(ref);
60 
61  if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
62  if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
63  FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
64  if (info->fileType != MIDI_TYPE && !(info->finderFlags & kIsAlias)) {
65  OSErr e;
66  info->fileType = MIDI_TYPE;
67  e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
68  if (e == noErr) {
69  DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
70  } else {
71  DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
72  }
73  }
74  }
75 }
76 
77 
85 static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
86 {
87  int fd;
88  int ret;
89  char magic[4];
90  FSRef fsref;
91  FSSpec fsspec;
92  short refnum = 0;
93  short resid = 0;
94 
95  assert(path != NULL);
96  assert(moov != NULL);
97 
98  DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
99 
100  /*
101  * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
102  * QuickTime load MIDI files without a .mid suffix without knowing it's
103  * a MIDI file and setting the OSType of the file to the 'Midi' value.
104  * Perhaps ugly, but it seems that it does the Right Thing(tm).
105  */
106  fd = open(path, O_RDONLY, 0);
107  if (fd == -1) return false;
108  ret = read(fd, magic, 4);
109  close(fd);
110  if (ret < 4) return false;
111 
112  DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
113  if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') {
114  return false;
115  }
116 
117  if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
118  SetMIDITypeIfNeeded(&fsref);
119 
120  if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
121  if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
122  DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
123 
124  if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
125  newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
126  CloseMovieFile(refnum);
127  return false;
128  }
129  DEBUG(driver, 3, "qtmidi: movie container created");
130 
131  CloseMovieFile(refnum);
132  return true;
133 }
134 
135 
140 static bool _quicktime_started = false;
141 
142 
149 {
150  OSStatus dummy;
151 
152  if (_quicktime_started) return;
153 
154  DEBUG(driver, 2, "qtmidi: initializing Quicktime");
155  /* Be polite: check wether QuickTime is available and initialize it. */
157  (noErr == Gestalt(gestaltQuickTime, &dummy)) &&
158  (noErr == EnterMovies());
159  if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
160 }
161 
162 
164 enum QTStates {
168 };
169 
170 
171 static Movie _quicktime_movie;
172 static byte _quicktime_volume = 127;
174 
175 
179 #define VOLUME ((short)((0x00FF & _quicktime_volume) << 1))
180 
181 
189 const char *MusicDriver_QtMidi::Start(const char * const *parm)
190 {
192  return (_quicktime_started) ? NULL : "can't initialize QuickTime";
193 }
194 
195 
203 {
204  if (!_quicktime_started) return true;
205 
206  switch (_quicktime_state) {
207  case QT_STATE_IDLE:
208  case QT_STATE_STOP:
209  /* Do nothing. */
210  break;
211 
212  case QT_STATE_PLAY:
213  MoviesTask(_quicktime_movie, 0);
214  /* Check wether movie ended. */
215  if (IsMovieDone(_quicktime_movie) ||
216  (GetMovieTime(_quicktime_movie, NULL) >=
217  GetMovieDuration(_quicktime_movie))) {
219  }
220  }
221 
223 }
224 
225 
233 {
234  if (!_quicktime_started) return;
235 
236  DEBUG(driver, 2, "qtmidi: stopping driver...");
237  switch (_quicktime_state) {
238  case QT_STATE_IDLE:
239  DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
240  /* Do nothing. */
241  break;
242 
243  case QT_STATE_PLAY:
244  StopSong();
245  /* FALL THROUGH */
246 
247  case QT_STATE_STOP:
248  DisposeMovie(_quicktime_movie);
249  }
250 
251  ExitMovies();
252  _quicktime_started = false;
253 }
254 
255 
261 void MusicDriver_QtMidi::PlaySong(const char *filename)
262 {
263  if (!_quicktime_started) return;
264 
265  DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename);
266  switch (_quicktime_state) {
267  case QT_STATE_PLAY:
268  StopSong();
269  DEBUG(driver, 3, "qtmidi: previous tune stopped");
270  /* FALL THROUGH */
271 
272  case QT_STATE_STOP:
273  DisposeMovie(_quicktime_movie);
274  DEBUG(driver, 3, "qtmidi: previous tune disposed");
276  /* FALL THROUGH */
277 
278  case QT_STATE_IDLE:
280  SetMovieVolume(_quicktime_movie, VOLUME);
281  StartMovie(_quicktime_movie);
283  }
284  DEBUG(driver, 3, "qtmidi: playing '%s'", filename);
285 }
286 
287 
292 {
293  if (!_quicktime_started) return;
294 
295  switch (_quicktime_state) {
296  case QT_STATE_IDLE:
297  /* FALL THROUGH */
298 
299  case QT_STATE_STOP:
300  DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
301  /* Do nothing. */
302  break;
303 
304  case QT_STATE_PLAY:
305  StopMovie(_quicktime_movie);
307  DEBUG(driver, 3, "qtmidi: player stopped");
308  }
309 }
310 
311 
322 {
323  if (!_quicktime_started) return;
324 
325  _quicktime_volume = vol;
326 
327  DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
328  switch (_quicktime_state) {
329  case QT_STATE_IDLE:
330  /* Do nothing. */
331  break;
332 
333  case QT_STATE_PLAY:
334  case QT_STATE_STOP:
335  SetMovieVolume(_quicktime_movie, VOLUME);
336  }
337 }
338 
339 #endif /* NO_QUICKTIME */