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