# HG changeset patch # User tron # Date 2005-07-26 06:59:48 # Node ID 2448467991a5e382f3b85825b520161f8fdce0b5 # Parent dfde8f866cf70c230526eb1dcc0c5f412ea20e38 (svn r2712) Overhaul DirectMusic MIDI backend: - Merge the .c and .cpp part into one file - Properly deinitialize at the end - Remove "experimental" status - Miscellaneous smaller changes -Fix: Volume control works now diff --git a/Makefile b/Makefile --- a/Makefile +++ b/Makefile @@ -66,6 +66,7 @@ # MIDI: if set, it will use it as custom path to midi player. # If unset, it will use the hardcoded path in the c code # This can still be overriden by the music.extmidi openttd.cfg option. +# WITH_DIRECTMUSIC: enable DirectMusic MIDI support # WITH_NETWORK: enable networking # DEDICATED: allows compilation on UNIX without SDL. Useful for dedicated servers # @@ -118,9 +119,6 @@ # CC_HOST: the gcc of your localhost if you are making a target that produces incompatible executables # CFLAGS_HOST: cflags used for CC_HOST. Make it something if you are getting errors when you try to compi # windows executables on linux. (just: CFLAGS_HOST:='-I' or something) -# -# Experimental (does not work properly): -# WITH_DIRECTMUSIC: enable DirectMusic MIDI support ############################################################################## @@ -751,8 +749,7 @@ OBJS += winres.o endif ifdef WITH_DIRECTMUSIC -C_SOURCES += music/dmusic.c -CXX_SOURCES += music/dmusic2.cpp +CXX_SOURCES += music/dmusic.cpp endif DEPS = $(OBJS:%.o=.deps/%.d) diff --git a/makefiledir/Makefile.config_writer b/makefiledir/Makefile.config_writer --- a/makefiledir/Makefile.config_writer +++ b/makefiledir/Makefile.config_writer @@ -27,6 +27,7 @@ CONFIG_LINE=@$(SHELL) -c 'echo $(1)' >> $(call CONFIG_LINE,MIDI:=$(MIDI)) $(call CONFIG_LINE,MIDI_ARG:=$(MIDI_ARG)) $(call CONFIG_LINE,SUPRESS_LANG_ERRORS:=$(SUPRESS_LANG_ERRORS)) + $(call CONFIG_LINE,WITH_DIRECTMUSIC:=$(WITH_DIRECTMUSIC)) $(call CONFIG_LINE,WITH_NETWORK:=$(WITH_NETWORK)) $(call CONFIG_LINE,DEDICATED:=$(DEDICATED)) $(call CONFIG_LINE,GPMI:=$(GPMI)) @@ -48,11 +49,6 @@ CONFIG_LINE=@$(SHELL) -c 'echo $(1)' >> $(call CONFIG_LINE,CUSTOM_LANG_PATH:=$(CUSTOM_LANG_PATH)) $(call CONFIG_LINE,) - $(call CONFIG_LINE,\# Experimental) - $(call CONFIG_LINE,WITH_DIRECTMUSIC:=$(WITH_DIRECTMUSIC)) - $(call CONFIG_LINE,) - $(call CONFIG_LINE,) - $(call CONFIG_LINE,\# Flag to skip test for OS when building static) $(call CONFIG_LINE,\# OpenTTD have only been succesfully tested with static builds on MorphOS and MacOSX) $(call CONFIG_LINE,\# If you want to try anyway on other OSes, set this flag) diff --git a/music/dmusic.c b/music/dmusic.c deleted file mode 100644 --- a/music/dmusic.c +++ /dev/null @@ -1,114 +0,0 @@ -/* $Id$ */ - -/********************************************************************* - * OpenTTD: An Open Source Transport Tycoon Deluxe clone * - * Copyright (c) 2002-2004 OpenTTD Developers. All Rights Reserved. * - * * - * Web site: http://openttd.sourceforge.net/ * - *********************************************************************/ - -/* - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* DirectMusic driver for Win32 */ -/* Based on dxmci from TTDPatch */ - -#include "../stdafx.h" - -#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT - -#include "../openttd.h" -#include "../string.h" -#include "../variables.h" -#include "../sound.h" -#include "dmusic.h" - -static const char * DMusicMidiStart(const char * const *parm); -static void DMusicMidiStop(void); -static void DMusicMidiPlaySong(const char *filename); -static void DMusicMidiStopSong(void); -static bool DMusicMidiIsSongPlaying(void); -static void DMusicMidiSetVolume(byte vol); - -const HalMusicDriver _dmusic_midi_driver = { - DMusicMidiStart, - DMusicMidiStop, - DMusicMidiPlaySong, - DMusicMidiStopSong, - DMusicMidiIsSongPlaying, - DMusicMidiSetVolume, -}; - -extern bool LoadMIDI (char *directory, char *filename); -extern bool InitDirectMusic (void); -extern void ReleaseSegment (void); -extern void ShutdownDirectMusic (void); -extern bool LoadMIDI (char *directory, char *filename); -extern void PlaySegment (void); -extern void StopSegment (void); -extern bool IsSegmentPlaying (void); -extern void SetVolume (long); - -static bool seeking = false; - -static const char * DMusicMidiStart(const char * const *parm) -{ - return InitDirectMusic() ? NULL : "Unable to initialize DirectMusic"; -} - -static void DMusicMidiStop(void) -{ - StopSegment(); -} - -static void DMusicMidiPlaySong(const char *filename) -{ - char *pos; - char dir[MAX_PATH]; - char file[MAX_PATH]; - - // split full path into directory and file components - ttd_strlcpy(dir, filename, MAX_PATH); - pos = strrchr(dir, '\\') + 1; - ttd_strlcpy(file, pos, MAX_PATH); - *pos = '\0'; - - LoadMIDI(dir, file); - PlaySegment(); - seeking = true; -} - -static void DMusicMidiStopSong(void) -{ - StopSegment(); -} - -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 (!IsSegmentPlaying() && seeking) return true; - if (IsSegmentPlaying()) seeking = false; - - return IsSegmentPlaying(); -} - -static void DMusicMidiSetVolume(byte vol) -{ - SetVolume(vol); -} - -#endif /* WIN32_ENABLE_DIRECTMUSIC_SUPPORT */ diff --git a/music/dmusic.cpp b/music/dmusic.cpp new file mode 100644 --- /dev/null +++ b/music/dmusic.cpp @@ -0,0 +1,226 @@ +/* $Id$ */ + +#include "../stdafx.h" + +#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT + +extern "C" { + #include "../openttd.h" + #include "../debug.h" + #include "../win32.h" + #include "dmusic.h" +} + +#include +#include +#include +#include +#include + + +// 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) +{ + /* release everything but the segment, which the performance + * will release automatically (and it'll crash if it's been + * released already) */ + + seeking = false; + + loader->Release(); + loader = NULL; + + performance->CloseDown(); + performance->Release(); + performance = NULL; + + segment = 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(misc, 0) ("DirectMusic: Get object failed"); + return; + } + + // tell the segment what kind of data it contains + if (FAILED(segment->SetParam( + GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance + ))) { + DEBUG(misc, 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(misc, 0) ("DirectMusic: Failed to download instruments"); + return; + } + + // start playing the MIDI file + if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) { + DEBUG(misc, 0) ("DirectMusic: PlaySegment failed"); + return; + } + + seeking = true; +} + + +static void DMusicMidiStopSong(void) +{ + if (FAILED(performance->Stop(segment, NULL, 0, 0))) { + DEBUG(misc, 0) ("DirecMusic: 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 diff --git a/music/dmusic2.cpp b/music/dmusic2.cpp deleted file mode 100644 --- a/music/dmusic2.cpp +++ /dev/null @@ -1,313 +0,0 @@ -/* $Id$ */ - -/********************************************************************* - * OpenTTD: An Open Source Transport Tycoon Deluxe clone * - * Copyright (c) 2002-2004 OpenTTD Developers. All Rights Reserved. * - * * - * Web site: http://openttd.sourceforge.net/ * - *********************************************************************/ - -/* - * This program 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; either version 2 of the License, or - * (at your option) any later version. - * - * This program 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. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -/* DirectMusic driver for Win32 */ -/* Based on dxmci from TTDPatch */ - -#include "../stdafx.h" - -#ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT - -// for gcc, the GUIDs are available in a library instead -#ifndef __GNUC__ -#define INITGUID -#endif - -#ifdef __cplusplus - extern "C" { -#endif - -#include "../openttd.h" -#include "../debug.h" - -#ifdef __cplusplus - } -#endif - -#include -#include - -#include -#include -#include -#include - -#define MSGBOX(output) DEBUG(misc, 0) ("DirectMusic driver: %s\n", output); //MessageBox(NULL, output, "dxmci",MB_OK); - -static void MultiToWide(WCHAR* to, const char* from) -{ - MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, from, -1, to, _MAX_PATH); -} - -// the performance object controls manipulation of the segments -static IDirectMusicPerformance *performance = NULL; - -// the segment object is where the MIDI data is stored for playback -static IDirectMusicSegment *segment = NULL; - -// the loader bject can load many types of DMusic related files -static IDirectMusicLoader *loader = NULL; - -// whether we've initialized COM or not (when deciding whether to shut down) -static int COMInitialized = 0; - - -extern "C" bool LoadLibraryList(void **proc, const char *dll); - -// Use lazy linking -struct ProcPtrs { - unsigned long (WINAPI *CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID * ppv); - HRESULT (WINAPI *CoInitialize)( LPVOID pvReserved ); - void (WINAPI *CoUninitialize)( ); -}; - -#define M(x) x "\0" -static const char ole_files[] = - M("ole32.dll") - M("CoCreateInstance") - M("CoInitialize") - M("CoUninitialize") - M("") -; -#undef M - - -static ProcPtrs _proc; - -static bool LoadOleDLL(void) -{ - if (_proc.CoCreateInstance != NULL) - return true; - if (!LoadLibraryList((void**)&_proc, ole_files)) - return false; - return true; -} - - -#ifdef __cplusplus -extern "C" { -#endif - -// Initialize COM and DirectMusic -bool InitDirectMusic(void) -{ - if (NULL != performance) - return true; - - // Initialize COM - if (!COMInitialized) { - if (!LoadOleDLL()) { - MSGBOX("ole32.dll load failed"); - return false; - } - - _proc.CoInitialize(NULL); - COMInitialized = 1; - } - - // Create the performance object via CoCreateInstance - if (FAILED(_proc.CoCreateInstance( - CLSID_DirectMusicPerformance, - NULL, - CLSCTX_INPROC, - IID_IDirectMusicPerformance, - (LPVOID*)&performance - ))) { - MSGBOX("Failed to create the performance object"); - return false; - } - - // Initialize it - if (FAILED(performance->Init(NULL, NULL, NULL))) { - MSGBOX("Failed to initialize performance object"); - return false; - } - - // Choose default Windows synth - if (FAILED(performance->AddPort(NULL))) { - MSGBOX("AddPort failed"); - return false; - } - - // now we'll create the loader object. This will be used to load the - // midi file for our demo. Again, we need to use CoCreateInstance - // and pass the appropriate ID parameters - if (FAILED(_proc.CoCreateInstance( - CLSID_DirectMusicLoader, - NULL, - CLSCTX_INPROC, - IID_IDirectMusicLoader, - (LPVOID*)&loader - ))) { - MSGBOX("Failed to create loader object"); - return false; - } - - // that's it for initialization. If we made it this far we - // were successful. - return true; -} - -// Releases memory used by all of the initialized -// DirectMusic objects in the program -void ReleaseSegment(void) -{ - if (NULL != segment) { - segment->Release(); - segment = NULL; - } -} - -void ShutdownDirectMusic(void) -{ - // release everything but the segment, which the performance - // will release automatically (and it'll crash if it's been - // released already) - - if (NULL != loader) { - loader->Release(); - loader = NULL; - } - - if (NULL != performance) { - performance->CloseDown(); - performance->Release(); - performance = NULL; - } - - if (COMInitialized) { - _proc.CoUninitialize(); - COMInitialized = 0; - } -} - -// Load MIDI file for playing -bool LoadMIDI(const char *directory, const char *filename) -{ - DMUS_OBJECTDESC obj_desc; - WCHAR w_directory[_MAX_PATH]; // utf-16 version of the directory name. - WCHAR w_filename[_MAX_PATH]; // utf-16 version of the file name - - if (NULL == performance) - return false; - - MultiToWide(w_directory, directory); - - if (FAILED(loader->SetSearchDirectory( - GUID_DirectMusicAllTypes, w_directory, FALSE - ))) { - MSGBOX("LoadMIDI: SetSearchDirectory failed"); - return false; - } - - // set up the loader object info - ZeroMemory(&obj_desc, sizeof(obj_desc)); - obj_desc.dwSize = sizeof(obj_desc); - - MultiToWide(w_filename, filename); - obj_desc.guidClass = CLSID_DirectMusicSegment; - - wcscpy(obj_desc.wszFileName, w_filename); - obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME; - - // release the existing segment if we have any - if (NULL != segment) - ReleaseSegment(); - - // and make a new segment - if (FAILED(loader->GetObject( - &obj_desc, IID_IDirectMusicSegment, (LPVOID*)&segment - ))) { - MSGBOX("LoadMIDI: Get object failed"); - return false; - } - - // next we need to tell the segment what kind of data it contains. We do this - // with the IDirectMusicSegment::SetParam function. - if (FAILED(segment->SetParam( - GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance - ))) { - MSGBOX("LoadMIDI: SetParam (MIDI file) failed"); - return false; - } - - // finally, we need to tell the segment to 'download' the instruments - if (FAILED(segment->SetParam(GUID_Download, 0xFFFFFFFF, 0, 0, performance))) { - MSGBOX("LoadMIDI: Failed to download instruments"); - return false; - } - - // at this point, the MIDI file is loaded and ready to play! - return true; -} - -// Start playing the MIDI file -void PlaySegment(void) -{ - if (NULL == performance) - return; - - if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) { - MSGBOX("PlaySegment failed"); - } -} - -// Stop playing -void StopSegment(void) -{ - if (NULL == performance || NULL == segment) - return; - - if (FAILED(performance->Stop(segment, NULL, 0, 0))) { - MSGBOX("StopSegment failed"); - } -} - -// Find out whether playing has started or stopped -bool IsSegmentPlaying(void) -{ - if (NULL == performance || NULL == segment) - return false; - - // IsPlaying return S_OK if the segment is currently playing - return performance->IsPlaying(segment, NULL) == S_OK; -} - -void SetVolume(long vol) -{ - long db; - - if (performance == NULL && !InitDirectMusic()) - return; - - db = ((vol >> 21) & 0x7ff) - 1000; - performance->SetGlobalParam(GUID_PerfMasterVolume, &db, sizeof(db)); -} - -#if defined(__cplusplus) -} -#endif - -#endif /* WIN32_ENABLE_DIRECTMUSIC_SUPPORT */