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