1 /* 2 * IDirectMusic8 Implementation 3 * 4 * Copyright (C) 2003-2004 Rok Mandeljc 5 * Copyright (C) 2012 Christian Costa 6 * 7 * This program is free software; you can redistribute it and/or 8 * modify it under the terms of the GNU Lesser General Public 9 * License as published by the Free Software Foundation; either 10 * version 2.1 of the License, or (at your option) any later version. 11 * 12 * This program is distributed in the hope that it will be useful, 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 * Lesser General Public License for more details. 16 * 17 * You should have received a copy of the GNU Lesser General Public 18 * License along with this program; if not, write to the Free Software 19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 20 */ 21 22 #include <stdio.h> 23 24 #include "dmusic_private.h" 25 26 WINE_DEFAULT_DEBUG_CHANNEL(dmusic); 27 28 static inline IDirectMusic8Impl *impl_from_IDirectMusic8(IDirectMusic8 *iface) 29 { 30 return CONTAINING_RECORD(iface, IDirectMusic8Impl, IDirectMusic8_iface); 31 } 32 33 /* IDirectMusic8Impl IUnknown part: */ 34 static HRESULT WINAPI IDirectMusic8Impl_QueryInterface(LPDIRECTMUSIC8 iface, REFIID riid, LPVOID *ret_iface) 35 { 36 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 37 38 TRACE("(%p)->(%s, %p)\n", iface, debugstr_dmguid(riid), ret_iface); 39 40 if (IsEqualIID (riid, &IID_IUnknown) || 41 IsEqualIID (riid, &IID_IDirectMusic) || 42 IsEqualIID (riid, &IID_IDirectMusic2) || 43 IsEqualIID (riid, &IID_IDirectMusic8)) 44 { 45 IDirectMusic8_AddRef(iface); 46 *ret_iface = iface; 47 return S_OK; 48 } 49 50 *ret_iface = NULL; 51 52 WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ret_iface); 53 54 return E_NOINTERFACE; 55 } 56 57 static ULONG WINAPI IDirectMusic8Impl_AddRef(LPDIRECTMUSIC8 iface) 58 { 59 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 60 ULONG ref = InterlockedIncrement(&This->ref); 61 62 TRACE("(%p)->(): new ref = %u\n", This, ref); 63 64 return ref; 65 } 66 67 static ULONG WINAPI IDirectMusic8Impl_Release(LPDIRECTMUSIC8 iface) 68 { 69 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 70 ULONG ref = InterlockedDecrement(&This->ref); 71 72 TRACE("(%p)->(): new ref = %u\n", This, ref); 73 74 if (!ref) { 75 IReferenceClock_Release(&This->master_clock->IReferenceClock_iface); 76 if (This->dsound) 77 IDirectSound_Release(This->dsound); 78 HeapFree(GetProcessHeap(), 0, This->system_ports); 79 HeapFree(GetProcessHeap(), 0, This->ports); 80 HeapFree(GetProcessHeap(), 0, This); 81 DMUSIC_UnlockModule(); 82 } 83 84 return ref; 85 } 86 87 /* IDirectMusic8Impl IDirectMusic part: */ 88 static HRESULT WINAPI IDirectMusic8Impl_EnumPort(LPDIRECTMUSIC8 iface, DWORD index, LPDMUS_PORTCAPS port_caps) 89 { 90 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 91 92 TRACE("(%p, %d, %p)\n", This, index, port_caps); 93 94 if (!port_caps) 95 return E_POINTER; 96 97 if (index >= This->num_system_ports) 98 return S_FALSE; 99 100 *port_caps = This->system_ports[index].caps; 101 102 return S_OK; 103 } 104 105 static HRESULT WINAPI IDirectMusic8Impl_CreateMusicBuffer(LPDIRECTMUSIC8 iface, LPDMUS_BUFFERDESC buffer_desc, LPDIRECTMUSICBUFFER* buffer, LPUNKNOWN unkouter) 106 { 107 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 108 109 TRACE("(%p)->(%p, %p, %p)\n", This, buffer_desc, buffer, unkouter); 110 111 if (unkouter) 112 return CLASS_E_NOAGGREGATION; 113 114 if (!buffer_desc || !buffer) 115 return E_POINTER; 116 117 return DMUSIC_CreateDirectMusicBufferImpl(buffer_desc, (LPVOID)buffer); 118 } 119 120 static HRESULT WINAPI IDirectMusic8Impl_CreatePort(LPDIRECTMUSIC8 iface, REFCLSID rclsid_port, LPDMUS_PORTPARAMS port_params, LPDIRECTMUSICPORT* port, LPUNKNOWN unkouter) 121 { 122 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 123 int i; 124 DMUS_PORTCAPS port_caps; 125 IDirectMusicPort* new_port = NULL; 126 HRESULT hr; 127 GUID default_port; 128 const GUID *request_port = rclsid_port; 129 130 TRACE("(%p)->(%s, %p, %p, %p)\n", This, debugstr_dmguid(rclsid_port), port_params, port, unkouter); 131 132 if (!rclsid_port || !port) 133 return E_POINTER; 134 if (!port_params) 135 return E_INVALIDARG; 136 if (unkouter) 137 return CLASS_E_NOAGGREGATION; 138 if (!This->dsound) 139 return DMUS_E_DSOUND_NOT_SET; 140 141 if (TRACE_ON(dmusic)) 142 dump_DMUS_PORTPARAMS(port_params); 143 144 ZeroMemory(&port_caps, sizeof(DMUS_PORTCAPS)); 145 port_caps.dwSize = sizeof(DMUS_PORTCAPS); 146 147 if (IsEqualGUID(request_port, &GUID_NULL)) { 148 hr = IDirectMusic8_GetDefaultPort(iface, &default_port); 149 if(FAILED(hr)) 150 return hr; 151 request_port = &default_port; 152 } 153 154 for (i = 0; S_FALSE != IDirectMusic8Impl_EnumPort(iface, i, &port_caps); i++) { 155 if (IsEqualCLSID(request_port, &port_caps.guidPort)) { 156 hr = This->system_ports[i].create(This, port_params, &port_caps, &new_port); 157 if (FAILED(hr)) { 158 *port = NULL; 159 return hr; 160 } 161 This->num_ports++; 162 if (!This->ports) 163 This->ports = HeapAlloc(GetProcessHeap(), 0, 164 sizeof(*This->ports) * This->num_ports); 165 else 166 This->ports = HeapReAlloc(GetProcessHeap(), 0, This->ports, 167 sizeof(*This->ports) * This->num_ports); 168 This->ports[This->num_ports - 1] = new_port; 169 *port = new_port; 170 return S_OK; 171 } 172 } 173 174 return E_NOINTERFACE; 175 } 176 177 void dmusic_remove_port(IDirectMusic8Impl *dmusic, IDirectMusicPort *port) 178 { 179 BOOL found = FALSE; 180 int i; 181 182 TRACE("Removing port %p.\n", port); 183 184 for (i = 0; i < dmusic->num_ports; i++) 185 { 186 if (dmusic->ports[i] == port) { 187 found = TRUE; 188 break; 189 } 190 } 191 192 if (!found) 193 { 194 ERR("Port %p not found in ports array.\n", port); 195 return; 196 } 197 198 if (!--dmusic->num_ports) { 199 HeapFree(GetProcessHeap(), 0, dmusic->ports); 200 dmusic->ports = NULL; 201 return; 202 } 203 204 memmove(&dmusic->ports[i], &dmusic->ports[i + 1], 205 (dmusic->num_ports - i) * sizeof(*dmusic->ports)); 206 dmusic->ports = HeapReAlloc(GetProcessHeap(), 0, dmusic->ports, 207 sizeof(*dmusic->ports) * dmusic->num_ports); 208 } 209 210 static HRESULT WINAPI IDirectMusic8Impl_EnumMasterClock(LPDIRECTMUSIC8 iface, DWORD index, LPDMUS_CLOCKINFO clock_info) 211 { 212 TRACE("(%p)->(%d, %p)\n", iface, index, clock_info); 213 214 if (!clock_info) 215 return E_POINTER; 216 217 if (index > 1) 218 return S_FALSE; 219 220 if (!index) 221 { 222 static const GUID guid_system_clock = { 0x58d58419, 0x71b4, 0x11d1, { 0xa7, 0x4c, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12 } }; 223 static const WCHAR name_system_clock[] = { 'S','y','s','t','e','m',' ','C','l','o','c','k',0 }; 224 225 clock_info->ctType = 0; 226 clock_info->guidClock = guid_system_clock; 227 strcpyW(clock_info->wszDescription, name_system_clock); 228 } 229 else 230 { 231 static const GUID guid_dsound_clock = { 0x58d58420, 0x71b4, 0x11d1, { 0xa7, 0x4c, 0x00, 0x00, 0xf8, 0x75, 0xac, 0x12 } }; 232 static const WCHAR name_dsound_clock[] = { 'D','i','r','e','c','t','S','o','u','n','d',' ','C','l','o','c','k',0 }; 233 234 clock_info->ctType = 0; 235 clock_info->guidClock = guid_dsound_clock; 236 strcpyW(clock_info->wszDescription, name_dsound_clock); 237 } 238 239 return S_OK; 240 } 241 242 static HRESULT WINAPI IDirectMusic8Impl_GetMasterClock(LPDIRECTMUSIC8 iface, LPGUID guid_clock, IReferenceClock** reference_clock) 243 { 244 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 245 246 TRACE("(%p)->(%p, %p)\n", This, guid_clock, reference_clock); 247 248 if (guid_clock) 249 *guid_clock = This->master_clock->pClockInfo.guidClock; 250 if (reference_clock) { 251 *reference_clock = &This->master_clock->IReferenceClock_iface; 252 IReferenceClock_AddRef(*reference_clock); 253 } 254 255 return S_OK; 256 } 257 258 static HRESULT WINAPI IDirectMusic8Impl_SetMasterClock(LPDIRECTMUSIC8 iface, REFGUID rguidClock) 259 { 260 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 261 262 FIXME("(%p)->(%s): stub\n", This, debugstr_dmguid(rguidClock)); 263 264 return S_OK; 265 } 266 267 static HRESULT WINAPI IDirectMusic8Impl_Activate(LPDIRECTMUSIC8 iface, BOOL enable) 268 { 269 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 270 int i; 271 HRESULT hr; 272 273 TRACE("(%p)->(%u)\n", This, enable); 274 275 for (i = 0; i < This->num_ports; i++) 276 { 277 hr = IDirectMusicPort_Activate(This->ports[i], enable); 278 if (FAILED(hr)) 279 return hr; 280 } 281 282 return S_OK; 283 } 284 285 static HRESULT WINAPI IDirectMusic8Impl_GetDefaultPort(LPDIRECTMUSIC8 iface, LPGUID guid_port) 286 { 287 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 288 HKEY hkGUID; 289 DWORD returnTypeGUID, sizeOfReturnBuffer = 50; 290 char returnBuffer[51]; 291 GUID defaultPortGUID; 292 WCHAR buff[51]; 293 294 TRACE("(%p)->(%p)\n", This, guid_port); 295 296 if ((RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic\\Defaults" , 0, KEY_READ, &hkGUID) != ERROR_SUCCESS) || 297 (RegQueryValueExA(hkGUID, "DefaultOutputPort", NULL, &returnTypeGUID, (LPBYTE)returnBuffer, &sizeOfReturnBuffer) != ERROR_SUCCESS)) 298 { 299 WARN(": registry entry missing\n" ); 300 *guid_port = CLSID_DirectMusicSynth; 301 return S_OK; 302 } 303 /* FIXME: Check return types to ensure we're interpreting data right */ 304 MultiByteToWideChar(CP_ACP, 0, returnBuffer, -1, buff, ARRAY_SIZE(buff)); 305 CLSIDFromString(buff, &defaultPortGUID); 306 *guid_port = defaultPortGUID; 307 308 return S_OK; 309 } 310 311 static HRESULT WINAPI IDirectMusic8Impl_SetDirectSound(IDirectMusic8 *iface, IDirectSound *dsound, 312 HWND hwnd) 313 { 314 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 315 HRESULT hr; 316 int i; 317 318 TRACE("(%p)->(%p, %p)\n", This, dsound, hwnd); 319 320 for (i = 0; i < This->num_ports; i++) 321 { 322 hr = IDirectMusicPort_SetDirectSound(This->ports[i], NULL, NULL); 323 if (FAILED(hr)) 324 return hr; 325 } 326 327 if (This->dsound) 328 IDirectSound_Release(This->dsound); 329 330 if (!dsound) { 331 hr = DirectSoundCreate8(NULL, (IDirectSound8 **)&This->dsound, NULL); 332 if (FAILED(hr)) 333 return hr; 334 hr = IDirectSound_SetCooperativeLevel(This->dsound, hwnd ? hwnd : GetForegroundWindow(), 335 DSSCL_PRIORITY); 336 if (FAILED(hr)) { 337 IDirectSound_Release(This->dsound); 338 This->dsound = NULL; 339 } 340 return hr; 341 } 342 343 IDirectSound_AddRef(dsound); 344 This->dsound = dsound; 345 346 return S_OK; 347 } 348 349 static HRESULT WINAPI IDirectMusic8Impl_SetExternalMasterClock(LPDIRECTMUSIC8 iface, IReferenceClock* clock) 350 { 351 IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface); 352 353 FIXME("(%p)->(%p): stub\n", This, clock); 354 355 return S_OK; 356 } 357 358 static const IDirectMusic8Vtbl DirectMusic8_Vtbl = { 359 IDirectMusic8Impl_QueryInterface, 360 IDirectMusic8Impl_AddRef, 361 IDirectMusic8Impl_Release, 362 IDirectMusic8Impl_EnumPort, 363 IDirectMusic8Impl_CreateMusicBuffer, 364 IDirectMusic8Impl_CreatePort, 365 IDirectMusic8Impl_EnumMasterClock, 366 IDirectMusic8Impl_GetMasterClock, 367 IDirectMusic8Impl_SetMasterClock, 368 IDirectMusic8Impl_Activate, 369 IDirectMusic8Impl_GetDefaultPort, 370 IDirectMusic8Impl_SetDirectSound, 371 IDirectMusic8Impl_SetExternalMasterClock 372 }; 373 374 static void create_system_ports_list(IDirectMusic8Impl* object) 375 { 376 static const WCHAR emulated[] = {' ','[','E','m','u','l','a','t','e','d',']',0}; 377 port_info * port; 378 ULONG nb_ports; 379 ULONG nb_midi_out; 380 ULONG nb_midi_in; 381 MIDIOUTCAPSW caps_out; 382 MIDIINCAPSW caps_in; 383 IDirectMusicSynth8* synth; 384 HRESULT hr; 385 ULONG i; 386 387 TRACE("(%p)\n", object); 388 389 /* NOTE: 390 - it seems some native versions get the rest of devices through dmusic32.EnumLegacyDevices...*sigh*...which is undocumented 391 - should we enum wave devices ? Native does not seem to 392 */ 393 394 nb_midi_out = midiOutGetNumDevs(); 395 nb_midi_in = midiInGetNumDevs(); 396 nb_ports = 1 /* midi mapper */ + nb_midi_out + nb_midi_in + 1 /* synth port */; 397 398 port = object->system_ports = HeapAlloc(GetProcessHeap(), 0, nb_ports * sizeof(port_info)); 399 if (!object->system_ports) 400 return; 401 402 /* Fill common port caps for all winmm ports */ 403 for (i = 0; i < (nb_ports - 1 /* synth port*/); i++) 404 { 405 object->system_ports[i].caps.dwSize = sizeof(DMUS_PORTCAPS); 406 object->system_ports[i].caps.dwType = DMUS_PORT_WINMM_DRIVER; 407 object->system_ports[i].caps.dwMemorySize = 0; 408 object->system_ports[i].caps.dwMaxChannelGroups = 1; 409 object->system_ports[i].caps.dwMaxVoices = 0; 410 object->system_ports[i].caps.dwMaxAudioChannels = 0; 411 object->system_ports[i].caps.dwEffectFlags = DMUS_EFFECT_NONE; 412 /* Fake port GUID */ 413 object->system_ports[i].caps.guidPort = IID_IUnknown; 414 object->system_ports[i].caps.guidPort.Data1 = i + 1; 415 } 416 417 /* Fill midi mapper port info */ 418 port->device = MIDI_MAPPER; 419 port->create = midi_out_port_create; 420 midiOutGetDevCapsW(MIDI_MAPPER, &caps_out, sizeof(caps_out)); 421 strcpyW(port->caps.wszDescription, caps_out.szPname); 422 strcatW(port->caps.wszDescription, emulated); 423 port->caps.dwFlags = DMUS_PC_SHAREABLE; 424 port->caps.dwClass = DMUS_PC_OUTPUTCLASS; 425 port++; 426 427 /* Fill midi out port info */ 428 for (i = 0; i < nb_midi_out; i++) 429 { 430 port->device = i; 431 port->create = midi_out_port_create; 432 midiOutGetDevCapsW(i, &caps_out, sizeof(caps_out)); 433 strcpyW(port->caps.wszDescription, caps_out.szPname); 434 strcatW(port->caps.wszDescription, emulated); 435 port->caps.dwFlags = DMUS_PC_SHAREABLE | DMUS_PC_EXTERNAL; 436 port->caps.dwClass = DMUS_PC_OUTPUTCLASS; 437 port++; 438 } 439 440 /* Fill midi in port info */ 441 for (i = 0; i < nb_midi_in; i++) 442 { 443 port->device = i; 444 port->create = midi_in_port_create; 445 midiInGetDevCapsW(i, &caps_in, sizeof(caps_in)); 446 strcpyW(port->caps.wszDescription, caps_in.szPname); 447 strcatW(port->caps.wszDescription, emulated); 448 port->caps.dwFlags = DMUS_PC_EXTERNAL; 449 port->caps.dwClass = DMUS_PC_INPUTCLASS; 450 port++; 451 } 452 453 /* Fill synth port info */ 454 port->create = synth_port_create; 455 hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynth8, (void**)&synth); 456 if (SUCCEEDED(hr)) 457 { 458 port->caps.dwSize = sizeof(port->caps); 459 hr = IDirectMusicSynth8_GetPortCaps(synth, &port->caps); 460 IDirectMusicSynth8_Release(synth); 461 } 462 if (FAILED(hr)) 463 nb_ports--; 464 465 object->num_system_ports = nb_ports; 466 } 467 468 /* For ClassFactory */ 469 HRESULT WINAPI DMUSIC_CreateDirectMusicImpl(LPCGUID riid, LPVOID* ret_iface, LPUNKNOWN unkouter) 470 { 471 IDirectMusic8Impl *dmusic; 472 HRESULT ret; 473 474 TRACE("(%s, %p, %p)\n", debugstr_guid(riid), ret_iface, unkouter); 475 476 *ret_iface = NULL; 477 if (unkouter) 478 return CLASS_E_NOAGGREGATION; 479 480 dmusic = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusic8Impl)); 481 if (!dmusic) 482 return E_OUTOFMEMORY; 483 484 dmusic->IDirectMusic8_iface.lpVtbl = &DirectMusic8_Vtbl; 485 dmusic->ref = 1; 486 ret = DMUSIC_CreateReferenceClockImpl(&IID_IReferenceClock, (void **)&dmusic->master_clock, NULL); 487 if (FAILED(ret)) { 488 HeapFree(GetProcessHeap(), 0, dmusic); 489 return ret; 490 } 491 492 create_system_ports_list(dmusic); 493 494 DMUSIC_LockModule(); 495 ret = IDirectMusic8Impl_QueryInterface(&dmusic->IDirectMusic8_iface, riid, ret_iface); 496 IDirectMusic8_Release(&dmusic->IDirectMusic8_iface); 497 498 return ret; 499 } 500