tron@2197: /* $Id$ */ tron@2197: tron@2197: #include "../stdafx.h" tron@2197: tron@2197: #ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT tron@2197: rubidium@5838: #include "../openttd.h" rubidium@5838: #include "../debug.h" rubidium@5838: #include "../win32.h" rubidium@5838: #include "dmusic.h" tron@2197: tron@2197: #include tron@2197: #include tron@2197: #include tron@2197: #include tron@2197: #include tron@2197: tron@2197: tron@2197: // the performance object controls manipulation of the segments tron@2197: static IDirectMusicPerformance* performance = NULL; tron@2197: tron@2197: // the loader object can load many types of DMusic related files tron@2197: static IDirectMusicLoader* loader = NULL; tron@2197: tron@2197: // the segment object is where the MIDI data is stored for playback tron@2197: static IDirectMusicSegment* segment = NULL; tron@2197: tron@2197: static bool seeking = false; tron@2197: tron@2197: tron@2197: #define M(x) x "\0" tron@2197: static const char ole_files[] = tron@2197: M("ole32.dll") tron@2197: M("CoCreateInstance") tron@2197: M("CoInitialize") tron@2197: M("CoUninitialize") tron@2197: M("") tron@2197: ; tron@2197: #undef M tron@2197: tron@2197: struct ProcPtrs { tron@2197: unsigned long (WINAPI * CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID* ppv); tron@2197: HRESULT (WINAPI * CoInitialize)(LPVOID pvReserved); tron@2197: void (WINAPI * CoUninitialize)(); tron@2197: }; tron@2197: tron@2197: static ProcPtrs proc; tron@2197: tron@2197: tron@2197: static const char* DMusicMidiStart(const char* const* parm) tron@2197: { tron@2197: if (performance != NULL) return NULL; tron@2197: tron@2197: if (proc.CoCreateInstance == NULL) { tron@2197: if (!LoadLibraryList((Function*)&proc, ole_files)) tron@2197: return "ole32.dll load failed"; tron@2197: } tron@2197: tron@2197: // Initialize COM tron@2197: if (FAILED(proc.CoInitialize(NULL))) { tron@2197: return "COM initialization failed"; tron@2197: } tron@2197: tron@2197: // create the performance object tron@2197: if (FAILED(proc.CoCreateInstance( tron@2197: CLSID_DirectMusicPerformance, tron@2197: NULL, tron@2197: CLSCTX_INPROC, tron@2197: IID_IDirectMusicPerformance, tron@2197: (LPVOID*)&performance tron@2197: ))) { tron@2197: proc.CoUninitialize(); tron@2197: return "Failed to create the performance object"; tron@2197: } tron@2197: tron@2197: // initialize it tron@2197: if (FAILED(performance->Init(NULL, NULL, NULL))) { tron@2197: performance->Release(); tron@2197: performance = NULL; tron@2197: proc.CoUninitialize(); tron@2197: return "Failed to initialize performance object"; tron@2197: } tron@2197: tron@2197: // choose default Windows synth tron@2197: if (FAILED(performance->AddPort(NULL))) { tron@2197: performance->CloseDown(); tron@2197: performance->Release(); tron@2197: performance = NULL; tron@2197: proc.CoUninitialize(); tron@2197: return "AddPort failed"; tron@2197: } tron@2197: tron@2197: // create the loader object; this will be used to load the MIDI file tron@2197: if (FAILED(proc.CoCreateInstance( tron@2197: CLSID_DirectMusicLoader, tron@2197: NULL, tron@2197: CLSCTX_INPROC, tron@2197: IID_IDirectMusicLoader, tron@2197: (LPVOID*)&loader tron@2197: ))) { tron@2197: performance->CloseDown(); tron@2197: performance->Release(); tron@2197: performance = NULL; tron@2197: proc.CoUninitialize(); tron@2197: return "Failed to create loader object"; tron@2197: } tron@2197: tron@2197: return NULL; tron@2197: } tron@2197: tron@2197: tron@2197: static void DMusicMidiStop(void) tron@2197: { tron@2197: seeking = false; tron@2197: Darkvater@2396: if (performance != NULL) performance->Stop(NULL, NULL, 0, 0); tron@2197: Darkvater@2396: if (segment != NULL) { Darkvater@2894: segment->SetParam(GUID_Unload, 0xFFFFFFFF, 0, 0, performance); Darkvater@2396: segment->Release(); Darkvater@2396: segment = NULL; Darkvater@2396: } tron@2197: Darkvater@2396: if (performance != NULL) { Darkvater@2396: performance->CloseDown(); Darkvater@2396: performance->Release(); Darkvater@2396: performance = NULL; Darkvater@2396: } Darkvater@2396: Darkvater@2396: if (loader != NULL) { Darkvater@2396: loader->Release(); Darkvater@2396: loader = NULL; Darkvater@2396: } tron@2197: tron@2197: proc.CoUninitialize(); tron@2197: } tron@2197: tron@2197: tron@2197: static void DMusicMidiPlaySong(const char* filename) tron@2197: { tron@2197: // set up the loader object info tron@2197: DMUS_OBJECTDESC obj_desc; tron@2197: ZeroMemory(&obj_desc, sizeof(obj_desc)); tron@2197: obj_desc.dwSize = sizeof(obj_desc); tron@2197: obj_desc.guidClass = CLSID_DirectMusicSegment; tron@2197: obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH; tron@2197: MultiByteToWideChar( tron@2197: CP_ACP, MB_PRECOMPOSED, tron@2197: filename, -1, tron@2197: obj_desc.wszFileName, lengthof(obj_desc.wszFileName) tron@2197: ); tron@2197: tron@2197: // release the existing segment if we have any tron@2197: if (segment != NULL) { tron@2197: segment->Release(); tron@2197: segment = NULL; tron@2197: } tron@2197: tron@2197: // make a new segment tron@2197: if (FAILED(loader->GetObject( tron@2197: &obj_desc, IID_IDirectMusicSegment, (LPVOID*)&segment tron@2197: ))) { Darkvater@5568: DEBUG(driver, 0, "DirectMusic: GetObject failed"); tron@2197: return; tron@2197: } tron@2197: tron@2197: // tell the segment what kind of data it contains tron@2197: if (FAILED(segment->SetParam( tron@2197: GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance tron@2197: ))) { Darkvater@5568: DEBUG(driver, 0, "DirectMusic: SetParam (MIDI file) failed"); tron@2197: return; tron@2197: } tron@2197: tron@2197: // tell the segment to 'download' the instruments tron@2197: if (FAILED(segment->SetParam(GUID_Download, 0xFFFFFFFF, 0, 0, performance))) { Darkvater@5568: DEBUG(driver, 0, "DirectMusic: failed to download instruments"); tron@2197: return; tron@2197: } tron@2197: tron@2197: // start playing the MIDI file tron@2197: if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) { Darkvater@5568: DEBUG(driver, 0, "DirectMusic: PlaySegment failed"); tron@2197: return; tron@2197: } tron@2197: tron@2197: seeking = true; tron@2197: } tron@2197: tron@2197: tron@2197: static void DMusicMidiStopSong(void) tron@2197: { tron@2197: if (FAILED(performance->Stop(segment, NULL, 0, 0))) { Darkvater@5568: DEBUG(driver, 0, "DirectMusic: StopSegment failed"); tron@2197: } tron@2197: seeking = false; tron@2197: } tron@2197: tron@2197: tron@2197: static bool DMusicMidiIsSongPlaying(void) tron@2197: { tron@2197: /* Not the nicest code, but there is a short delay before playing actually tron@2197: * starts. OpenTTD makes no provision for this. */ tron@2197: if (performance->IsPlaying(segment, NULL) == S_OK) { tron@2197: seeking = false; tron@2197: return true; tron@2197: } else { tron@2197: return seeking; tron@2197: } tron@2197: } tron@2197: tron@2197: tron@2197: static void DMusicMidiSetVolume(byte vol) tron@2197: { tron@2197: // 0 - 127 -> -2000 - 0 tron@2197: long db = vol * 2000 / 127 - 2000; tron@2197: performance->SetGlobalParam(GUID_PerfMasterVolume, &db, sizeof(db)); tron@2197: } tron@2197: tron@2197: tron@2197: extern "C" const HalMusicDriver _dmusic_midi_driver = { tron@2197: DMusicMidiStart, tron@2197: DMusicMidiStop, tron@2197: DMusicMidiPlaySong, tron@2197: DMusicMidiStopSong, tron@2197: DMusicMidiIsSongPlaying, tron@2197: DMusicMidiSetVolume, tron@2197: }; tron@2197: tron@2197: #endif