From e373ea7096c741a8189f7a480863fd21dd6f6be1 Mon Sep 17 00:00:00 2001 From: KUDr Date: Wed, 10 Jan 2007 18:12:09 +0000 Subject: (svn r8033) [cpp] - Prepare for merge from branches/cpp (all .c files renamed to .cpp) --- src/music/extmidi.c | 108 --------------- src/music/extmidi.cpp | 108 +++++++++++++++ src/music/null_m.c | 21 --- src/music/null_m.cpp | 21 +++ src/music/os2_m.c | 77 ----------- src/music/os2_m.cpp | 77 +++++++++++ src/music/qtmidi.c | 371 -------------------------------------------------- src/music/qtmidi.cpp | 371 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/music/win32_m.c | 170 ----------------------- src/music/win32_m.cpp | 170 +++++++++++++++++++++++ 10 files changed, 747 insertions(+), 747 deletions(-) delete mode 100644 src/music/extmidi.c create mode 100644 src/music/extmidi.cpp delete mode 100644 src/music/null_m.c create mode 100644 src/music/null_m.cpp delete mode 100644 src/music/os2_m.c create mode 100644 src/music/os2_m.cpp delete mode 100644 src/music/qtmidi.c create mode 100644 src/music/qtmidi.cpp delete mode 100644 src/music/win32_m.c create mode 100644 src/music/win32_m.cpp (limited to 'src/music') diff --git a/src/music/extmidi.c b/src/music/extmidi.c deleted file mode 100644 index 901fa4fd7..000000000 --- a/src/music/extmidi.c +++ /dev/null @@ -1,108 +0,0 @@ -/* $Id$ */ - -#ifndef __MORPHOS__ -#include "../stdafx.h" -#include "../openttd.h" -#include "../sound.h" -#include "../string.h" -#include "../variables.h" -#include "../debug.h" -#include "extmidi.h" -#include -#include -#include -#include -#include -#include -#include - -static struct { - char song[MAX_PATH]; - pid_t pid; -} _midi; - -static void DoPlay(void); -static void DoStop(void); - -static const char* ExtMidiStart(const char* const * parm) -{ - _midi.song[0] = '\0'; - _midi.pid = -1; - return NULL; -} - -static void ExtMidiStop(void) -{ - _midi.song[0] = '\0'; - DoStop(); -} - -static void ExtMidiPlaySong(const char* filename) -{ - ttd_strlcpy(_midi.song, filename, lengthof(_midi.song)); - DoStop(); -} - -static void ExtMidiStopSong(void) -{ - _midi.song[0] = '\0'; - DoStop(); -} - -static bool ExtMidiIsPlaying(void) -{ - if (_midi.pid != -1 && waitpid(_midi.pid, NULL, WNOHANG) == _midi.pid) - _midi.pid = -1; - if (_midi.pid == -1 && _midi.song[0] != '\0') DoPlay(); - return _midi.pid != -1; -} - -static void ExtMidiSetVolume(byte vol) -{ - DEBUG(driver, 1, "extmidi: set volume not implemented"); -} - -static void DoPlay(void) -{ - _midi.pid = fork(); - switch (_midi.pid) { - case 0: { - int d; - - close(0); - d = open("/dev/null", O_RDONLY); - if (d != -1 && dup2(d, 1) != -1 && dup2(d, 2) != -1) { - #if defined(MIDI_ARG) - execlp(msf.extmidi, "extmidi", MIDI_ARG, _midi.song, (char*)0); - #else - execlp(msf.extmidi, "extmidi", _midi.song, (char*)0); - #endif - } - _exit(1); - } - - case -1: - DEBUG(driver, 0, "extmidi: couldn't fork: %s", strerror(errno)); - /* FALLTHROUGH */ - - default: - _midi.song[0] = '\0'; - break; - } -} - -static void DoStop(void) -{ - if (_midi.pid != -1) kill(_midi.pid, SIGTERM); -} - -const HalMusicDriver _extmidi_music_driver = { - ExtMidiStart, - ExtMidiStop, - ExtMidiPlaySong, - ExtMidiStopSong, - ExtMidiIsPlaying, - ExtMidiSetVolume, -}; - -#endif /* __MORPHOS__ */ diff --git a/src/music/extmidi.cpp b/src/music/extmidi.cpp new file mode 100644 index 000000000..901fa4fd7 --- /dev/null +++ b/src/music/extmidi.cpp @@ -0,0 +1,108 @@ +/* $Id$ */ + +#ifndef __MORPHOS__ +#include "../stdafx.h" +#include "../openttd.h" +#include "../sound.h" +#include "../string.h" +#include "../variables.h" +#include "../debug.h" +#include "extmidi.h" +#include +#include +#include +#include +#include +#include +#include + +static struct { + char song[MAX_PATH]; + pid_t pid; +} _midi; + +static void DoPlay(void); +static void DoStop(void); + +static const char* ExtMidiStart(const char* const * parm) +{ + _midi.song[0] = '\0'; + _midi.pid = -1; + return NULL; +} + +static void ExtMidiStop(void) +{ + _midi.song[0] = '\0'; + DoStop(); +} + +static void ExtMidiPlaySong(const char* filename) +{ + ttd_strlcpy(_midi.song, filename, lengthof(_midi.song)); + DoStop(); +} + +static void ExtMidiStopSong(void) +{ + _midi.song[0] = '\0'; + DoStop(); +} + +static bool ExtMidiIsPlaying(void) +{ + if (_midi.pid != -1 && waitpid(_midi.pid, NULL, WNOHANG) == _midi.pid) + _midi.pid = -1; + if (_midi.pid == -1 && _midi.song[0] != '\0') DoPlay(); + return _midi.pid != -1; +} + +static void ExtMidiSetVolume(byte vol) +{ + DEBUG(driver, 1, "extmidi: set volume not implemented"); +} + +static void DoPlay(void) +{ + _midi.pid = fork(); + switch (_midi.pid) { + case 0: { + int d; + + close(0); + d = open("/dev/null", O_RDONLY); + if (d != -1 && dup2(d, 1) != -1 && dup2(d, 2) != -1) { + #if defined(MIDI_ARG) + execlp(msf.extmidi, "extmidi", MIDI_ARG, _midi.song, (char*)0); + #else + execlp(msf.extmidi, "extmidi", _midi.song, (char*)0); + #endif + } + _exit(1); + } + + case -1: + DEBUG(driver, 0, "extmidi: couldn't fork: %s", strerror(errno)); + /* FALLTHROUGH */ + + default: + _midi.song[0] = '\0'; + break; + } +} + +static void DoStop(void) +{ + if (_midi.pid != -1) kill(_midi.pid, SIGTERM); +} + +const HalMusicDriver _extmidi_music_driver = { + ExtMidiStart, + ExtMidiStop, + ExtMidiPlaySong, + ExtMidiStopSong, + ExtMidiIsPlaying, + ExtMidiSetVolume, +}; + +#endif /* __MORPHOS__ */ diff --git a/src/music/null_m.c b/src/music/null_m.c deleted file mode 100644 index c76a60211..000000000 --- a/src/music/null_m.c +++ /dev/null @@ -1,21 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "null_m.h" - -static const char* NullMidiStart(const char* const* parm) { return NULL; } -static void NullMidiStop(void) {} -static void NullMidiPlaySong(const char *filename) {} -static void NullMidiStopSong(void) {} -static bool NullMidiIsSongPlaying(void) { return true; } -static void NullMidiSetVolume(byte vol) {} - -const HalMusicDriver _null_music_driver = { - NullMidiStart, - NullMidiStop, - NullMidiPlaySong, - NullMidiStopSong, - NullMidiIsSongPlaying, - NullMidiSetVolume, -}; diff --git a/src/music/null_m.cpp b/src/music/null_m.cpp new file mode 100644 index 000000000..c76a60211 --- /dev/null +++ b/src/music/null_m.cpp @@ -0,0 +1,21 @@ +/* $Id$ */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "null_m.h" + +static const char* NullMidiStart(const char* const* parm) { return NULL; } +static void NullMidiStop(void) {} +static void NullMidiPlaySong(const char *filename) {} +static void NullMidiStopSong(void) {} +static bool NullMidiIsSongPlaying(void) { return true; } +static void NullMidiSetVolume(byte vol) {} + +const HalMusicDriver _null_music_driver = { + NullMidiStart, + NullMidiStop, + NullMidiPlaySong, + NullMidiStopSong, + NullMidiIsSongPlaying, + NullMidiSetVolume, +}; diff --git a/src/music/os2_m.c b/src/music/os2_m.c deleted file mode 100644 index 75df60c8d..000000000 --- a/src/music/os2_m.c +++ /dev/null @@ -1,77 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "os2_m.h" - -#define INCL_DOS -#define INCL_OS2MM -#define INCL_WIN - -#include -#include -#include - -/********************** - * OS/2 MIDI PLAYER - **********************/ - -/* Interesting how similar the MCI API in OS/2 is to the Win32 MCI API, - * eh? Anyone would think they both came from the same place originally! ;) - */ - -static long CDECL MidiSendCommand(const char *cmd, ...) -{ - va_list va; - char buf[512]; - va_start(va, cmd); - vsprintf(buf, cmd, va); - va_end(va); - return mciSendString(buf, NULL, 0, NULL, 0); -} - -static void OS2MidiPlaySong(const char *filename) -{ - MidiSendCommand("close all"); - - if (MidiSendCommand("open %s type sequencer alias song", filename) != 0) - return; - - MidiSendCommand("play song from 0"); -} - -static void OS2MidiStopSong(void) -{ - MidiSendCommand("close all"); -} - -static void OS2MidiSetVolume(byte vol) -{ - MidiSendCommand("set song audio volume %d", ((vol/127)*100)); -} - -static bool OS2MidiIsSongPlaying(void) -{ - char buf[16]; - mciSendString("status song mode", buf, sizeof(buf), NULL, 0); - return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0; -} - -static const char *OS2MidiStart(const char * const *parm) -{ - return 0; -} - -static void OS2MidiStop(void) -{ - MidiSendCommand("close all"); -} - -const HalMusicDriver _os2_music_driver = { - OS2MidiStart, - OS2MidiStop, - OS2MidiPlaySong, - OS2MidiStopSong, - OS2MidiIsSongPlaying, - OS2MidiSetVolume, -}; diff --git a/src/music/os2_m.cpp b/src/music/os2_m.cpp new file mode 100644 index 000000000..75df60c8d --- /dev/null +++ b/src/music/os2_m.cpp @@ -0,0 +1,77 @@ +/* $Id$ */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "os2_m.h" + +#define INCL_DOS +#define INCL_OS2MM +#define INCL_WIN + +#include +#include +#include + +/********************** + * OS/2 MIDI PLAYER + **********************/ + +/* Interesting how similar the MCI API in OS/2 is to the Win32 MCI API, + * eh? Anyone would think they both came from the same place originally! ;) + */ + +static long CDECL MidiSendCommand(const char *cmd, ...) +{ + va_list va; + char buf[512]; + va_start(va, cmd); + vsprintf(buf, cmd, va); + va_end(va); + return mciSendString(buf, NULL, 0, NULL, 0); +} + +static void OS2MidiPlaySong(const char *filename) +{ + MidiSendCommand("close all"); + + if (MidiSendCommand("open %s type sequencer alias song", filename) != 0) + return; + + MidiSendCommand("play song from 0"); +} + +static void OS2MidiStopSong(void) +{ + MidiSendCommand("close all"); +} + +static void OS2MidiSetVolume(byte vol) +{ + MidiSendCommand("set song audio volume %d", ((vol/127)*100)); +} + +static bool OS2MidiIsSongPlaying(void) +{ + char buf[16]; + mciSendString("status song mode", buf, sizeof(buf), NULL, 0); + return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0; +} + +static const char *OS2MidiStart(const char * const *parm) +{ + return 0; +} + +static void OS2MidiStop(void) +{ + MidiSendCommand("close all"); +} + +const HalMusicDriver _os2_music_driver = { + OS2MidiStart, + OS2MidiStop, + OS2MidiPlaySong, + OS2MidiStopSong, + OS2MidiIsSongPlaying, + OS2MidiSetVolume, +}; diff --git a/src/music/qtmidi.c b/src/music/qtmidi.c deleted file mode 100644 index 6f0a215d0..000000000 --- a/src/music/qtmidi.c +++ /dev/null @@ -1,371 +0,0 @@ -/* $Id$ */ - -/** - * @file qtmidi.c - * @brief MIDI music player for MacOS X using QuickTime. - * - * This music player should work in all MacOS X releases starting from 10.0, - * as QuickTime is an integral part of the system since the old days of the - * Motorola 68k-based Macintoshes. The only extra dependency apart from - * QuickTime itself is Carbon, which is included since 10.0 as well. - * - * QuickTime gets fooled with the MIDI files from Transport Tycoon Deluxe - * because of the @c .gm suffix. To force QuickTime to load the MIDI files - * without the need of dealing with the individual QuickTime components - * needed to play music (data source, MIDI parser, note allocators, - * synthesizers and the like) some Carbon functions are used to set the file - * type as seen by QuickTime, using @c FSpSetFInfo() (which modifies the - * file's resource fork). - */ - - -/* - * OpenTTD includes. - */ -#define WindowClass OSX_WindowClass -#include -#undef WindowClass - -#include "../stdafx.h" -#include "../openttd.h" -#include "qtmidi.h" - -/* - * System includes. We need to workaround with some defines because there's - * stuff already defined in QuickTime headers. - */ -#define OTTD_Random OSX_OTTD_Random -#undef OTTD_Random -#undef WindowClass -#undef SL_ERROR -#undef bool - -#include -#include -#include - -// we need to include debug.h after CoreServices because defining DEBUG will break CoreServices in OSX 10.2 -#include "../debug.h" - - -enum { - midiType = 'Midi' /**< OSType code for MIDI songs. */ -}; - - -/** - * Converts a Unix-like pathname to a @c FSSpec structure which may be - * used with functions from several MacOS X frameworks (Carbon, QuickTime, - * etc). The pointed file or directory must exist. - * - * @param *path A string containing a Unix-like path. - * @param *spec Pointer to a @c FSSpec structure where the result will be - * stored. - * @return Wether the conversion was successful. - */ -static bool PathToFSSpec(const char *path, FSSpec *spec) -{ - FSRef ref; - assert(spec != NULL); - assert(path != NULL); - - return - FSPathMakeRef((UInt8*)path, &ref, NULL) == noErr && - FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL) == noErr; -} - - -/** - * Sets the @c OSType of a given file to @c 'Midi', but only if it's not - * already set. - * - * @param *spec A @c FSSpec structure referencing a file. - */ -static void SetMIDITypeIfNeeded(const FSSpec *spec) -{ - FSRef ref; - FSCatalogInfo catalogInfo; - - assert(spec); - - if (noErr != FSpMakeFSRef(spec, &ref)) return; - if (noErr != FSGetCatalogInfo(&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return; - if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) { - FileInfo * const info = (FileInfo *) catalogInfo.finderInfo; - if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) { - OSErr e; - info->fileType = midiType; - e = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo); - if (e == noErr) { - DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'"); - } else { - DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e); - } - } - } -} - - -/** - * Loads a MIDI file and returns it as a QuickTime Movie structure. - * - * @param *path String with the path of an existing MIDI file. - * @param *moov Pointer to a @c Movie where the result will be stored. - * @return Wether the file was loaded and the @c Movie successfully created. - */ -static bool LoadMovieForMIDIFile(const char *path, Movie *moov) -{ - int fd; - int ret; - char magic[4]; - FSSpec fsspec; - short refnum = 0; - short resid = 0; - - assert(path != NULL); - assert(moov != NULL); - - DEBUG(driver, 2, "qtmidi: start loading '%s'...", path); - - /* - * XXX Manual check for MIDI header ('MThd'), as I don't know how to make - * QuickTime load MIDI files without a .mid suffix without knowing it's - * a MIDI file and setting the OSType of the file to the 'Midi' value. - * Perhahaps ugly, but it seems that it does the Right Thing(tm). - */ - fd = open(path, O_RDONLY, 0); - if (fd == -1) return false; - ret = read(fd, magic, 4); - close(fd); - if (ret < 4) return false; - - DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic); - if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') - return false; - - if (!PathToFSSpec(path, &fsspec)) return false; - SetMIDITypeIfNeeded(&fsspec); - - if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false; - DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path); - - if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL, - newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) { - CloseMovieFile(refnum); - return false; - } - DEBUG(driver, 3, "qtmidi: movie container created"); - - CloseMovieFile(refnum); - return true; -} - - -/** - * Flag which has the @c true value when QuickTime is available and - * initialized. - */ -static bool _quicktime_started = false; - - -/** - * Initialize QuickTime if needed. This function sets the - * #_quicktime_started flag to @c true if QuickTime is present in the system - * and it was initialized properly. - */ -static void InitQuickTimeIfNeeded(void) -{ - OSStatus dummy; - - if (_quicktime_started) return; - - DEBUG(driver, 2, "qtmidi: initializing Quicktime"); - /* Be polite: check wether QuickTime is available and initialize it. */ - _quicktime_started = - (noErr == Gestalt(gestaltQuickTime, &dummy)) && - (noErr == EnterMovies()); - if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!"); -} - - -/** Possible states of the QuickTime music driver. */ -enum { - QT_STATE_IDLE, /**< No file loaded. */ - QT_STATE_PLAY, /**< File loaded, playing. */ - QT_STATE_STOP, /**< File loaded, stopped. */ -}; - - -static Movie _quicktime_movie; /**< Current QuickTime @c Movie. */ -static byte _quicktime_volume = 127; /**< Current volume. */ -static int _quicktime_state = QT_STATE_IDLE; /**< Current player state. */ - - -/** - * Maps OpenTTD volume to QuickTime notion of volume. - */ -#define VOLUME ((short)((0x00FF & _quicktime_volume) << 1)) - - -static void StopSong(void); - - -/** - * Initialized the MIDI player, including QuickTime initialization. - * - * @todo Give better error messages by inspecting error codes returned by - * @c Gestalt() and @c EnterMovies(). Needs changes in - * #InitQuickTimeIfNeeded. - */ -static const char* StartDriver(const char * const *parm) -{ - InitQuickTimeIfNeeded(); - return (_quicktime_started) ? NULL : "can't initialize QuickTime"; -} - - -/** - * Checks wether the player is active. - * - * This function is called at regular intervals from OpenTTD's main loop, so - * we call @c MoviesTask() from here to let QuickTime do its work. - */ -static bool SongIsPlaying(void) -{ - if (!_quicktime_started) return true; - - switch (_quicktime_state) { - case QT_STATE_IDLE: - case QT_STATE_STOP: - /* Do nothing. */ - break; - case QT_STATE_PLAY: - MoviesTask(_quicktime_movie, 0); - /* Check wether movie ended. */ - if (IsMovieDone(_quicktime_movie) || - (GetMovieTime(_quicktime_movie, NULL) >= - GetMovieDuration(_quicktime_movie))) - _quicktime_state = QT_STATE_STOP; - } - - return _quicktime_state == QT_STATE_PLAY; -} - - -/** - * Stops the MIDI player. - * - * Stops playing and frees any used resources before returning. As it - * deinitilizes QuickTime, the #_quicktime_started flag is set to @c false. - */ -static void StopDriver(void) -{ - if (!_quicktime_started) return; - - DEBUG(driver, 2, "qtmidi: stopping driver..."); - switch (_quicktime_state) { - case QT_STATE_IDLE: - DEBUG(driver, 3, "qtmidi: stopping not needed, already idle"); - /* Do nothing. */ - break; - case QT_STATE_PLAY: - StopSong(); - case QT_STATE_STOP: - DisposeMovie(_quicktime_movie); - } - - ExitMovies(); - _quicktime_started = false; -} - - -/** - * Starts playing a new song. - * - * @param filename Path to a MIDI file. - */ -static void PlaySong(const char *filename) -{ - if (!_quicktime_started) return; - - DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename); - switch (_quicktime_state) { - case QT_STATE_PLAY: - StopSong(); - DEBUG(driver, 3, "qtmidi: previous tune stopped"); - /* XXX Fall-through -- no break needed. */ - case QT_STATE_STOP: - DisposeMovie(_quicktime_movie); - DEBUG(driver, 3, "qtmidi: previous tune disposed"); - _quicktime_state = QT_STATE_IDLE; - /* XXX Fall-through -- no break needed. */ - case QT_STATE_IDLE: - LoadMovieForMIDIFile(filename, &_quicktime_movie); - SetMovieVolume(_quicktime_movie, VOLUME); - StartMovie(_quicktime_movie); - _quicktime_state = QT_STATE_PLAY; - } - DEBUG(driver, 3, "qtmidi: playing '%s'", filename); -} - - -/** - * Stops playing the current song, if the player is active. - */ -static void StopSong(void) -{ - if (!_quicktime_started) return; - - switch (_quicktime_state) { - case QT_STATE_IDLE: - /* XXX Fall-through -- no break needed. */ - case QT_STATE_STOP: - DEBUG(driver, 3, "qtmidi: stop requested, but already idle"); - /* Do nothing. */ - break; - case QT_STATE_PLAY: - StopMovie(_quicktime_movie); - _quicktime_state = QT_STATE_STOP; - DEBUG(driver, 3, "qtmidi: player stopped"); - } -} - - -/** - * Changes the playing volume of the MIDI player. - * - * As QuickTime controls volume in a per-movie basis, the desired volume is - * stored in #_quicktime_volume, and the volume is set here using the - * #VOLUME macro, @b and when loading new song in #PlaySong. - * - * @param vol The desired volume, range of the value is @c 0-127 - */ -static void SetVolume(byte vol) -{ - if (!_quicktime_started) return; - - _quicktime_volume = vol; - - DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME); - switch (_quicktime_state) { - case QT_STATE_IDLE: - /* Do nothing. */ - break; - case QT_STATE_PLAY: - case QT_STATE_STOP: - SetMovieVolume(_quicktime_movie, VOLUME); - } -} - - -/** - * Table of callbacks that implement the QuickTime MIDI player. - */ -const HalMusicDriver _qtime_music_driver = { - StartDriver, - StopDriver, - PlaySong, - StopSong, - SongIsPlaying, - SetVolume, -}; diff --git a/src/music/qtmidi.cpp b/src/music/qtmidi.cpp new file mode 100644 index 000000000..6f0a215d0 --- /dev/null +++ b/src/music/qtmidi.cpp @@ -0,0 +1,371 @@ +/* $Id$ */ + +/** + * @file qtmidi.c + * @brief MIDI music player for MacOS X using QuickTime. + * + * This music player should work in all MacOS X releases starting from 10.0, + * as QuickTime is an integral part of the system since the old days of the + * Motorola 68k-based Macintoshes. The only extra dependency apart from + * QuickTime itself is Carbon, which is included since 10.0 as well. + * + * QuickTime gets fooled with the MIDI files from Transport Tycoon Deluxe + * because of the @c .gm suffix. To force QuickTime to load the MIDI files + * without the need of dealing with the individual QuickTime components + * needed to play music (data source, MIDI parser, note allocators, + * synthesizers and the like) some Carbon functions are used to set the file + * type as seen by QuickTime, using @c FSpSetFInfo() (which modifies the + * file's resource fork). + */ + + +/* + * OpenTTD includes. + */ +#define WindowClass OSX_WindowClass +#include +#undef WindowClass + +#include "../stdafx.h" +#include "../openttd.h" +#include "qtmidi.h" + +/* + * System includes. We need to workaround with some defines because there's + * stuff already defined in QuickTime headers. + */ +#define OTTD_Random OSX_OTTD_Random +#undef OTTD_Random +#undef WindowClass +#undef SL_ERROR +#undef bool + +#include +#include +#include + +// we need to include debug.h after CoreServices because defining DEBUG will break CoreServices in OSX 10.2 +#include "../debug.h" + + +enum { + midiType = 'Midi' /**< OSType code for MIDI songs. */ +}; + + +/** + * Converts a Unix-like pathname to a @c FSSpec structure which may be + * used with functions from several MacOS X frameworks (Carbon, QuickTime, + * etc). The pointed file or directory must exist. + * + * @param *path A string containing a Unix-like path. + * @param *spec Pointer to a @c FSSpec structure where the result will be + * stored. + * @return Wether the conversion was successful. + */ +static bool PathToFSSpec(const char *path, FSSpec *spec) +{ + FSRef ref; + assert(spec != NULL); + assert(path != NULL); + + return + FSPathMakeRef((UInt8*)path, &ref, NULL) == noErr && + FSGetCatalogInfo(&ref, kFSCatInfoNone, NULL, NULL, spec, NULL) == noErr; +} + + +/** + * Sets the @c OSType of a given file to @c 'Midi', but only if it's not + * already set. + * + * @param *spec A @c FSSpec structure referencing a file. + */ +static void SetMIDITypeIfNeeded(const FSSpec *spec) +{ + FSRef ref; + FSCatalogInfo catalogInfo; + + assert(spec); + + if (noErr != FSpMakeFSRef(spec, &ref)) return; + if (noErr != FSGetCatalogInfo(&ref, kFSCatInfoNodeFlags | kFSCatInfoFinderInfo, &catalogInfo, NULL, NULL, NULL)) return; + if (!(catalogInfo.nodeFlags & kFSNodeIsDirectoryMask)) { + FileInfo * const info = (FileInfo *) catalogInfo.finderInfo; + if (info->fileType != midiType && !(info->finderFlags & kIsAlias)) { + OSErr e; + info->fileType = midiType; + e = FSSetCatalogInfo(&ref, kFSCatInfoFinderInfo, &catalogInfo); + if (e == noErr) { + DEBUG(driver, 3, "qtmidi: changed filetype to 'Midi'"); + } else { + DEBUG(driver, 0, "qtmidi: changing filetype to 'Midi' failed - error %d", e); + } + } + } +} + + +/** + * Loads a MIDI file and returns it as a QuickTime Movie structure. + * + * @param *path String with the path of an existing MIDI file. + * @param *moov Pointer to a @c Movie where the result will be stored. + * @return Wether the file was loaded and the @c Movie successfully created. + */ +static bool LoadMovieForMIDIFile(const char *path, Movie *moov) +{ + int fd; + int ret; + char magic[4]; + FSSpec fsspec; + short refnum = 0; + short resid = 0; + + assert(path != NULL); + assert(moov != NULL); + + DEBUG(driver, 2, "qtmidi: start loading '%s'...", path); + + /* + * XXX Manual check for MIDI header ('MThd'), as I don't know how to make + * QuickTime load MIDI files without a .mid suffix without knowing it's + * a MIDI file and setting the OSType of the file to the 'Midi' value. + * Perhahaps ugly, but it seems that it does the Right Thing(tm). + */ + fd = open(path, O_RDONLY, 0); + if (fd == -1) return false; + ret = read(fd, magic, 4); + close(fd); + if (ret < 4) return false; + + DEBUG(driver, 3, "qtmidi: header is '%.4s'", magic); + if (magic[0] != 'M' || magic[1] != 'T' || magic[2] != 'h' || magic[3] != 'd') + return false; + + if (!PathToFSSpec(path, &fsspec)) return false; + SetMIDITypeIfNeeded(&fsspec); + + if (OpenMovieFile(&fsspec, &refnum, fsRdPerm) != noErr) return false; + DEBUG(driver, 3, "qtmidi: '%s' successfully opened", path); + + if (noErr != NewMovieFromFile(moov, refnum, &resid, NULL, + newMovieActive | newMovieDontAskUnresolvedDataRefs, NULL)) { + CloseMovieFile(refnum); + return false; + } + DEBUG(driver, 3, "qtmidi: movie container created"); + + CloseMovieFile(refnum); + return true; +} + + +/** + * Flag which has the @c true value when QuickTime is available and + * initialized. + */ +static bool _quicktime_started = false; + + +/** + * Initialize QuickTime if needed. This function sets the + * #_quicktime_started flag to @c true if QuickTime is present in the system + * and it was initialized properly. + */ +static void InitQuickTimeIfNeeded(void) +{ + OSStatus dummy; + + if (_quicktime_started) return; + + DEBUG(driver, 2, "qtmidi: initializing Quicktime"); + /* Be polite: check wether QuickTime is available and initialize it. */ + _quicktime_started = + (noErr == Gestalt(gestaltQuickTime, &dummy)) && + (noErr == EnterMovies()); + if (!_quicktime_started) DEBUG(driver, 0, "qtmidi: Quicktime initialization failed!"); +} + + +/** Possible states of the QuickTime music driver. */ +enum { + QT_STATE_IDLE, /**< No file loaded. */ + QT_STATE_PLAY, /**< File loaded, playing. */ + QT_STATE_STOP, /**< File loaded, stopped. */ +}; + + +static Movie _quicktime_movie; /**< Current QuickTime @c Movie. */ +static byte _quicktime_volume = 127; /**< Current volume. */ +static int _quicktime_state = QT_STATE_IDLE; /**< Current player state. */ + + +/** + * Maps OpenTTD volume to QuickTime notion of volume. + */ +#define VOLUME ((short)((0x00FF & _quicktime_volume) << 1)) + + +static void StopSong(void); + + +/** + * Initialized the MIDI player, including QuickTime initialization. + * + * @todo Give better error messages by inspecting error codes returned by + * @c Gestalt() and @c EnterMovies(). Needs changes in + * #InitQuickTimeIfNeeded. + */ +static const char* StartDriver(const char * const *parm) +{ + InitQuickTimeIfNeeded(); + return (_quicktime_started) ? NULL : "can't initialize QuickTime"; +} + + +/** + * Checks wether the player is active. + * + * This function is called at regular intervals from OpenTTD's main loop, so + * we call @c MoviesTask() from here to let QuickTime do its work. + */ +static bool SongIsPlaying(void) +{ + if (!_quicktime_started) return true; + + switch (_quicktime_state) { + case QT_STATE_IDLE: + case QT_STATE_STOP: + /* Do nothing. */ + break; + case QT_STATE_PLAY: + MoviesTask(_quicktime_movie, 0); + /* Check wether movie ended. */ + if (IsMovieDone(_quicktime_movie) || + (GetMovieTime(_quicktime_movie, NULL) >= + GetMovieDuration(_quicktime_movie))) + _quicktime_state = QT_STATE_STOP; + } + + return _quicktime_state == QT_STATE_PLAY; +} + + +/** + * Stops the MIDI player. + * + * Stops playing and frees any used resources before returning. As it + * deinitilizes QuickTime, the #_quicktime_started flag is set to @c false. + */ +static void StopDriver(void) +{ + if (!_quicktime_started) return; + + DEBUG(driver, 2, "qtmidi: stopping driver..."); + switch (_quicktime_state) { + case QT_STATE_IDLE: + DEBUG(driver, 3, "qtmidi: stopping not needed, already idle"); + /* Do nothing. */ + break; + case QT_STATE_PLAY: + StopSong(); + case QT_STATE_STOP: + DisposeMovie(_quicktime_movie); + } + + ExitMovies(); + _quicktime_started = false; +} + + +/** + * Starts playing a new song. + * + * @param filename Path to a MIDI file. + */ +static void PlaySong(const char *filename) +{ + if (!_quicktime_started) return; + + DEBUG(driver, 2, "qtmidi: trying to play '%s'", filename); + switch (_quicktime_state) { + case QT_STATE_PLAY: + StopSong(); + DEBUG(driver, 3, "qtmidi: previous tune stopped"); + /* XXX Fall-through -- no break needed. */ + case QT_STATE_STOP: + DisposeMovie(_quicktime_movie); + DEBUG(driver, 3, "qtmidi: previous tune disposed"); + _quicktime_state = QT_STATE_IDLE; + /* XXX Fall-through -- no break needed. */ + case QT_STATE_IDLE: + LoadMovieForMIDIFile(filename, &_quicktime_movie); + SetMovieVolume(_quicktime_movie, VOLUME); + StartMovie(_quicktime_movie); + _quicktime_state = QT_STATE_PLAY; + } + DEBUG(driver, 3, "qtmidi: playing '%s'", filename); +} + + +/** + * Stops playing the current song, if the player is active. + */ +static void StopSong(void) +{ + if (!_quicktime_started) return; + + switch (_quicktime_state) { + case QT_STATE_IDLE: + /* XXX Fall-through -- no break needed. */ + case QT_STATE_STOP: + DEBUG(driver, 3, "qtmidi: stop requested, but already idle"); + /* Do nothing. */ + break; + case QT_STATE_PLAY: + StopMovie(_quicktime_movie); + _quicktime_state = QT_STATE_STOP; + DEBUG(driver, 3, "qtmidi: player stopped"); + } +} + + +/** + * Changes the playing volume of the MIDI player. + * + * As QuickTime controls volume in a per-movie basis, the desired volume is + * stored in #_quicktime_volume, and the volume is set here using the + * #VOLUME macro, @b and when loading new song in #PlaySong. + * + * @param vol The desired volume, range of the value is @c 0-127 + */ +static void SetVolume(byte vol) +{ + if (!_quicktime_started) return; + + _quicktime_volume = vol; + + DEBUG(driver, 2, "qtmidi: set volume to %u (%hi)", vol, VOLUME); + switch (_quicktime_state) { + case QT_STATE_IDLE: + /* Do nothing. */ + break; + case QT_STATE_PLAY: + case QT_STATE_STOP: + SetMovieVolume(_quicktime_movie, VOLUME); + } +} + + +/** + * Table of callbacks that implement the QuickTime MIDI player. + */ +const HalMusicDriver _qtime_music_driver = { + StartDriver, + StopDriver, + PlaySong, + StopSong, + SongIsPlaying, + SetVolume, +}; diff --git a/src/music/win32_m.c b/src/music/win32_m.c deleted file mode 100644 index 36b79b30b..000000000 --- a/src/music/win32_m.c +++ /dev/null @@ -1,170 +0,0 @@ -/* $Id$ */ - -#include "../stdafx.h" -#include "../openttd.h" -#include "win32_m.h" -#include -#include - -static struct { - bool stop_song; - bool terminate; - bool playing; - int new_vol; - HANDLE wait_obj; - UINT_PTR devid; - char start_song[260]; -} _midi; - -static void Win32MidiPlaySong(const char *filename) -{ - strcpy(_midi.start_song, filename); - _midi.playing = true; - _midi.stop_song = false; - SetEvent(_midi.wait_obj); -} - -static void Win32MidiStopSong(void) -{ - if (_midi.playing) { - _midi.stop_song = true; - _midi.start_song[0] = '\0'; - SetEvent(_midi.wait_obj); - } -} - -static bool Win32MidiIsSongPlaying(void) -{ - return _midi.playing; -} - -static void Win32MidiSetVolume(byte vol) -{ - _midi.new_vol = vol; - SetEvent(_midi.wait_obj); -} - -static MCIERROR CDECL MidiSendCommand(const char* cmd, ...) -{ - va_list va; - char buf[512]; - - va_start(va, cmd); - vsprintf(buf, cmd, va); - va_end(va); - return mciSendStringA(buf, NULL, 0, 0); -} - -static bool MidiIntPlaySong(const char *filename) -{ - MidiSendCommand("close all"); - if (MidiSendCommand("open \"%s\" type sequencer alias song", filename) != 0) - return false; - - if (MidiSendCommand("play song from 0") != 0) - return false; - return true; -} - -static void MidiIntStopSong(void) -{ - MidiSendCommand("close all"); -} - -static void MidiIntSetVolume(int vol) -{ - DWORD v = (vol * 65535 / 127); - midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16)); -} - -static bool MidiIntIsSongPlaying(void) -{ - char buf[16]; - mciSendStringA("status song mode", buf, sizeof(buf), 0); - return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0; -} - -static DWORD WINAPI MidiThread(LPVOID arg) -{ - _midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL); - - do { - char *s; - int vol; - - vol = _midi.new_vol; - if (vol != -1) { - _midi.new_vol = -1; - MidiIntSetVolume(vol); - } - - s = _midi.start_song; - if (s[0] != '\0') { - _midi.playing = MidiIntPlaySong(s); - s[0] = '\0'; - - // Delay somewhat in case we don't manage to play. - if (!_midi.playing) { - Sleep(5000); - } - } - - if (_midi.stop_song && _midi.playing) { - _midi.stop_song = false; - _midi.playing = false; - MidiIntStopSong(); - } - - if (_midi.playing && !MidiIntIsSongPlaying()) - _midi.playing = false; - - WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000); - } while (!_midi.terminate); - - DeleteObject(_midi.wait_obj); - return 0; -} - -static const char *Win32MidiStart(const char * const *parm) -{ - MIDIOUTCAPS midicaps; - DWORD threadId; - UINT nbdev; - UINT_PTR dev; - char buf[16]; - - mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0); - if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio"; - - memset(&_midi, 0, sizeof(_midi)); - _midi.new_vol = -1; - - /* Get midi device */ - _midi.devid = MIDI_MAPPER; - for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) { - if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) { - _midi.devid = dev; - break; - } - } - - if (CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId) == NULL) - return "Failed to create thread"; - - return NULL; -} - -static void Win32MidiStop(void) -{ - _midi.terminate = true; - SetEvent(_midi.wait_obj); -} - -const HalMusicDriver _win32_music_driver = { - Win32MidiStart, - Win32MidiStop, - Win32MidiPlaySong, - Win32MidiStopSong, - Win32MidiIsSongPlaying, - Win32MidiSetVolume, -}; diff --git a/src/music/win32_m.cpp b/src/music/win32_m.cpp new file mode 100644 index 000000000..36b79b30b --- /dev/null +++ b/src/music/win32_m.cpp @@ -0,0 +1,170 @@ +/* $Id$ */ + +#include "../stdafx.h" +#include "../openttd.h" +#include "win32_m.h" +#include +#include + +static struct { + bool stop_song; + bool terminate; + bool playing; + int new_vol; + HANDLE wait_obj; + UINT_PTR devid; + char start_song[260]; +} _midi; + +static void Win32MidiPlaySong(const char *filename) +{ + strcpy(_midi.start_song, filename); + _midi.playing = true; + _midi.stop_song = false; + SetEvent(_midi.wait_obj); +} + +static void Win32MidiStopSong(void) +{ + if (_midi.playing) { + _midi.stop_song = true; + _midi.start_song[0] = '\0'; + SetEvent(_midi.wait_obj); + } +} + +static bool Win32MidiIsSongPlaying(void) +{ + return _midi.playing; +} + +static void Win32MidiSetVolume(byte vol) +{ + _midi.new_vol = vol; + SetEvent(_midi.wait_obj); +} + +static MCIERROR CDECL MidiSendCommand(const char* cmd, ...) +{ + va_list va; + char buf[512]; + + va_start(va, cmd); + vsprintf(buf, cmd, va); + va_end(va); + return mciSendStringA(buf, NULL, 0, 0); +} + +static bool MidiIntPlaySong(const char *filename) +{ + MidiSendCommand("close all"); + if (MidiSendCommand("open \"%s\" type sequencer alias song", filename) != 0) + return false; + + if (MidiSendCommand("play song from 0") != 0) + return false; + return true; +} + +static void MidiIntStopSong(void) +{ + MidiSendCommand("close all"); +} + +static void MidiIntSetVolume(int vol) +{ + DWORD v = (vol * 65535 / 127); + midiOutSetVolume((HMIDIOUT)_midi.devid, v + (v << 16)); +} + +static bool MidiIntIsSongPlaying(void) +{ + char buf[16]; + mciSendStringA("status song mode", buf, sizeof(buf), 0); + return strcmp(buf, "playing") == 0 || strcmp(buf, "seeking") == 0; +} + +static DWORD WINAPI MidiThread(LPVOID arg) +{ + _midi.wait_obj = CreateEvent(NULL, FALSE, FALSE, NULL); + + do { + char *s; + int vol; + + vol = _midi.new_vol; + if (vol != -1) { + _midi.new_vol = -1; + MidiIntSetVolume(vol); + } + + s = _midi.start_song; + if (s[0] != '\0') { + _midi.playing = MidiIntPlaySong(s); + s[0] = '\0'; + + // Delay somewhat in case we don't manage to play. + if (!_midi.playing) { + Sleep(5000); + } + } + + if (_midi.stop_song && _midi.playing) { + _midi.stop_song = false; + _midi.playing = false; + MidiIntStopSong(); + } + + if (_midi.playing && !MidiIntIsSongPlaying()) + _midi.playing = false; + + WaitForMultipleObjects(1, &_midi.wait_obj, FALSE, 1000); + } while (!_midi.terminate); + + DeleteObject(_midi.wait_obj); + return 0; +} + +static const char *Win32MidiStart(const char * const *parm) +{ + MIDIOUTCAPS midicaps; + DWORD threadId; + UINT nbdev; + UINT_PTR dev; + char buf[16]; + + mciSendStringA("capability sequencer has audio", buf, lengthof(buf), 0); + if (strcmp(buf, "true") != 0) return "MCI sequencer can't play audio"; + + memset(&_midi, 0, sizeof(_midi)); + _midi.new_vol = -1; + + /* Get midi device */ + _midi.devid = MIDI_MAPPER; + for (dev = 0, nbdev = midiOutGetNumDevs(); dev < nbdev; dev++) { + if (midiOutGetDevCaps(dev, &midicaps, sizeof(midicaps)) == 0 && (midicaps.dwSupport & MIDICAPS_VOLUME)) { + _midi.devid = dev; + break; + } + } + + if (CreateThread(NULL, 8192, MidiThread, 0, 0, &threadId) == NULL) + return "Failed to create thread"; + + return NULL; +} + +static void Win32MidiStop(void) +{ + _midi.terminate = true; + SetEvent(_midi.wait_obj); +} + +const HalMusicDriver _win32_music_driver = { + Win32MidiStart, + Win32MidiStop, + Win32MidiPlaySong, + Win32MidiStopSong, + Win32MidiIsSongPlaying, + Win32MidiSetVolume, +}; -- cgit v1.2.3-54-g00ecf