Files @ r4725:3a289e38fda7
Branch filter:

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

bjarni
(svn r6637) -Codechange: merged all (vehicle type)EnterDepot into VehicleEnterDepot()
This revealed duplicated code like aircraft lists got invalidated twice
Moved invalidation of the vehicle detail window to VehicleServiceInDepot() as it should always be updated when serviced
/* $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