Files @ r4381:c965d1f3016a
Branch filter:

Location: cpp/openttd-patchpack/source/music/dmusic.cpp

belugas
(svn r6131) -Codechange : Complete all missing _ttdpatch_flags entries
-Feature : both unifiedmaglevmode are now set.
Maglev and monorail are not allowed to run on each other tracks and will not be.
Setting those flags will allow grfsets as the Norvegian one to be loaded
-Codechange : link the TTDPatch's irregularstations with OTTD's nonuniform_stations
-Codechange : Reformat the whole array (thanks Rubidium, it sure looks better now)
/* $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(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