# dmusic.cpp

Go to the documentation of this file.

    00001 /* $Id: dmusic.cpp 25269 2013-05-20 13:35:41Z michi_cc $ */
    00002
    00003 /*
    00004  * This file is part of OpenTTD.
    00005  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
    00006  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
    00007  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
    00008  */
    00009
    00012 #ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
    00013
    00014 #define INITGUID
    00015 #include "../stdafx.h"
    00016 #ifdef WIN32_LEAN_AND_MEAN
    00017   #undef WIN32_LEAN_AND_MEAN // Don't exclude rarely-used stuff from Windows headers
    00018 #endif
    00019 #include "../debug.h"
    00020 #include "../os/windows/win32.h"
    00021 #include "../core/mem_func.hpp"
    00022 #include "dmusic.h"
    00023
    00024 #include <windows.h>
    00025 #include <dmksctrl.h>
    00026 #include <dmusici.h>
    00027 #include <dmusicc.h>
    00028 #include <dmusicf.h>
    00029
    00030 static FMusicDriver_DMusic iFMusicDriver_DMusic;
    00031
    00033 static IDirectMusic *music = NULL;
    00034
    00036 static IDirectMusicPerformance *performance = NULL;
    00037
    00039 static IDirectMusicLoader *loader = NULL;
    00040
    00042 static IDirectMusicSegment *segment = NULL;
    00043
    00044 static bool seeking = false;
    00045
    00046
    00047 #define M(x) x "\0"
    00048 static const char ole_files[] =
    00049   M("ole32.dll")
    00050   M("CoCreateInstance")
    00051   M("CoInitialize")
    00052   M("CoUninitialize")
    00053   M("")
    00054 ;
    00055 #undef M
    00056
    00057 struct ProcPtrs {
    00058   unsigned long (WINAPI * CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
    00059   HRESULT (WINAPI * CoInitialize)(LPVOID pvReserved);
    00060   void (WINAPI * CoUninitialize)();
    00061 };
    00062
    00063 static ProcPtrs proc;
    00064
    00065
    00066 const char *MusicDriver_DMusic::Start(const char * const *parm)
    00067 {
    00068   if (performance != NULL) return NULL;
    00069
    00070   if (proc.CoCreateInstance == NULL) {
    00071     if (!LoadLibraryList((Function*)&proc, ole_files)) {
    00072       return "ole32.dll load failed";
    00073     }
    00074   }
    00075
    00076   /* Initialize COM */
    00077   if (FAILED(proc.CoInitialize(NULL))) {
    00078     return "COM initialization failed";
    00079   }
    00080
    00081   /* create the performance object */
    00082   if (FAILED(proc.CoCreateInstance(
    00083         CLSID_DirectMusicPerformance,
    00084         NULL,
    00085         CLSCTX_INPROC,
    00086         IID_IDirectMusicPerformance,
    00087         (LPVOID*)&performance
    00088       ))) {
    00089     return "Failed to create the performance object";
    00090   }
    00091
    00092   /* initialize it */
    00093   if (FAILED(performance->Init(&music, NULL, NULL))) {
    00094     return "Failed to initialize performance object";
    00095   }
    00096
    00097   int port = GetDriverParamInt(parm, "port", -1);
    00098
    00099 #ifndef NO_DEBUG_MESSAGES
    00100   if (_debug_driver_level > 0) {
    00101     /* Print all valid output ports. */
    00102     char desc[DMUS_MAX_DESCRIPTION];
    00103
    00104     DMUS_PORTCAPS caps;
    00105     MemSetT(&caps, 0);
    00106     caps.dwSize = sizeof(DMUS_PORTCAPS);
    00107
    00108     DEBUG(driver, 1, "Detected DirectMusic ports:");
    00109     for (int i = 0; music->EnumPort(i, &caps) == S_OK; i++) {
    00110       if (caps.dwClass == DMUS_PC_OUTPUTCLASS) {
    00111         /* Description is UNICODE even for ANSI build. */
    00112         DEBUG(driver, 1, " %d: %s%s", i, convert_from_fs(caps.wszDescription, desc, lengthof(desc)), i == port ? " (selected)" : "");
    00113       }
    00114     }
    00115   }
    00116 #endif
    00117
    00118   IDirectMusicPort *music_port = NULL; // NULL means 'use default port'.
    00119
    00120   if (port >= 0) {
    00121     /* Check if the passed port is a valid port. */
    00122     DMUS_PORTCAPS caps;
    00123     MemSetT(&caps, 0);
    00124     caps.dwSize = sizeof(DMUS_PORTCAPS);
    00125     if (FAILED(music->EnumPort(port, &caps))) return "Supplied port parameter is not a valid port";
    00126     if (caps.dwClass != DMUS_PC_OUTPUTCLASS) return "Supplied port parameter is not an output port";
    00127
    00128     /* Create new port. */
    00129     DMUS_PORTPARAMS params;
    00130     MemSetT(&params, 0);
    00131     params.dwSize          = sizeof(DMUS_PORTPARAMS);
    00132     params.dwValidParams   = DMUS_PORTPARAMS_CHANNELGROUPS;
    00133     params.dwChannelGroups = 1;
    00134
    00135     if (FAILED(music->CreatePort(caps.guidPort, &params, &music_port, NULL))) {
    00136       return "Failed to create port";
    00137     }
    00138
    00139     /* Activate port. */
    00140     if (FAILED(music_port->Activate(TRUE))) {
    00141       music_port->Release();
    00142       return "Failed to activate port";
    00143     }
    00144   }
    00145
    00146   /* Add port to performance. */
    00147   if (FAILED(performance->AddPort(music_port))) {
    00148     if (music_port != NULL) music_port->Release();
    00149     return "AddPort failed";
    00150   }
    00151
    00152   /* Assign a performance channel block to the performance if we added
    00153    * a custom port to the performance. */
    00154   if (music_port != NULL) {
    00155     if (FAILED(performance->AssignPChannelBlock(0, music_port, 1))) {
    00156       music_port->Release();
    00157       return "Failed to assign PChannel block";
    00158     }
    00159     /* We don't need the port anymore. */
    00160     music_port->Release();
    00161   }
    00162
    00163   /* create the loader object; this will be used to load the MIDI file */
    00164   if (FAILED(proc.CoCreateInstance(
    00165         CLSID_DirectMusicLoader,
    00166         NULL,
    00167         CLSCTX_INPROC,
    00168         IID_IDirectMusicLoader,
    00169         (LPVOID*)&loader
    00170       ))) {
    00171     return "Failed to create loader object";
    00172   }
    00173
    00174   return NULL;
    00175 }
    00176
    00177
    00178 MusicDriver_DMusic::~MusicDriver_DMusic()
    00179 {
    00180   this->Stop();
    00181 }
    00182
    00183
    00184 void MusicDriver_DMusic::Stop()
    00185 {
    00186   seeking = false;
    00187
    00188   if (performance != NULL) performance->Stop(NULL, NULL, 0, 0);
    00189
    00190   if (segment != NULL) {
    00191     segment->SetParam(GUID_Unload, 0xFFFFFFFF, 0, 0, performance);
    00192     segment->Release();
    00193     segment = NULL;
    00194   }
    00195
    00196   if (music != NULL) {
    00197     music->Release();
    00198     music = NULL;
    00199   }
    00200
    00201   if (performance != NULL) {
    00202     performance->CloseDown();
    00203     performance->Release();
    00204     performance = NULL;
    00205   }
    00206
    00207   if (loader != NULL) {
    00208     loader->Release();
    00209     loader = NULL;
    00210   }
    00211
    00212   proc.CoUninitialize();
    00213 }
    00214
    00215
    00216 void MusicDriver_DMusic::PlaySong(const char *filename)
    00217 {
    00218   /* set up the loader object info */
    00219   DMUS_OBJECTDESC obj_desc;
    00220   ZeroMemory(&obj_desc, sizeof(obj_desc));
    00221   obj_desc.dwSize = sizeof(obj_desc);
    00222   obj_desc.guidClass = CLSID_DirectMusicSegment;
    00223   obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
    00224   MultiByteToWideChar(
    00225     CP_ACP, MB_PRECOMPOSED,
    00226     filename, -1,
    00227     obj_desc.wszFileName, lengthof(obj_desc.wszFileName)
    00228   );
    00229
    00230   /* release the existing segment if we have any */
    00231   if (segment != NULL) {
    00232     segment->Release();
    00233     segment = NULL;
    00234   }
    00235
    00236   /* make a new segment */
    00237   if (FAILED(loader->GetObject(
    00238         &obj_desc, IID_IDirectMusicSegment, (LPVOID*)&segment
    00239       ))) {
    00240     DEBUG(driver, 0, "DirectMusic: GetObject failed");
    00241     return;
    00242   }
    00243
    00244   /* tell the segment what kind of data it contains */
    00245   if (FAILED(segment->SetParam(
    00246         GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance
    00247       ))) {
    00248     DEBUG(driver, 0, "DirectMusic: SetParam (MIDI file) failed");
    00249     return;
    00250   }
    00251
    00252   /* tell the segment to 'download' the instruments */
    00253   if (FAILED(segment->SetParam(GUID_Download, 0xFFFFFFFF, 0, 0, performance))) {
    00254     DEBUG(driver, 0, "DirectMusic: failed to download instruments");
    00255     return;
    00256   }
    00257
    00258   /* start playing the MIDI file */
    00259   if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) {
    00260     DEBUG(driver, 0, "DirectMusic: PlaySegment failed");
    00261     return;
    00262   }
    00263
    00264   seeking = true;
    00265 }
    00266
    00267
    00268 void MusicDriver_DMusic::StopSong()
    00269 {
    00270   if (FAILED(performance->Stop(segment, NULL, 0, 0))) {
    00271     DEBUG(driver, 0, "DirectMusic: StopSegment failed");
    00272   }
    00273   seeking = false;
    00274 }
    00275
    00276
    00277 bool MusicDriver_DMusic::IsSongPlaying()
    00278 {
    00279   /* Not the nicest code, but there is a short delay before playing actually
    00280    * starts. OpenTTD makes no provision for this. */
    00281   if (performance->IsPlaying(segment, NULL) == S_OK) {
    00282     seeking = false;
    00283     return true;
    00284   } else {
    00285     return seeking;
    00286   }
    00287 }
    00288
    00289
    00290 void MusicDriver_DMusic::SetVolume(byte vol)
    00291 {
    00292   long db = vol * 2000 / 127 - 2000;
    00293   performance->SetGlobalParam(GUID_PerfMasterVolume, &db, sizeof(db));
    00294 }
    00295
    00296
    00297 #endif /* WIN32_ENABLE_DIRECTMUSIC_SUPPORT */