Files @ r4603:3b159d0db197
Branch filter:

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

peter1138
(svn r6455) - Feature: Add 2cc (two company colours) livery schemes. This replaces the original colour selection window and bumps the saveload version. Liveries are supported for all vehicles, not just those with 2cc support. Thanks to lakie for GUI inspiration.
/* $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