OpenTTD
qtmidi.cpp
Go to the documentation of this file.
1 /* $Id$ */
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 "midifile.hpp"
34 #include "../debug.h"
35 #include "../base_media_base.h"
36 
37 #define Rect OTTDRect
38 #define Point OTTDPoint
39 #include <QuickTime/QuickTime.h>
40 #undef Rect
41 #undef Point
42 
43 #include "../safeguards.h"
44 
45 static FMusicDriver_QtMidi iFMusicDriver_QtMidi;
46 
47 
48 static const uint MIDI_TYPE = 'Midi';
49 
50 
57 static void SetMIDITypeIfNeeded(const FSRef *ref)
58 {
59  FSCatalogInfo catalogInfo;
60 
61  assert(ref);
62 
63  if (noErr != FSGetCatalogInfo(ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return;
64  if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) {
65  FileInfo * const info = (FileInfo *) catalogInfo.finderInfo;
66  if (info->fileType != MIDI_TYPE && !(info->finderFlags & kIsAlias)) {
67  OSErr e;
68  info->fileType = MIDI_TYPE;
69  e = FSSetCatalogInfo(ref, kFSCatInfoFinderInfo, &catalogInfo);
70  if (e == noErr) {
71  DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'");
72  } else {
73  DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e);
74  }
75  }
76  }
77 }
78 
79 
87 static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
88 {
89  int fd;
90  int ret;
91  char magic[4];
92  FSRef fsref;
93  FSSpec fsspec;
94  short refnum = 0;
95  short resid = 0;
96 
97  assert(path != NULL);
98  assert(moov != NULL);
99 
100  DEBUG(driver, 2, "qtmidi: start loading '%s'...", path);
101 
102  /*
103  * XXX Manual check for MIDI header ('MThd'), as I don't know how to make
104  * QuickTime load MIDI files without a .mid suffix without knowing it's
105  * a MIDI file and setting the OSType of the file to the 'Midi' value.
106  * Perhaps ugly, but it seems that it does the Right Thing(tm).
107  */
108  fd = open(path, O_RDONLY, 0);
109  if (fd == -1) return false;
110  ret = read(fd, magic, 4);
111  close(fd);
112  if (ret < 4) return false;
113 
114  DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic);
115  if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') {
116  return false;
117  }
118 
119  if (noErr != FSPathMakeRef((const UInt8 *) path, &fsref, NULL)) return false;
120  SetMIDITypeIfNeeded(&fsref);
121 
122  if (noErr != FSGetCatalogInfo(&fsref, kFSCatInfoNone, NULL, NULL, &fsspec, NULL)) return false;
123  if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false;
124  DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path);
125 
126  if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL,
127  newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) {
128  CloseMovieFile(refnum);
129  return false;
130  }
131  DEBUG(driver, 3, "qtmidi: movie container created");
132 
133  CloseMovieFile(refnum);
134  return true;
135 }
136 
137 
142 static bool _quicktime_started = false;
143 
144 
151 {
152  OSStatus dummy;
153 
154  if (_quicktime_started) return;
155 
156  DEBUG(driver, 2, "qtmidi: initializing Quicktime");
157  /* Be polite: check wether QuickTime is available and initialize it. */
159  (noErr == Gestalt(gestaltQuickTime, &dummy)) &&
160  (noErr == EnterMovies());
161  if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!");
162 }
163 
164 
166 enum QTStates {
170 };
171 
172 
173 static Movie _quicktime_movie;
174 static byte _quicktime_volume = 127;
176 
177 
181 #define VOLUME ((short)((0x00FF & _quicktime_volume) << 1))
182 
183 
191 const char *MusicDriver_QtMidi::Start(const char * const *parm)
192 {
194  return (_quicktime_started) ? NULL : "can't initialize QuickTime";
195 }
196 
197 
205 {
206  if (!_quicktime_started) return true;
207 
208  switch (_quicktime_state) {
209  case QT_STATE_IDLE:
210  case QT_STATE_STOP:
211  /* Do nothing. */
212  break;
213 
214  case QT_STATE_PLAY:
215  MoviesTask(_quicktime_movie, 0);
216  /* Check wether movie ended. */
217  if (IsMovieDone(_quicktime_movie) ||
218  (GetMovieTime(_quicktime_movie, NULL) >=
219  GetMovieDuration(_quicktime_movie))) {
221  }
222  }
223 
225 }
226 
227 
235 {
236  if (!_quicktime_started) return;
237 
238  DEBUG(driver, 2, "qtmidi: stopping driver...");
239  switch (_quicktime_state) {
240  case QT_STATE_IDLE:
241  DEBUG(driver, 3, "qtmidi: stopping not needed, already idle");
242  /* Do nothing. */
243  break;
244 
245  case QT_STATE_PLAY:
246  StopSong();
247  FALLTHROUGH;
248 
249  case QT_STATE_STOP:
250  DisposeMovie(_quicktime_movie);
251  }
252 
253  ExitMovies();
254  _quicktime_started = false;
255 }
256 
257 
264 {
265  if (!_quicktime_started) return;
266 
267  std::string filename = MidiFile::GetSMFFile(song);
268  if (filename.empty()) return;
269 
270  DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename.c_str());
271  switch (_quicktime_state) {
272  case QT_STATE_PLAY:
273  StopSong();
274  DEBUG(driver, 3, "qtmidi: previous tune stopped");
275  FALLTHROUGH;
276 
277  case QT_STATE_STOP:
278  DisposeMovie(_quicktime_movie);
279  DEBUG(driver, 3, "qtmidi: previous tune disposed");
281  FALLTHROUGH;
282 
283  case QT_STATE_IDLE:
284  LoadMovieForMIDIFile(filename.c_str(), &_quicktime_movie);
285  SetMovieVolume(_quicktime_movie, VOLUME);
286  StartMovie(_quicktime_movie);
288  }
289  DEBUG(driver, 3, "qtmidi: playing '%s'", filename.c_str());
290 }
291 
292 
297 {
298  if (!_quicktime_started) return;
299 
300  switch (_quicktime_state) {
301  case QT_STATE_IDLE:
302  FALLTHROUGH;
303 
304  case QT_STATE_STOP:
305  DEBUG(driver, 3, "qtmidi: stop requested, but already idle");
306  /* Do nothing. */
307  break;
308 
309  case QT_STATE_PLAY:
310  StopMovie(_quicktime_movie);
312  DEBUG(driver, 3, "qtmidi: player stopped");
313  }
314 }
315 
316 
327 {
328  if (!_quicktime_started) return;
329 
330  _quicktime_volume = vol;
331 
332  DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME);
333  switch (_quicktime_state) {
334  case QT_STATE_IDLE:
335  /* Do nothing. */
336  break;
337 
338  case QT_STATE_PLAY:
339  case QT_STATE_STOP:
340  SetMovieVolume(_quicktime_movie, VOLUME);
341  }
342 }
343 
344 #endif /* NO_QUICKTIME */
File loaded, stopped.
Definition: qtmidi.cpp:169
Metadata about a music track.
Base of music playback via the QuickTime driver.
bool IsSongPlaying()
Checks whether the player is active.
Definition: qtmidi.cpp:204
static void InitQuickTimeIfNeeded()
Initialize QuickTime if needed.
Definition: qtmidi.cpp:150
void StopSong()
Stops playing the current song, if the player is active.
Definition: qtmidi.cpp:296
static int _quicktime_state
Current player state.
Definition: qtmidi.cpp:175
void Stop()
Stops the MIDI player.
Definition: qtmidi.cpp:234
void SetVolume(byte vol)
Changes the playing volume of the MIDI player.
Definition: qtmidi.cpp:326
static Movie _quicktime_movie
Current QuickTime Movie.
Definition: qtmidi.cpp:173
void PlaySong(const MusicSongInfo &song)
Starts playing a new song.
Definition: qtmidi.cpp:263
static bool LoadMovieForMIDIFile(const char *path, Movie *moov)
Loads a MIDI file and returns it as a QuickTime Movie structure.
Definition: qtmidi.cpp:87
static byte _quicktime_volume
Current volume.
Definition: qtmidi.cpp:174
#define DEBUG(name, level,...)
Output a line of debugging information.
Definition: debug.h:36
No file loaded.
Definition: qtmidi.cpp:167
static std::string GetSMFFile(const MusicSongInfo &song)
Get the name of a Standard MIDI File for a given song.
Definition: midifile.cpp:1041
QTStates
Possible states of the QuickTime music driver.
Definition: qtmidi.cpp:166
static const uint MIDI_TYPE
OSType code for MIDI songs.
Definition: qtmidi.cpp:48
static bool _quicktime_started
Flag which has the true value when QuickTime is available and initialized.
Definition: qtmidi.cpp:142
File loaded, playing.
Definition: qtmidi.cpp:168
static void SetMIDITypeIfNeeded(const FSRef *ref)
Sets the OSType of a given file to &#39;Midi&#39;, but only if it&#39;s not already set.
Definition: qtmidi.cpp:57
const char * Start(const char *const *param)
Initialized the MIDI player, including QuickTime initialization.
Definition: qtmidi.cpp:191
#define VOLUME
Maps OpenTTD volume to QuickTime notion of volume.
Definition: qtmidi.cpp:181