OpenTTD: src/music/dmusic.cpp Source File

OpenTTD

- Main Page
- Related Pages
- Modules
- Namespaces
- Data Structures
- Files

- File List
- Globals

dmusic.cpp
Go to the documentation of this file.
1 /* $Id: dmusic.cpp 26482 2014-04-23 20:13:33Z rubidium $ */
2
3 /*
4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
8 */
9
12 #ifdef WIN32_ENABLE_DIRECTMUSIC_SUPPORT
13
14 #define INITGUID
15 #include "../stdafx.h"
16 #ifdef WIN32_LEAN_AND_MEAN
17 #undef WIN32_LEAN_AND_MEAN // Don't exclude rarely-used stuff from Windows headers
18 #endif
19 #include "../debug.h"
20 #include "../os/windows/win32.h"
21 #include "../core/mem_func.hpp"
22 #include "dmusic.h"
23
24 #include <windows.h>
25 #include <dmksctrl.h>
26 #include <dmusici.h>
27 #include <dmusicc.h>
28 #include <dmusicf.h>
29
30 #include "../safeguards.h"
31
32 static FMusicDriver_DMusic iFMusicDriver_DMusic;
33
35 static IDirectMusic *music = NULL;
36
38 static IDirectMusicPerformance *performance = NULL;
39
41 static IDirectMusicLoader *loader = NULL;
42
44 static IDirectMusicSegment *segment = NULL;
45
46 static bool seeking = false;
47
48
49 #define M(x) x "\0"
50 static const char ole_files[] =
51 M("ole32.dll")
52 M("CoCreateInstance")
53 M("CoInitialize")
54 M("CoUninitialize")
55 M("")
56 ;
57 #undef M
58
59 struct ProcPtrs {
60 unsigned long (WINAPI * CoCreateInstance)(REFCLSID rclsid, LPUNKNOWN pUnkOuter, DWORD dwClsContext, REFIID riid, LPVOID *ppv);
61 HRESULT (WINAPI * CoInitialize)(LPVOID pvReserved);
62 void (WINAPI * CoUninitialize)();
63 };
64
65 static ProcPtrs proc;
66
67
68 const char *MusicDriver_DMusic::Start(const char * const *parm)
69 {
70 if (performance != NULL) return NULL;
71
72 if (proc.CoCreateInstance == NULL) {
73 if (!LoadLibraryList((Function*)&proc, ole_files)) {
74 return "ole32.dll load failed";
75 }
76 }
77
78 /* Initialize COM */
79 if (FAILED(proc.CoInitialize(NULL))) {
80 return "COM initialization failed";
81 }
82
83 /* create the performance object */
84 if (FAILED(proc.CoCreateInstance(
85 CLSID_DirectMusicPerformance,
86 NULL,
87 CLSCTX_INPROC,
88 IID_IDirectMusicPerformance,
89 (LPVOID*)&performance
90 ))) {
91 return "Failed to create the performance object";
92 }
93
94 /* initialize it */
95 if (FAILED(performance->Init(&music, NULL, NULL))) {
96 return "Failed to initialize performance object";
97 }
98
99 int port = GetDriverParamInt(parm, "port", -1);
100
101 #ifndef NO_DEBUG_MESSAGES
102 if (_debug_driver_level > 0) {
103 /* Print all valid output ports. */
104 char desc[DMUS_MAX_DESCRIPTION];
105
106 DMUS_PORTCAPS caps;
107 MemSetT(&caps, 0);
108 caps.dwSize = sizeof(DMUS_PORTCAPS);
109
110 DEBUG(driver, 1, "Detected DirectMusic ports:");
111 for (int i = 0; music->EnumPort(i, &caps) == S_OK; i++) {
112 if (caps.dwClass == DMUS_PC_OUTPUTCLASS) {
113 /* Description is UNICODE even for ANSI build. */
114 DEBUG(driver, 1, " %d: %s%s", i, convert_from_fs(caps.wszDescription, desc, lengthof(desc)), i == port ? " (selected)" : "");
115 }
116 }
117 }
118 #endif
119
120 IDirectMusicPort *music_port = NULL; // NULL means 'use default port'.
121
122 if (port >= 0) {
123 /* Check if the passed port is a valid port. */
124 DMUS_PORTCAPS caps;
125 MemSetT(&caps, 0);
126 caps.dwSize = sizeof(DMUS_PORTCAPS);
127 if (FAILED(music->EnumPort(port, &caps))) return "Supplied port parameter is not a valid port";
128 if (caps.dwClass != DMUS_PC_OUTPUTCLASS) return "Supplied port parameter is not an output port";
129
130 /* Create new port. */
131 DMUS_PORTPARAMS params;
132 MemSetT(&params, 0);
133 params.dwSize = sizeof(DMUS_PORTPARAMS);
134 params.dwValidParams = DMUS_PORTPARAMS_CHANNELGROUPS;
135 params.dwChannelGroups = 1;
136
137 if (FAILED(music->CreatePort(caps.guidPort, &params, &music_port, NULL))) {
138 return "Failed to create port";
139 }
140
141 /* Activate port. */
142 if (FAILED(music_port->Activate(TRUE))) {
143 music_port->Release();
144 return "Failed to activate port";
145 }
146 }
147
148 /* Add port to performance. */
149 if (FAILED(performance->AddPort(music_port))) {
150 if (music_port != NULL) music_port->Release();
151 return "AddPort failed";
152 }
153
154 /* Assign a performance channel block to the performance if we added
155 * a custom port to the performance. */
156 if (music_port != NULL) {
157 if (FAILED(performance->AssignPChannelBlock(0, music_port, 1))) {
158 music_port->Release();
159 return "Failed to assign PChannel block";
160 }
161 /* We don't need the port anymore. */
162 music_port->Release();
163 }
164
165 /* create the loader object; this will be used to load the MIDI file */
166 if (FAILED(proc.CoCreateInstance(
167 CLSID_DirectMusicLoader,
168 NULL,
169 CLSCTX_INPROC,
170 IID_IDirectMusicLoader,
171 (LPVOID*)&loader
172 ))) {
173 return "Failed to create loader object";
174 }
175
176 return NULL;
177 }
178
179
180 MusicDriver_DMusic::~MusicDriver_DMusic()
181 {
182 this->Stop();
183 }
184
185
186 void MusicDriver_DMusic::Stop()
187 {
188 seeking = false;
189
190 if (performance != NULL) performance->Stop(NULL, NULL, 0, 0);
191
192 if (segment != NULL) {
193 segment->SetParam(GUID_Unload, 0xFFFFFFFF, 0, 0, performance);
194 segment->Release();
195 segment = NULL;
196 }
197
198 if (music != NULL) {
199 music->Release();
200 music = NULL;
201 }
202
203 if (performance != NULL) {
204 performance->CloseDown();
205 performance->Release();
206 performance = NULL;
207 }
208
209 if (loader != NULL) {
210 loader->Release();
211 loader = NULL;
212 }
213
214 proc.CoUninitialize();
215 }
216
217
218 void MusicDriver_DMusic::PlaySong(const char *filename)
219 {
220 /* set up the loader object info */
221 DMUS_OBJECTDESC obj_desc;
222 ZeroMemory(&obj_desc, sizeof(obj_desc));
223 obj_desc.dwSize = sizeof(obj_desc);
224 obj_desc.guidClass = CLSID_DirectMusicSegment;
225 obj_desc.dwValidData = DMUS_OBJ_CLASS | DMUS_OBJ_FILENAME | DMUS_OBJ_FULLPATH;
226 MultiByteToWideChar(
227 CP_ACP, MB_PRECOMPOSED,
228 filename, -1,
229 obj_desc.wszFileName, lengthof(obj_desc.wszFileName)
230 );
231
232 /* release the existing segment if we have any */
233 if (segment != NULL) {
234 segment->Release();
235 segment = NULL;
236 }
237
238 /* make a new segment */
239 if (FAILED(loader->GetObject(
240 &obj_desc, IID_IDirectMusicSegment, (LPVOID*)&segment
241 ))) {
242 DEBUG(driver, 0, "DirectMusic: GetObject failed");
243 return;
244 }
245
246 /* tell the segment what kind of data it contains */
247 if (FAILED(segment->SetParam(
248 GUID_StandardMIDIFile, 0xFFFFFFFF, 0, 0, performance
249 ))) {
250 DEBUG(driver, 0, "DirectMusic: SetParam (MIDI file) failed");
251 return;
252 }
253
254 /* tell the segment to 'download' the instruments */
255 if (FAILED(segment->SetParam(GUID_Download, 0xFFFFFFFF, 0, 0, performance))) {
256 DEBUG(driver, 0, "DirectMusic: failed to download instruments");
257 return;
258 }
259
260 /* start playing the MIDI file */
261 if (FAILED(performance->PlaySegment(segment, 0, 0, NULL))) {
262 DEBUG(driver, 0, "DirectMusic: PlaySegment failed");
263 return;
264 }
265
266 seeking = true;
267 }
268
269
270 void MusicDriver_DMusic::StopSong()
271 {
272 if (FAILED(performance->Stop(segment, NULL, 0, 0))) {
273 DEBUG(driver, 0, "DirectMusic: StopSegment failed");
274 }
275 seeking = false;
276 }
277
278
279 bool MusicDriver_DMusic::IsSongPlaying()
280 {
281 /* Not the nicest code, but there is a short delay before playing actually
282 * starts. OpenTTD makes no provision for this. */
283 if (performance->IsPlaying(segment, NULL) == S_OK) {
284 seeking = false;
285 return true;
286 } else {
287 return seeking;
288 }
289 }
290
291
292 void MusicDriver_DMusic::SetVolume(byte vol)
293 {
294 long db = vol * 2000 / 127 - 2000;
295 performance->SetGlobalParam(GUID_PerfMasterVolume, &db, sizeof(db));
296 }
297
298
299 #endif /* WIN32_ENABLE_DIRECTMUSIC_SUPPORT */