changeset 2197:e5ce44e21eed draft

(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
author tron <tron@openttd.org>
date Tue, 26 Jul 2005 06:59:48 +0000
parents ef445069eff2
children b49b4c0a1007
files Makefile makefiledir/Makefile.config_writer music/dmusic.c music/dmusic.cpp music/dmusic2.cpp
diffstat 5 files changed, 229 insertions(+), 437 deletions(-) [+]
line wrap: on
line diff
--- 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 @@
 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)
--- a/makefiledir/Makefile.config_writer
+++ b/makefiledir/Makefile.config_writer
@@ -27,6 +27,7 @@
 	$(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 @@
 	$(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)
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 */
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 <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)
+{
+	/* 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
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 <windows.h>
-#include <stdio.h>
-
-#include <dmksctrl.h>
-#include <dmusici.h>
-#include <dmusicc.h>
-#include <dmusicf.h>
-
-#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 */