dmusic: COM cleanup of IDirectMusic8.
[wine] / dlls / dmusic / dmusic.c
1 /* IDirectMusic8 Implementation
2  *
3  * Copyright (C) 2003-2004 Rok Mandeljc
4  *
5  * This program is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
18  */
19
20 #include <stdio.h>
21
22 #include "dmusic_private.h"
23
24 WINE_DEFAULT_DEBUG_CHANNEL(dmusic);
25
26 static inline IDirectMusic8Impl *impl_from_IDirectMusic8(IDirectMusic8 *iface)
27 {
28     return CONTAINING_RECORD(iface, IDirectMusic8Impl, IDirectMusic8_iface);
29 }
30
31 /* IDirectMusic8Impl IUnknown part: */
32 static HRESULT WINAPI IDirectMusic8Impl_QueryInterface(LPDIRECTMUSIC8 iface, REFIID riid, LPVOID *ppobj)
33 {
34         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
35         TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj);
36
37         if (IsEqualIID (riid, &IID_IUnknown) || 
38             IsEqualIID (riid, &IID_IDirectMusic) ||
39             IsEqualIID (riid, &IID_IDirectMusic2) ||
40             IsEqualIID (riid, &IID_IDirectMusic8)) {
41                 IUnknown_AddRef(iface);
42                 *ppobj = This;
43                 return S_OK;
44         }
45
46         WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ppobj);
47         return E_NOINTERFACE;
48 }
49
50 static ULONG WINAPI IDirectMusic8Impl_AddRef(LPDIRECTMUSIC8 iface)
51 {
52         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
53         ULONG refCount = InterlockedIncrement(&This->ref);
54
55         TRACE("(%p)->(ref before=%u)\n", This, refCount - 1);
56
57         DMUSIC_LockModule();
58
59         return refCount;
60 }
61
62 static ULONG WINAPI IDirectMusic8Impl_Release(LPDIRECTMUSIC8 iface)
63 {
64         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
65         ULONG refCount = InterlockedDecrement(&This->ref);
66
67         TRACE("(%p)->(ref before=%u)\n", This, refCount + 1);
68
69         if (!refCount) {
70                 HeapFree(GetProcessHeap(), 0, This->ppPorts);
71                 HeapFree(GetProcessHeap(), 0, This);
72         }
73
74         DMUSIC_UnlockModule();
75         
76         return refCount;
77 }
78
79 /* IDirectMusic8Impl IDirectMusic part: */
80 static HRESULT WINAPI IDirectMusic8Impl_EnumPort(LPDIRECTMUSIC8 iface, DWORD index, LPDMUS_PORTCAPS port_caps)
81 {
82     IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
83     ULONG nb_midi_out;
84     ULONG nb_midi_in;
85     const WCHAR emulated[] = {' ','[','E','m','u','l','a','t','e','d',']',0};
86
87     TRACE("(%p, %d, %p)\n", This, index, port_caps);
88
89     if (!port_caps)
90         return E_POINTER;
91
92     /* NOTE: It seems some native versions get the rest of devices through dmusic32.EnumLegacyDevices...*sigh*...which is undocumented */
93
94     /* NOTE: Should we enum wave devices ? Native does not seem to */
95
96     /* Fill common port caps for winmm ports */
97     port_caps->dwType = DMUS_PORT_WINMM_DRIVER;
98     port_caps->dwMemorySize = 0;
99     port_caps->dwMaxChannelGroups = 1;
100     port_caps->dwMaxVoices = 0;
101     port_caps->dwMaxAudioChannels = 0;
102     port_caps->dwEffectFlags = DMUS_EFFECT_NONE;
103     /* Fake port GUID */
104     port_caps->guidPort = IID_IUnknown;
105     port_caps->guidPort.Data1 = index + 1;
106
107     nb_midi_out = midiOutGetNumDevs();
108
109     if (index == 0)
110     {
111         MIDIOUTCAPSW caps;
112         midiOutGetDevCapsW(MIDI_MAPPER, &caps, sizeof(caps));
113         strcpyW(port_caps->wszDescription, caps.szPname);
114         strcatW(port_caps->wszDescription, emulated);
115         port_caps->dwFlags = DMUS_PC_SHAREABLE;
116         port_caps->dwClass = DMUS_PC_OUTPUTCLASS;
117         TRACE("Enumerating port: %s\n", debugstr_w(port_caps->wszDescription));
118         return S_OK;
119     }
120
121     if (index < (nb_midi_out + 1))
122     {
123         MIDIOUTCAPSW caps;
124         midiOutGetDevCapsW(index - 1, &caps, sizeof(caps));
125         strcpyW(port_caps->wszDescription, caps.szPname);
126         strcatW(port_caps->wszDescription, emulated);
127         port_caps->dwFlags = DMUS_PC_SHAREABLE | DMUS_PC_EXTERNAL;
128         port_caps->dwClass = DMUS_PC_OUTPUTCLASS;
129         TRACE("Enumerating port: %s\n", debugstr_w(port_caps->wszDescription));
130         return S_OK;
131     }
132
133     nb_midi_in = midiInGetNumDevs();
134
135     if (index < (nb_midi_in + nb_midi_out + 1))
136     {
137         MIDIINCAPSW caps;
138         midiInGetDevCapsW(index - nb_midi_out - 1, &caps, sizeof(caps));
139         strcpyW(port_caps->wszDescription, caps.szPname);
140         strcatW(port_caps->wszDescription, emulated);
141         port_caps->dwFlags = DMUS_PC_EXTERNAL;
142         port_caps->dwClass = DMUS_PC_INPUTCLASS;
143         TRACE("Enumerating port: %s\n", debugstr_w(port_caps->wszDescription));
144         return S_OK;
145     }
146
147     if (index == (nb_midi_in + nb_midi_out + 1))
148     {
149         IDirectMusicSynth8* synth = NULL;
150         HRESULT hr;
151         hr = CoCreateInstance(&CLSID_DirectMusicSynth, NULL, CLSCTX_INPROC_SERVER, &IID_IDirectMusicSynth8, (void**)&synth);
152         if (SUCCEEDED(hr))
153             hr = IDirectMusicSynth8_GetPortCaps(synth, port_caps);
154         if (SUCCEEDED(hr))
155             TRACE("Enumerating port: %s\n", debugstr_w(port_caps->wszDescription));
156         if (synth)
157             IDirectMusicSynth8_Release(synth);
158         return hr;
159     }
160
161     return S_FALSE;
162 }
163
164 static HRESULT WINAPI IDirectMusic8Impl_CreateMusicBuffer(LPDIRECTMUSIC8 iface, LPDMUS_BUFFERDESC pBufferDesc, LPDIRECTMUSICBUFFER* ppBuffer, LPUNKNOWN pUnkOuter)
165 {
166         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
167
168         TRACE("(%p, %p, %p, %p)\n", This, pBufferDesc, ppBuffer, pUnkOuter);
169
170         if (pUnkOuter)
171                 return CLASS_E_NOAGGREGATION;
172
173         if (!pBufferDesc || !ppBuffer)
174                 return E_POINTER;
175
176         return DMUSIC_CreateDirectMusicBufferImpl(pBufferDesc, (LPVOID)ppBuffer);
177 }
178
179 static HRESULT WINAPI IDirectMusic8Impl_CreatePort(LPDIRECTMUSIC8 iface, REFCLSID rclsidPort, LPDMUS_PORTPARAMS pPortParams, LPDIRECTMUSICPORT* ppPort, LPUNKNOWN pUnkOuter)
180 {
181         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
182         int i;
183         DMUS_PORTCAPS PortCaps;
184         IDirectMusicPort* pNewPort = NULL;
185         HRESULT hr;
186         GUID default_port;
187         const GUID *request_port = rclsidPort;
188
189         TRACE("(%p, %s, %p, %p, %p)\n", This, debugstr_dmguid(rclsidPort), pPortParams, ppPort, pUnkOuter);
190
191         if (TRACE_ON(dmusic))
192                 dump_DMUS_PORTPARAMS(pPortParams);
193
194         if (!rclsidPort)
195                 return E_POINTER;
196         if (!pPortParams)
197                 return E_INVALIDARG;
198         if (!ppPort)
199                 return E_POINTER;
200         if (pUnkOuter)
201                 return CLASS_E_NOAGGREGATION;
202
203         if (TRACE_ON(dmusic))
204                 dump_DMUS_PORTPARAMS(pPortParams);
205
206         ZeroMemory(&PortCaps, sizeof(DMUS_PORTCAPS));
207         PortCaps.dwSize = sizeof(DMUS_PORTCAPS);
208
209         if(IsEqualGUID(request_port, &GUID_NULL)){
210                 hr = IDirectMusic8_GetDefaultPort(iface, &default_port);
211                 if(FAILED(hr))
212                         return hr;
213                 request_port = &default_port;
214         }
215
216         for (i = 0; S_FALSE != IDirectMusic8Impl_EnumPort(iface, i, &PortCaps); i++) {
217                 if (IsEqualCLSID (request_port, &PortCaps.guidPort)) {
218                         hr = DMUSIC_CreateDirectMusicPortImpl(&IID_IDirectMusicPort, (LPVOID*) &pNewPort, (LPUNKNOWN) This, pPortParams, &PortCaps);
219                         if (FAILED(hr)) {
220                           *ppPort = NULL;
221                           return hr;
222                         }
223                         This->nrofports++;
224                         if (!This->ppPorts) This->ppPorts = HeapAlloc(GetProcessHeap(), 0, sizeof(LPDIRECTMUSICPORT) * This->nrofports);
225                         else This->ppPorts = HeapReAlloc(GetProcessHeap(), 0, This->ppPorts, sizeof(LPDIRECTMUSICPORT) * This->nrofports);
226                         This->ppPorts[This->nrofports - 1] = pNewPort;
227                         *ppPort = pNewPort;
228                         return S_OK;
229                 }
230         }
231         return E_NOINTERFACE;
232 }
233
234 static HRESULT WINAPI IDirectMusic8Impl_EnumMasterClock(LPDIRECTMUSIC8 iface, DWORD index, LPDMUS_CLOCKINFO clock_info)
235 {
236     IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
237
238     FIXME("(%p)->(%d, %p): stub\n", This, index, clock_info);
239
240     return S_FALSE;
241 }
242
243 static HRESULT WINAPI IDirectMusic8Impl_GetMasterClock(LPDIRECTMUSIC8 iface, LPGUID pguidClock, IReferenceClock** ppReferenceClock)
244 {
245         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
246
247         TRACE("(%p, %p, %p)\n", This, pguidClock, ppReferenceClock);
248         if (pguidClock)
249                 *pguidClock = This->pMasterClock->pClockInfo.guidClock;
250         if(ppReferenceClock)
251                 *ppReferenceClock = (IReferenceClock *)This->pMasterClock;
252
253         return S_OK;
254 }
255
256 static HRESULT WINAPI IDirectMusic8Impl_SetMasterClock(LPDIRECTMUSIC8 iface, REFGUID rguidClock)
257 {
258     IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
259
260     FIXME("(%p)->(%s): stub\n", This, debugstr_dmguid(rguidClock));
261
262     return S_OK;
263 }
264
265 static HRESULT WINAPI IDirectMusic8Impl_Activate(LPDIRECTMUSIC8 iface, BOOL fEnable)
266 {
267         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
268         int i;
269         
270         FIXME("(%p, %d): stub\n", This, fEnable);
271         for (i = 0; i < This->nrofports; i++) {
272             IDirectMusicPort_Activate(This->ppPorts[i], fEnable);
273         }
274         
275         return S_OK;
276 }
277
278 static HRESULT WINAPI IDirectMusic8Impl_GetDefaultPort(LPDIRECTMUSIC8 iface, LPGUID pguidPort)
279 {
280         IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
281         HKEY hkGUID;
282         DWORD returnTypeGUID, sizeOfReturnBuffer = 50;
283         char returnBuffer[51];
284         GUID defaultPortGUID;
285         WCHAR buff[51];
286
287         TRACE("(%p, %p)\n", This, pguidPort);
288         if ((RegOpenKeyExA(HKEY_LOCAL_MACHINE, "Software\\Microsoft\\DirectMusic\\Defaults" , 0, KEY_READ, &hkGUID) != ERROR_SUCCESS) || 
289             (RegQueryValueExA(hkGUID, "DefaultOutputPort", NULL, &returnTypeGUID, (LPBYTE)returnBuffer, &sizeOfReturnBuffer) != ERROR_SUCCESS))
290         {
291                 WARN(": registry entry missing\n" );
292                 *pguidPort = CLSID_DirectMusicSynth;
293                 return S_OK;
294         }
295         /* FIXME: Check return types to ensure we're interpreting data right */
296         MultiByteToWideChar(CP_ACP, 0, returnBuffer, -1, buff, sizeof(buff) / sizeof(WCHAR));
297         CLSIDFromString(buff, &defaultPortGUID);
298         *pguidPort = defaultPortGUID;
299         
300         return S_OK;
301 }
302
303 static HRESULT WINAPI IDirectMusic8Impl_SetDirectSound(LPDIRECTMUSIC8 iface, LPDIRECTSOUND dsound, HWND wnd)
304 {
305     IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
306
307     FIXME("(%p)->(%p, %p): stub\n", This, dsound, wnd);
308
309     return S_OK;
310 }
311
312 static HRESULT WINAPI IDirectMusic8Impl_SetExternalMasterClock(LPDIRECTMUSIC8 iface, IReferenceClock* clock)
313 {
314     IDirectMusic8Impl *This = impl_from_IDirectMusic8(iface);
315
316     FIXME("(%p)->(%p): stub\n", This, clock);
317
318     return S_OK;
319 }
320
321 static const IDirectMusic8Vtbl DirectMusic8_Vtbl = {
322         IDirectMusic8Impl_QueryInterface,
323         IDirectMusic8Impl_AddRef,
324         IDirectMusic8Impl_Release,
325         IDirectMusic8Impl_EnumPort,
326         IDirectMusic8Impl_CreateMusicBuffer,
327         IDirectMusic8Impl_CreatePort,
328         IDirectMusic8Impl_EnumMasterClock,
329         IDirectMusic8Impl_GetMasterClock,
330         IDirectMusic8Impl_SetMasterClock,
331         IDirectMusic8Impl_Activate,
332         IDirectMusic8Impl_GetDefaultPort,
333         IDirectMusic8Impl_SetDirectSound,
334         IDirectMusic8Impl_SetExternalMasterClock
335 };
336
337 /* for ClassFactory */
338 HRESULT WINAPI DMUSIC_CreateDirectMusicImpl (LPCGUID lpcGUID, LPVOID* ppobj, LPUNKNOWN pUnkOuter) {
339         IDirectMusic8Impl *dmusic;
340
341         TRACE("(%p,%p,%p)\n",lpcGUID, ppobj, pUnkOuter);
342
343         dmusic = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IDirectMusic8Impl));
344         if (NULL == dmusic) {
345                 *ppobj = NULL;
346                 return E_OUTOFMEMORY;
347         }
348         dmusic->IDirectMusic8_iface.lpVtbl = &DirectMusic8_Vtbl;
349         dmusic->ref = 0; /* will be inited with QueryInterface */
350         dmusic->pMasterClock = NULL;
351         dmusic->ppPorts = NULL;
352         dmusic->nrofports = 0;
353         DMUSIC_CreateReferenceClockImpl (&IID_IReferenceClock, (LPVOID*)&dmusic->pMasterClock, NULL);
354         
355         return IDirectMusic8Impl_QueryInterface ((LPDIRECTMUSIC8)dmusic, lpcGUID, ppobj);
356 }