summaryrefslogtreecommitdiff
path: root/src/music/dmusic.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/music/dmusic.cpp')
-rw-r--r--src/music/dmusic.cpp232
1 files changed, 232 insertions, 0 deletions
diff --git a/src/music/dmusic.cpp b/src/music/dmusic.cpp
new file mode 100644
index 000000000..e20842d2e
--- /dev/null
+++ b/src/music/dmusic.cpp
@@ -0,0 +1,232 @@
+/* $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(driver, 0, "DirectMusic: GetObject failed");
+ return;
+ }
+
+ // tell the segment what kind of data it contains
+ if (FAILED(segment->SetParam(
+ GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance
+ ))) {
+ DEBUG(driver, 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(driver, 0, "DirectMusic: failed to download instruments");
+ return;
+ }
+
+ // start playing the MIDI file
+ if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) {
+ DEBUG(driver, 0, "DirectMusic: PlaySegment failed");
+ return;
+ }
+
+ seeking = true;
+}
+
+
+static void DMusicMidiStopSong(void)
+{
+ if (FAILED(performance->Stop(segment, NULL, 0, 0))) {
+ DEBUG(driver, 0, "DirectMusic: 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