include/mscvpdb.h: Use flexible array members for the rest of structures.
[wine.git] / dlls / dmime / audiopath.c
blob3389f965b8ffc62a5d967f7d23dabd1d17d485a6
1 /* IDirectMusicAudioPath Implementation
3 * Copyright (C) 2003-2004 Rok Mandeljc
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.
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.
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
20 #include "dmime_private.h"
22 WINE_DEFAULT_DEBUG_CHANNEL(dmime);
24 struct IDirectMusicAudioPathImpl {
25 IDirectMusicAudioPath IDirectMusicAudioPath_iface;
26 LONG ref;
27 IDirectMusicPerformance8* pPerf;
28 IDirectMusicGraph* pToolGraph;
29 IDirectSoundBuffer* pDSBuffer;
30 IDirectSoundBuffer* pPrimary;
32 BOOL fActive;
35 struct audio_path_pchannel_to_buffer
37 struct list entry;
38 DMUS_IO_PCHANNELTOBUFFER_HEADER header;
39 GUID guids[];
42 C_ASSERT(sizeof(struct audio_path_pchannel_to_buffer) == offsetof(struct audio_path_pchannel_to_buffer, guids[0]));
44 struct audio_path_port_config
46 struct list entry;
47 DMUS_IO_PORTCONFIG_HEADER header;
48 DMUS_PORTPARAMS8 params;
50 struct list pchannel_to_buffer_entries;
53 struct audio_path_config
55 IUnknown IUnknown_iface;
56 struct dmobject dmobj;
57 LONG ref;
59 struct list port_config_entries;
62 static inline struct IDirectMusicAudioPathImpl *impl_from_IDirectMusicAudioPath(IDirectMusicAudioPath *iface)
64 return CONTAINING_RECORD(iface, struct IDirectMusicAudioPathImpl, IDirectMusicAudioPath_iface);
67 static inline struct audio_path_config *impl_from_IPersistStream(IPersistStream *iface)
69 return CONTAINING_RECORD(iface, struct audio_path_config, dmobj.IPersistStream_iface);
72 void set_audiopath_perf_pointer(IDirectMusicAudioPath *iface, IDirectMusicPerformance8 *performance)
74 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
75 This->pPerf = performance;
78 void set_audiopath_dsound_buffer(IDirectMusicAudioPath *iface, IDirectSoundBuffer *buffer)
80 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
81 This->pDSBuffer = buffer;
84 void set_audiopath_primary_dsound_buffer(IDirectMusicAudioPath *iface, IDirectSoundBuffer *buffer)
86 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
87 This->pPrimary = buffer;
90 /*****************************************************************************
91 * IDirectMusicAudioPathImpl implementation
93 static HRESULT WINAPI IDirectMusicAudioPathImpl_QueryInterface (IDirectMusicAudioPath *iface, REFIID riid, void **ppobj)
95 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
97 TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj);
99 *ppobj = NULL;
101 if (IsEqualIID (riid, &IID_IDirectMusicAudioPath) || IsEqualIID (riid, &IID_IUnknown))
102 *ppobj = &This->IDirectMusicAudioPath_iface;
104 if (*ppobj) {
105 IUnknown_AddRef((IUnknown*)*ppobj);
106 return S_OK;
109 WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ppobj);
110 return E_NOINTERFACE;
113 static ULONG WINAPI IDirectMusicAudioPathImpl_AddRef (IDirectMusicAudioPath *iface)
115 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
116 ULONG ref = InterlockedIncrement(&This->ref);
118 TRACE("(%p): ref=%ld\n", This, ref);
120 return ref;
123 static ULONG WINAPI IDirectMusicAudioPathImpl_Release (IDirectMusicAudioPath *iface)
125 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
126 ULONG ref = InterlockedDecrement(&This->ref);
128 TRACE("(%p): ref=%ld\n", This, ref);
130 if (ref == 0) {
131 if (This->pPrimary)
132 IDirectSoundBuffer_Release(This->pPrimary);
133 if (This->pDSBuffer)
134 IDirectSoundBuffer_Release(This->pDSBuffer);
135 This->pPerf = NULL;
136 free(This);
139 return ref;
142 static HRESULT WINAPI IDirectMusicAudioPathImpl_GetObjectInPath (IDirectMusicAudioPath *iface, DWORD dwPChannel, DWORD dwStage, DWORD dwBuffer,
143 REFGUID guidObject, DWORD dwIndex, REFGUID iidInterface, void** ppObject)
145 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
146 HRESULT hr;
148 FIXME("(%p, %ld, %ld, %ld, %s, %ld, %s, %p): stub\n", This, dwPChannel, dwStage, dwBuffer, debugstr_dmguid(guidObject),
149 dwIndex, debugstr_dmguid(iidInterface), ppObject);
151 switch (dwStage) {
152 case DMUS_PATH_BUFFER:
153 if (This->pDSBuffer)
155 if (IsEqualIID (iidInterface, &IID_IUnknown) ||
156 IsEqualIID (iidInterface, &IID_IDirectSoundBuffer) ||
157 IsEqualIID (iidInterface, &IID_IDirectSoundBuffer8) ||
158 IsEqualIID (iidInterface, &IID_IDirectSound3DBuffer)) {
159 return IDirectSoundBuffer_QueryInterface(This->pDSBuffer, iidInterface, ppObject);
162 WARN("Unsupported interface %s\n", debugstr_dmguid(iidInterface));
163 *ppObject = NULL;
164 return E_NOINTERFACE;
166 break;
168 case DMUS_PATH_PRIMARY_BUFFER: {
169 if (IsEqualIID (iidInterface, &IID_IDirectSound3DListener)) {
170 IDirectSoundBuffer_QueryInterface(This->pPrimary, &IID_IDirectSound3DListener, ppObject);
171 return S_OK;
172 } else {
173 FIXME("bad iid...\n");
176 break;
178 case DMUS_PATH_AUDIOPATH_GRAPH:
180 if (IsEqualIID (iidInterface, &IID_IDirectMusicGraph)) {
181 if (NULL == This->pToolGraph) {
182 IDirectMusicGraph* pGraph;
183 hr = create_dmgraph(&IID_IDirectMusicGraph, (void**)&pGraph);
184 if (FAILED(hr))
185 return hr;
186 This->pToolGraph = pGraph;
188 *ppObject = This->pToolGraph;
189 IDirectMusicGraph_AddRef((LPDIRECTMUSICGRAPH) *ppObject);
190 return S_OK;
193 break;
195 case DMUS_PATH_AUDIOPATH_TOOL:
197 /* TODO */
199 break;
201 case DMUS_PATH_PERFORMANCE:
203 /* TODO check wanted GUID */
204 *ppObject = This->pPerf;
205 IUnknown_AddRef((LPUNKNOWN) *ppObject);
206 return S_OK;
208 break;
210 case DMUS_PATH_PERFORMANCE_GRAPH:
212 IDirectMusicGraph* pPerfoGraph = NULL;
213 IDirectMusicPerformance8_GetGraph(This->pPerf, &pPerfoGraph);
214 if (NULL == pPerfoGraph) {
215 IDirectMusicGraph* pGraph = NULL;
216 hr = create_dmgraph(&IID_IDirectMusicGraph, (void**)&pGraph);
217 if (FAILED(hr))
218 return hr;
219 IDirectMusicPerformance8_SetGraph(This->pPerf, pGraph);
220 pPerfoGraph = pGraph;
222 *ppObject = pPerfoGraph;
223 return S_OK;
225 break;
227 case DMUS_PATH_PERFORMANCE_TOOL:
228 default:
229 break;
232 *ppObject = NULL;
233 return E_INVALIDARG;
236 static HRESULT WINAPI IDirectMusicAudioPathImpl_Activate(IDirectMusicAudioPath *iface, BOOL activate)
238 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
240 FIXME("(%p, %d): semi-stub\n", This, activate);
242 if (!!activate == This->fActive)
243 return S_FALSE;
245 if (!activate && This->pDSBuffer) {
246 /* Path is being deactivated */
247 IDirectSoundBuffer_Stop(This->pDSBuffer);
250 This->fActive = !!activate;
252 return S_OK;
255 static HRESULT WINAPI IDirectMusicAudioPathImpl_SetVolume (IDirectMusicAudioPath *iface, LONG lVolume, DWORD dwDuration)
257 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
258 FIXME("(%p, %li, %ld): stub\n", This, lVolume, dwDuration);
259 return S_OK;
262 static HRESULT WINAPI IDirectMusicAudioPathImpl_ConvertPChannel (IDirectMusicAudioPath *iface, DWORD dwPChannelIn, DWORD* pdwPChannelOut)
264 struct IDirectMusicAudioPathImpl *This = impl_from_IDirectMusicAudioPath(iface);
265 FIXME("(%p, %ld, %p): stub\n", This, dwPChannelIn, pdwPChannelOut);
266 return S_OK;
269 static const IDirectMusicAudioPathVtbl DirectMusicAudioPathVtbl = {
270 IDirectMusicAudioPathImpl_QueryInterface,
271 IDirectMusicAudioPathImpl_AddRef,
272 IDirectMusicAudioPathImpl_Release,
273 IDirectMusicAudioPathImpl_GetObjectInPath,
274 IDirectMusicAudioPathImpl_Activate,
275 IDirectMusicAudioPathImpl_SetVolume,
276 IDirectMusicAudioPathImpl_ConvertPChannel
279 /* IDirectMusicObject */
280 static HRESULT WINAPI path_config_IDirectMusicObject_ParseDescriptor(IDirectMusicObject *iface,
281 IStream *stream, DMUS_OBJECTDESC *desc)
283 struct chunk_entry riff = {0};
284 HRESULT hr;
286 TRACE("(%p, %p, %p)\n", iface, stream, desc);
288 if (!stream)
289 return E_POINTER;
290 if (!desc || desc->dwSize != sizeof(*desc))
291 return E_INVALIDARG;
293 if ((hr = stream_get_chunk(stream, &riff)) != S_OK)
294 return hr;
295 if (riff.id != FOURCC_RIFF || riff.type != DMUS_FOURCC_AUDIOPATH_FORM) {
296 TRACE("loading failed: unexpected %s\n", debugstr_chunk(&riff));
297 stream_skip_chunk(stream, &riff);
298 return DMUS_E_CHUNKNOTFOUND;
301 hr = dmobj_parsedescriptor(stream, &riff, desc,
302 DMUS_OBJ_OBJECT|DMUS_OBJ_NAME|DMUS_OBJ_CATEGORY|DMUS_OBJ_VERSION);
303 if (FAILED(hr))
304 return hr;
306 desc->guidClass = CLSID_DirectMusicAudioPathConfig;
307 desc->dwValidData |= DMUS_OBJ_CLASS;
309 dump_DMUS_OBJECTDESC(desc);
310 return S_OK;
313 static const IDirectMusicObjectVtbl dmobject_vtbl = {
314 dmobj_IDirectMusicObject_QueryInterface,
315 dmobj_IDirectMusicObject_AddRef,
316 dmobj_IDirectMusicObject_Release,
317 dmobj_IDirectMusicObject_GetDescriptor,
318 dmobj_IDirectMusicObject_SetDescriptor,
319 path_config_IDirectMusicObject_ParseDescriptor
322 static HRESULT parse_pchannel_to_buffers_list(struct audio_path_port_config *This, IStream *stream, const struct chunk_entry *pchl)
324 struct chunk_entry chunk = { .parent = pchl };
325 struct audio_path_pchannel_to_buffer *pchannel_to_buffer = NULL;
326 DMUS_IO_PCHANNELTOBUFFER_HEADER header;
327 SIZE_T bytes;
328 ULONG i;
329 HRESULT hr;
331 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
333 switch (chunk.id)
335 case DMUS_FOURCC_PCHANNELS_ITEM:
336 hr = stream_read(stream, &header, sizeof(header));
337 if (FAILED(hr))
338 break;
340 TRACE("Got PChannelToBuffer header:\n");
341 TRACE("\tdwPChannelCount: %lu\n", header.dwPChannelCount);
342 TRACE("\tdwPChannelBase: %lu\n", header.dwPChannelBase);
343 TRACE("\tdwBufferCount: %lu\n", header.dwBufferCount);
344 TRACE("\tdwFlags: %lx\n", header.dwFlags);
346 bytes = chunk.size - sizeof(header);
348 if (bytes != sizeof(GUID) * header.dwBufferCount)
350 WARN("Invalid size of GUIDs array\n");
351 hr = E_FAIL;
352 break;
355 pchannel_to_buffer = calloc(1, sizeof(*pchannel_to_buffer) + bytes);
356 if (!pchannel_to_buffer)
358 hr = E_OUTOFMEMORY;
359 break;
362 pchannel_to_buffer->header = header;
363 TRACE("number of GUIDs: %lu\n", pchannel_to_buffer->header.dwBufferCount);
365 hr = stream_read(stream, pchannel_to_buffer->guids, bytes);
366 if (FAILED(hr))
367 break;
369 for (i = 0; i < pchannel_to_buffer->header.dwBufferCount; i++)
370 TRACE("\tguids[%lu]: %s\n", i, debugstr_dmguid(&pchannel_to_buffer->guids[i]));
372 list_add_tail(&This->pchannel_to_buffer_entries, &pchannel_to_buffer->entry);
373 pchannel_to_buffer = NULL;
374 break;
375 case FOURCC_LIST:
376 if (chunk.type == DMUS_FOURCC_UNFO_LIST)
377 TRACE("Unfo list in PChannelToBuffer is ignored\n");
378 else
379 WARN("Unknown %s\n", debugstr_chunk(&chunk));
380 break;
381 case MAKEFOURCC('p','b','g','d'):
382 FIXME("Undocumented %s\n", debugstr_chunk(&chunk));
383 break;
384 default:
385 WARN("Unknown %s\n", debugstr_chunk(&chunk));
386 break;
389 if (FAILED(hr))
390 break;
393 if (FAILED(hr) && pchannel_to_buffer)
394 free(pchannel_to_buffer);
395 return hr;
398 static void port_config_destroy(struct audio_path_port_config *port_config)
400 struct audio_path_pchannel_to_buffer *pchannel_to_buffer, *next_pchannel_to_buffer;
401 LIST_FOR_EACH_ENTRY_SAFE(pchannel_to_buffer, next_pchannel_to_buffer, &port_config->pchannel_to_buffer_entries,
402 struct audio_path_pchannel_to_buffer, entry)
404 list_remove(&pchannel_to_buffer->entry);
405 free(pchannel_to_buffer);
407 free(port_config);
410 static HRESULT parse_port_config_list(struct audio_path_config *This, IStream *stream, const struct chunk_entry *pcfl)
412 struct chunk_entry chunk = { .parent = pcfl };
413 struct audio_path_port_config *port_config = calloc(1, sizeof(*port_config));
414 HRESULT hr;
416 if (!port_config)
417 return E_OUTOFMEMORY;
419 list_init(&port_config->pchannel_to_buffer_entries);
421 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
423 switch (chunk.id)
425 case DMUS_FOURCC_PORTCONFIG_ITEM:
426 hr = stream_chunk_get_data(stream, &chunk, &port_config->header, sizeof(port_config->header));
427 if (FAILED(hr))
428 break;
429 TRACE("Got PortConfig header:\n");
430 TRACE("\tdwPChannelBase: %lu\n", port_config->header.dwPChannelBase);
431 TRACE("\tdwPChannelCount: %lu\n", port_config->header.dwPChannelCount);
432 TRACE("\tdwFlags: %lx\n", port_config->header.dwFlags);
433 TRACE("\tguidPort: %s\n", debugstr_dmguid(&port_config->header.guidPort));
434 break;
435 case DMUS_FOURCC_PORTPARAMS_ITEM:
436 hr = stream_chunk_get_data(stream, &chunk, &port_config->params, sizeof(port_config->params));
437 if (FAILED(hr))
438 break;
439 TRACE("Got PortConfig params:\n");
440 TRACE("\tdwSize: %lu\n", port_config->params.dwSize);
441 TRACE("\tdwValidParams: %lx\n", port_config->params.dwValidParams);
442 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_VOICES)
443 TRACE("\tvoices: %lu\n", port_config->params.dwVoices);
444 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS)
445 TRACE("\tchannel groups: %lu\n", port_config->params.dwChannelGroups);
446 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS)
447 TRACE("\taudio channels: %lu\n", port_config->params.dwAudioChannels);
448 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_SAMPLERATE)
449 TRACE("\tsample rate: %lu\n", port_config->params.dwSampleRate);
450 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_EFFECTS)
451 TRACE("\teffects: %lx\n", port_config->params.dwEffectFlags);
452 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_SHARE)
453 TRACE("\tshare: %d\n", port_config->params.fShare);
454 if (port_config->params.dwValidParams & DMUS_PORTPARAMS_FEATURES)
455 TRACE("\tfeatures: %lx\n", port_config->params.dwFeatures);
456 break;
457 case FOURCC_LIST:
458 if (chunk.type == DMUS_FOURCC_DSBUFFER_LIST)
459 FIXME("DirectSound buffer descriptors is not supported\n");
460 else if (chunk.type == DMUS_FOURCC_PCHANNELS_LIST)
461 hr = parse_pchannel_to_buffers_list(port_config, stream, &chunk);
462 else if (chunk.type == DMUS_FOURCC_UNFO_LIST)
463 TRACE("Unfo list in PortConfig is ignored\n");
464 else
465 WARN("Unknown %s\n", debugstr_chunk(&chunk));
466 break;
467 default:
468 WARN("Unknown %s\n", debugstr_chunk(&chunk));
471 if (FAILED(hr))
472 break;
475 if (FAILED(hr))
476 port_config_destroy(port_config);
477 else
478 list_add_tail(&This->port_config_entries, &port_config->entry);
479 return hr;
482 static HRESULT parse_port_configs_list(struct audio_path_config *This, IStream *stream, const struct chunk_entry *pcsl)
484 struct chunk_entry chunk = { .parent = pcsl };
485 HRESULT hr;
487 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
489 switch (MAKE_IDTYPE(chunk.id, chunk.type))
491 case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_PORTCONFIG_LIST):
492 hr = parse_port_config_list(This, stream, &chunk);
493 if (FAILED(hr))
494 return hr;
495 break;
496 default:
497 WARN("Unknown %s\n", debugstr_chunk(&chunk));
498 break;
501 return SUCCEEDED(hr) ? S_OK : hr;
504 /* IPersistStream */
505 static HRESULT WINAPI path_config_IPersistStream_Load(IPersistStream *iface, IStream *stream)
507 struct audio_path_config *This = impl_from_IPersistStream(iface);
508 struct chunk_entry riff = {0}, chunk = {0};
509 HRESULT hr = S_OK;
511 FIXME("(%p, %p): Loading\n", This, stream);
513 if (!stream)
514 return E_POINTER;
516 hr = stream_get_chunk(stream, &riff);
517 if (FAILED(hr))
519 WARN("Failed to get chunk %#lx.\n", hr);
520 return hr;
523 if (MAKE_IDTYPE(riff.id, riff.type) != MAKE_IDTYPE(FOURCC_RIFF, DMUS_FOURCC_AUDIOPATH_FORM))
525 WARN("loading failed: unexpected %s\n", debugstr_chunk(&riff));
526 return E_UNEXPECTED;
529 if (FAILED(hr = dmobj_parsedescriptor(stream, &riff, &This->dmobj.desc,
530 DMUS_OBJ_OBJECT | DMUS_OBJ_VERSION | DMUS_OBJ_NAME | DMUS_OBJ_CATEGORY))
531 || FAILED(hr = stream_reset_chunk_data(stream, &riff)))
533 WARN("Failed to parse descriptor %#lx.\n", hr);
534 return hr;
536 This->dmobj.desc.guidClass = CLSID_DirectMusicAudioPathConfig;
537 This->dmobj.desc.dwValidData |= DMUS_OBJ_CLASS;
539 chunk.parent = &riff;
540 while ((hr = stream_next_chunk(stream, &chunk)) == S_OK)
542 switch (MAKE_IDTYPE(chunk.id, chunk.type))
544 case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_PORTCONFIGS_LIST):
545 hr = parse_port_configs_list(This, stream, &chunk);
546 if (FAILED(hr))
547 return hr;
548 case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_DSBUFFER_LIST):
549 FIXME("buffer attributes are not supported\n");
550 break;
551 case MAKE_IDTYPE(FOURCC_LIST, MAKEFOURCC('p','a','p','d')):
552 FIXME("Undocumented %s\n", debugstr_chunk(&chunk));
553 break;
554 case MAKE_IDTYPE(FOURCC_LIST, DMUS_FOURCC_UNFO_LIST):
555 WARN("Unknown %s\n", debugstr_chunk(&chunk));
556 break;
557 case DMUS_FOURCC_GUID_CHUNK:
558 case DMUS_FOURCC_VERSION_CHUNK:
559 /* Already parsed in dmobj_parsedescriptor. */
560 break;
561 default:
562 WARN("Unknown %s\n", debugstr_chunk(&chunk));
563 break;
566 TRACE("Finished parsing %#lx\n", hr);
567 return SUCCEEDED(hr) ? S_OK : hr;
570 static const IPersistStreamVtbl persiststream_vtbl = {
571 dmobj_IPersistStream_QueryInterface,
572 dmobj_IPersistStream_AddRef,
573 dmobj_IPersistStream_Release,
574 dmobj_IPersistStream_GetClassID,
575 unimpl_IPersistStream_IsDirty,
576 path_config_IPersistStream_Load,
577 unimpl_IPersistStream_Save,
578 unimpl_IPersistStream_GetSizeMax
581 static inline struct audio_path_config *impl_from_IUnknown(IUnknown *iface)
583 return CONTAINING_RECORD(iface, struct audio_path_config, IUnknown_iface);
586 static HRESULT WINAPI path_config_IUnknown_QueryInterface(IUnknown *iface, REFIID riid, void **ppobj)
588 struct audio_path_config *This = impl_from_IUnknown(iface);
590 TRACE("(%p, %s, %p)\n", This, debugstr_dmguid(riid), ppobj);
592 *ppobj = NULL;
594 if (IsEqualIID (riid, &IID_IUnknown))
595 *ppobj = &This->IUnknown_iface;
596 else if (IsEqualIID (riid, &IID_IDirectMusicObject))
597 *ppobj = &This->dmobj.IDirectMusicObject_iface;
598 else if (IsEqualIID (riid, &IID_IPersistStream))
599 *ppobj = &This->dmobj.IPersistStream_iface;
601 if (*ppobj)
603 IUnknown_AddRef((IUnknown*)*ppobj);
604 return S_OK;
607 WARN("(%p, %s, %p): not found\n", This, debugstr_dmguid(riid), ppobj);
608 return E_NOINTERFACE;
611 static ULONG WINAPI path_config_IUnknown_AddRef(IUnknown *unk)
613 struct audio_path_config *This = impl_from_IUnknown(unk);
614 ULONG ref = InterlockedIncrement(&This->ref);
616 TRACE("(%p): ref=%ld\n", This, ref);
618 return ref;
621 static ULONG WINAPI path_config_IUnknown_Release(IUnknown *unk)
623 struct audio_path_config *This = impl_from_IUnknown(unk);
624 ULONG ref = InterlockedDecrement(&This->ref);
626 TRACE("(%p): ref=%ld\n", This, ref);
628 if (!ref)
630 struct audio_path_port_config *config, *next_config;
631 LIST_FOR_EACH_ENTRY_SAFE(config, next_config, &This->port_config_entries, struct audio_path_port_config, entry)
633 list_remove(&config->entry);
634 port_config_destroy(config);
636 free(This);
638 return ref;
641 static const IUnknownVtbl path_config_unk_vtbl =
643 path_config_IUnknown_QueryInterface,
644 path_config_IUnknown_AddRef,
645 path_config_IUnknown_Release,
648 HRESULT path_config_get_audio_path_params(IUnknown *iface, WAVEFORMATEX *format, DSBUFFERDESC *desc, DMUS_PORTPARAMS *params)
650 struct audio_path_config *This = impl_from_IUnknown(iface);
651 struct list *first_port_config, *first_pchannel_to_buffer;
652 struct audio_path_port_config *port_config;
653 struct audio_path_pchannel_to_buffer *pchannel_to_buffer;
654 GUID *guids;
656 first_port_config = list_head(&This->port_config_entries);
657 if (list_next(&This->port_config_entries, first_port_config))
658 FIXME("Only one port config supported. %p -> %p\n", first_port_config, list_next(&This->port_config_entries, first_port_config));
659 port_config = LIST_ENTRY(first_port_config, struct audio_path_port_config, entry);
660 first_pchannel_to_buffer = list_head(&port_config->pchannel_to_buffer_entries);
661 if (list_next(&port_config->pchannel_to_buffer_entries, first_pchannel_to_buffer))
662 FIXME("Only one pchannel to buffer entry supported.\n");
663 pchannel_to_buffer = LIST_ENTRY(first_pchannel_to_buffer, struct audio_path_pchannel_to_buffer, entry);
665 /* Secondary buffer description */
666 memset(format, 0, sizeof(*format));
667 format->wFormatTag = WAVE_FORMAT_PCM;
668 format->nChannels = 1;
669 format->nSamplesPerSec = 44000;
670 format->nAvgBytesPerSec = 44000 * 2;
671 format->nBlockAlign = 2;
672 format->wBitsPerSample = 16;
673 format->cbSize = 0;
675 memset(desc, 0, sizeof(*desc));
676 desc->dwSize = sizeof(*desc);
677 desc->dwFlags = DSBCAPS_CTRLFX | DSBCAPS_CTRLVOLUME | DSBCAPS_GLOBALFOCUS;
678 desc->dwBufferBytes = DSBSIZE_MIN;
679 desc->dwReserved = 0;
680 desc->lpwfxFormat = format;
681 desc->guid3DAlgorithm = GUID_NULL;
683 guids = pchannel_to_buffer->guids;
684 if (pchannel_to_buffer->header.dwBufferCount == 2)
686 if ((!IsEqualGUID(&guids[0], &GUID_Buffer_Reverb) && !IsEqualGUID(&guids[0], &GUID_Buffer_Stereo)) ||
687 (!IsEqualGUID(&guids[1], &GUID_Buffer_Reverb) && !IsEqualGUID(&guids[1], &GUID_Buffer_Stereo)) ||
688 IsEqualGUID(&guids[0], &guids[1]))
689 FIXME("Only a stereo plus reverb buffer is supported\n");
690 else
692 desc->dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
693 format->nChannels = 2;
694 format->nBlockAlign *= 2;
695 format->nAvgBytesPerSec *= 2;
698 else if (pchannel_to_buffer->header.dwBufferCount == 1)
700 if (IsEqualGUID(guids, &GUID_Buffer_Stereo))
702 desc->dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
703 format->nChannels = 2;
704 format->nBlockAlign *= 2;
705 format->nAvgBytesPerSec *= 2;
707 else if (IsEqualGUID(guids, &GUID_Buffer_3D_Dry))
708 desc->dwFlags |= DSBCAPS_CTRL3D | DSBCAPS_CTRLFREQUENCY | DSBCAPS_MUTE3DATMAXDISTANCE;
709 else if (IsEqualGUID(guids, &GUID_Buffer_Mono))
710 desc->dwFlags |= DSBCAPS_CTRLPAN | DSBCAPS_CTRLFREQUENCY;
711 else
712 FIXME("Unsupported buffer guid %s\n", debugstr_dmguid(guids));
714 else
715 FIXME("Multiple buffers not supported\n");
717 *params = port_config->params;
718 if (!(params->dwValidParams & DMUS_PORTPARAMS_CHANNELGROUPS))
720 params->dwValidParams |= DMUS_PORTPARAMS_CHANNELGROUPS;
721 params->dwChannelGroups = (port_config->header.dwPChannelCount + 15) / 16;
723 if (!(params->dwValidParams & DMUS_PORTPARAMS_AUDIOCHANNELS))
725 params->dwValidParams |= DMUS_PORTPARAMS_AUDIOCHANNELS;
726 params->dwAudioChannels = format->nChannels;
728 return S_OK;
731 /* for ClassFactory */
732 HRESULT create_dmaudiopath(REFIID riid, void **ppobj)
734 IDirectMusicAudioPathImpl* obj;
735 HRESULT hr;
737 *ppobj = NULL;
738 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
739 obj->IDirectMusicAudioPath_iface.lpVtbl = &DirectMusicAudioPathVtbl;
740 obj->ref = 1;
742 hr = IDirectMusicAudioPath_QueryInterface(&obj->IDirectMusicAudioPath_iface, riid, ppobj);
743 IDirectMusicAudioPath_Release(&obj->IDirectMusicAudioPath_iface);
744 return hr;
747 HRESULT create_dmaudiopath_config(REFIID riid, void **ppobj)
749 struct audio_path_config *obj;
750 HRESULT hr;
752 *ppobj = NULL;
753 if (!(obj = calloc(1, sizeof(*obj)))) return E_OUTOFMEMORY;
754 dmobject_init(&obj->dmobj, &CLSID_DirectMusicAudioPathConfig, &obj->IUnknown_iface);
755 obj->IUnknown_iface.lpVtbl = &path_config_unk_vtbl;
756 obj->dmobj.IDirectMusicObject_iface.lpVtbl = &dmobject_vtbl;
757 obj->dmobj.IPersistStream_iface.lpVtbl = &persiststream_vtbl;
758 obj->ref = 1;
760 list_init(&obj->port_config_entries);
762 hr = IUnknown_QueryInterface(&obj->IUnknown_iface, riid, ppobj);
763 IUnknown_Release(&obj->IUnknown_iface);
764 return hr;