dmusic.cpp

Go to the documentation of this file.
00001 /* $Id$ */
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 */