mirror of
https://github.com/OpenTTD/OpenTTD.git
synced 2025-08-23 14:39:08 +00:00
(svn r7759) -Merge: makefile rewrite. This merge features:
- A proper ./configure, so everything needs to be configured only once, not for every make. - Usage of makedepend when available. This greatly reduces the time needed for generating the dependencies. - A generator for all project files. There is a single file with sources, which is used to generate Makefiles and the project files for MSVC. - Proper support for OSX universal binaries. - Object files for non-MSVC compiles are also placed in separate directories, making is faster to switch between debug and release compiles and it does not touch the directory with the source files. - Functionality to make a bundle of all needed files for for example a nightly or distribution of a binary with all needed GRFs and language files. Note: as this merge moves almost all files, it is recommended to make a backup of your working copy before updating your working copy.
This commit is contained in:
53
src/music/bemidi.cpp
Normal file
53
src/music/bemidi.cpp
Normal file
@@ -0,0 +1,53 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "bemidi.h"
|
||||
|
||||
// BeOS System Includes
|
||||
#include <MidiSynthFile.h>
|
||||
|
||||
static BMidiSynthFile midiSynthFile;
|
||||
|
||||
static const char *bemidi_start(const char * const *parm)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void bemidi_stop(void)
|
||||
{
|
||||
midiSynthFile.UnloadFile();
|
||||
}
|
||||
|
||||
static void bemidi_play_song(const char *filename)
|
||||
{
|
||||
bemidi_stop();
|
||||
entry_ref midiRef;
|
||||
get_ref_for_path(filename, &midiRef);
|
||||
midiSynthFile.LoadFile(&midiRef);
|
||||
midiSynthFile.Start();
|
||||
}
|
||||
|
||||
static void bemidi_stop_song(void)
|
||||
{
|
||||
midiSynthFile.UnloadFile();
|
||||
}
|
||||
|
||||
static bool bemidi_is_playing(void)
|
||||
{
|
||||
return !midiSynthFile.IsFinished();
|
||||
}
|
||||
|
||||
static void bemidi_set_volume(byte vol)
|
||||
{
|
||||
fprintf(stderr, "BeMidi: Set volume not implemented\n");
|
||||
}
|
||||
|
||||
const HalMusicDriver _bemidi_music_driver = {
|
||||
bemidi_start,
|
||||
bemidi_stop,
|
||||
bemidi_play_song,
|
||||
bemidi_stop_song,
|
||||
bemidi_is_playing,
|
||||
bemidi_set_volume,
|
||||
};
|
10
src/music/bemidi.h
Normal file
10
src/music/bemidi.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_BEMIDI_H
|
||||
#define MUSIC_BEMIDI_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _bemidi_music_driver;
|
||||
|
||||
#endif
|
232
src/music/dmusic.cpp
Normal file
232
src/music/dmusic.cpp
Normal file
@@ -0,0 +1,232 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
|
||||
|
||||
extern "C" {
|
||||
#include "../openttd.h"
|
||||
#include "../debug.h"
|
||||
#include "../win32.h"
|
||||
#include "dmusic.h"
|
||||
}
|
||||
|
||||
#include <windows.h>
|
||||
#include <dmksctrl.h>
|
||||
#include <dmusici.h>
|
||||
#include <dmusicc.h>
|
||||
#include <dmusicf.h>
|
||||
|
||||
|
||||
// the performance object controls manipulation of the segments
|
||||
static IDirectMusicPerformance* performance = NULL;
|
||||
|
||||
// the loader object can load many types of DMusic related files
|
||||
static IDirectMusicLoader* loader = NULL;
|
||||
|
||||
// the segment object is where the MIDI data is stored for playback
|
||||
static IDirectMusicSegment* segment = NULL;
|
||||
|
||||
static bool seeking = false;
|
||||
|
||||
|
||||
#define M(x) x "\0"
|
||||
static const char ole_files[] =
|
||||
M("ole32.dll")
|
||||
M("CoCreateInstance")
|
||||
M("CoInitialize")
|
||||
M("CoUninitialize")
|
||||
M("")
|
||||
;
|
||||
#undef M
|
||||
|
||||
struct ProcPtrs {
|
||||
unsigned long (WINAPI * CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv);
|
||||
HRESULT (WINAPI * CoInitialize)(LPVOID pvReserved);
|
||||
void (WINAPI * CoUninitialize)();
|
||||
};
|
||||
|
||||
static ProcPtrs proc;
|
||||
|
||||
|
||||
static const char* DMusicMidiStart(const char* const* parm)
|
||||
{
|
||||
if (performance != NULL) return NULL;
|
||||
|
||||
if (proc.CoCreateInstance == NULL) {
|
||||
if (!LoadLibraryList((Function*)&proc, ole_files))
|
||||
return "ole32.dll load failed";
|
||||
}
|
||||
|
||||
// Initialize COM
|
||||
if (FAILED(proc.CoInitialize(NULL))) {
|
||||
return "COM initialization failed";
|
||||
}
|
||||
|
||||
// create the performance object
|
||||
if (FAILED(proc.CoCreateInstance(
|
||||
CLSID_DirectMusicPerformance,
|
||||
NULL,
|
||||
CLSCTX_INPROC,
|
||||
IID_IDirectMusicPerformance,
|
||||
(LPVOID*)&performance
|
||||
))) {
|
||||
proc.CoUninitialize();
|
||||
return "Failed to create the performance object";
|
||||
}
|
||||
|
||||
// initialize it
|
||||
if (FAILED(performance->Init(NULL, NULL, NULL))) {
|
||||
performance->Release();
|
||||
performance = NULL;
|
||||
proc.CoUninitialize();
|
||||
return "Failed to initialize performance object";
|
||||
}
|
||||
|
||||
// choose default Windows synth
|
||||
if (FAILED(performance->AddPort(NULL))) {
|
||||
performance->CloseDown();
|
||||
performance->Release();
|
||||
performance = NULL;
|
||||
proc.CoUninitialize();
|
||||
return "AddPort failed";
|
||||
}
|
||||
|
||||
// create the loader object; this will be used to load the MIDI file
|
||||
if (FAILED(proc.CoCreateInstance(
|
||||
CLSID_DirectMusicLoader,
|
||||
NULL,
|
||||
CLSCTX_INPROC,
|
||||
IID_IDirectMusicLoader,
|
||||
(LPVOID*)&loader
|
||||
))) {
|
||||
performance->CloseDown();
|
||||
performance->Release();
|
||||
performance = NULL;
|
||||
proc.CoUninitialize();
|
||||
return "Failed to create loader object";
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
static void DMusicMidiStop(void)
|
||||
{
|
||||
seeking = false;
|
||||
|
||||
if (performance != NULL) performance->Stop(NULL, NULL, 0, 0);
|
||||
|
||||
if (segment != NULL) {
|
||||
segment->SetParam(GUID_Unload, 0xFFFFFFFF, 0, 0, performance);
|
||||
segment->Release();
|
||||
segment = NULL;
|
||||
}
|
||||
|
||||
if (performance != NULL) {
|
||||
performance->CloseDown();
|
||||
performance->Release();
|
||||
performance = NULL;
|
||||
}
|
||||
|
||||
if (loader != NULL) {
|
||||
loader->Release();
|
||||
loader = NULL;
|
||||
}
|
||||
|
||||
proc.CoUninitialize();
|
||||
}
|
||||
|
||||
|
||||
static void DMusicMidiPlaySong(const char* filename)
|
||||
{
|
||||
// set up the loader object info
|
||||
DMUS_OBJECTDESC obj_desc;
|
||||
ZeroMemory(&obj_desc, sizeof(obj_desc));
|
||||
obj_desc.dwSize = sizeof(obj_desc);
|
||||
obj_desc.guidClass = CLSID_DirectMusicSegment;
|
||||
obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
|
||||
MultiByteToWideChar(
|
||||
CP_ACP, MB_PRECOMPOSED,
|
||||
filename, -1,
|
||||
obj_desc.wszFileName, lengthof(obj_desc.wszFileName)
|
||||
);
|
||||
|
||||
// release the existing segment if we have any
|
||||
if (segment != NULL) {
|
||||
segment->Release();
|
||||
segment = NULL;
|
||||
}
|
||||
|
||||
// make a new segment
|
||||
if (FAILED(loader->GetObject(
|
||||
&obj_desc, IID_IDirectMusicSegment, (LPVOID*)&segment
|
||||
))) {
|
||||
DEBUG(driver, 0, "DirectMusic: GetObject failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// tell the segment what kind of data it contains
|
||||
if (FAILED(segment->SetParam(
|
||||
GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance
|
||||
))) {
|
||||
DEBUG(driver, 0, "DirectMusic: SetParam (MIDI file) failed");
|
||||
return;
|
||||
}
|
||||
|
||||
// tell the segment to 'download' the instruments
|
||||
if (FAILED(segment->SetParam(GUID_Download, 0xFFFFFFFF, 0, 0, performance))) {
|
||||
DEBUG(driver, 0, "DirectMusic: failed to download instruments");
|
||||
return;
|
||||
}
|
||||
|
||||
// start playing the MIDI file
|
||||
if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) {
|
||||
DEBUG(driver, 0, "DirectMusic: PlaySegment failed");
|
||||
return;
|
||||
}
|
||||
|
||||
seeking = true;
|
||||
}
|
||||
|
||||
|
||||
static void DMusicMidiStopSong(void)
|
||||
{
|
||||
if (FAILED(performance->Stop(segment, NULL, 0, 0))) {
|
||||
DEBUG(driver, 0, "DirectMusic: StopSegment failed");
|
||||
}
|
||||
seeking = false;
|
||||
}
|
||||
|
||||
|
||||
static bool DMusicMidiIsSongPlaying(void)
|
||||
{
|
||||
/* Not the nicest code, but there is a short delay before playing actually
|
||||
* starts. OpenTTD makes no provision for this. */
|
||||
if (performance->IsPlaying(segment, NULL) == S_OK) {
|
||||
seeking = false;
|
||||
return true;
|
||||
} else {
|
||||
return seeking;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void DMusicMidiSetVolume(byte vol)
|
||||
{
|
||||
// 0 - 127 -> -2000 - 0
|
||||
long db = vol * 2000 / 127 - 2000;
|
||||
performance->SetGlobalParam(GUID_PerfMasterVolume, &db, sizeof(db));
|
||||
}
|
||||
|
||||
|
||||
extern "C" const HalMusicDriver _dmusic_midi_driver = {
|
||||
DMusicMidiStart,
|
||||
DMusicMidiStop,
|
||||
DMusicMidiPlaySong,
|
||||
DMusicMidiStopSong,
|
||||
DMusicMidiIsSongPlaying,
|
||||
DMusicMidiSetVolume,
|
||||
};
|
||||
|
||||
#endif
|
10
src/music/dmusic.h
Normal file
10
src/music/dmusic.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_DMUSIC_H
|
||||
#define MUSIC_DMUSIC_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _dmusic_midi_driver;
|
||||
|
||||
#endif
|
108
src/music/extmidi.c
Normal file
108
src/music/extmidi.c
Normal file
@@ -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 <fcntl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <signal.h>
|
||||
#include <sys/stat.h>
|
||||
#include <errno.h>
|
||||
|
||||
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__ */
|
10
src/music/extmidi.h
Normal file
10
src/music/extmidi.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_EXTERNAL_H
|
||||
#define MUSIC_EXTERNAL_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _extmidi_music_driver;
|
||||
|
||||
#endif
|
21
src/music/null_m.c
Normal file
21
src/music/null_m.c
Normal file
@@ -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,
|
||||
};
|
10
src/music/null_m.h
Normal file
10
src/music/null_m.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_NULL_H
|
||||
#define MUSIC_NULL_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _null_music_driver;
|
||||
|
||||
#endif
|
77
src/music/os2_m.c
Normal file
77
src/music/os2_m.c
Normal file
@@ -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 <stdarg.h>
|
||||
#include <os2.h>
|
||||
#include <os2me.h>
|
||||
|
||||
/**********************
|
||||
* 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,
|
||||
};
|
10
src/music/os2_m.h
Normal file
10
src/music/os2_m.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_OS2_H
|
||||
#define MUSIC_OS2_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _os2_music_driver;
|
||||
|
||||
#endif
|
371
src/music/qtmidi.c
Normal file
371
src/music/qtmidi.c
Normal file
@@ -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 <QuickTime/QuickTime.h>
|
||||
#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 <assert.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
// 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,
|
||||
};
|
10
src/music/qtmidi.h
Normal file
10
src/music/qtmidi.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_MACOSX_QUICKTIME_H
|
||||
#define MUSIC_MACOSX_QUICKTIME_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _qtime_music_driver;
|
||||
|
||||
#endif /* !MUSIC_MACOSX_QUICKTIME_H */
|
170
src/music/win32_m.c
Normal file
170
src/music/win32_m.c
Normal file
@@ -0,0 +1,170 @@
|
||||
/* $Id$ */
|
||||
|
||||
#include "../stdafx.h"
|
||||
#include "../openttd.h"
|
||||
#include "win32_m.h"
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
|
||||
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,
|
||||
};
|
10
src/music/win32_m.h
Normal file
10
src/music/win32_m.h
Normal file
@@ -0,0 +1,10 @@
|
||||
/* $Id$ */
|
||||
|
||||
#ifndef MUSIC_WIN32_H
|
||||
#define MUSIC_WIN32_H
|
||||
|
||||
#include "../hal.h"
|
||||
|
||||
extern const HalMusicDriver _win32_music_driver;
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user