2 * Copyright 2020 Andrew Eikum for CodeWeavers
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
20 #define NONAMELESSUNION
28 #include "wine/heap.h"
29 #include "wine/debug.h"
30 #include "wine/list.h"
33 #include "mmdeviceapi.h"
35 #include "audioclient.h"
36 #include "endpointvolume.h"
37 #include "audiopolicy.h"
38 #include "spatialaudioclient.h"
40 #include "mmdevapi_private.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
44 static UINT32
AudioObjectType_to_index(AudioObjectType type
)
54 typedef struct SpatialAudioImpl SpatialAudioImpl
;
55 typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl
;
56 typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl
;
58 struct SpatialAudioObjectImpl
{
59 ISpatialAudioObject ISpatialAudioObject_iface
;
62 SpatialAudioStreamImpl
*sa_stream
;
71 struct SpatialAudioStreamImpl
{
72 ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface
;
74 CRITICAL_SECTION lock
;
76 SpatialAudioImpl
*sa_client
;
77 SpatialAudioObjectRenderStreamActivationParams params
;
80 IAudioRenderClient
*render
;
82 UINT32 period_frames
, update_frames
;
83 WAVEFORMATEXTENSIBLE stream_fmtex
;
87 UINT32 static_object_map
[17];
92 struct SpatialAudioImpl
{
93 ISpatialAudioClient ISpatialAudioClient_iface
;
94 IAudioFormatEnumerator IAudioFormatEnumerator_iface
;
97 WAVEFORMATEXTENSIBLE object_fmtex
;
100 static inline SpatialAudioObjectImpl
*impl_from_ISpatialAudioObject(ISpatialAudioObject
*iface
)
102 return CONTAINING_RECORD(iface
, SpatialAudioObjectImpl
, ISpatialAudioObject_iface
);
105 static inline SpatialAudioStreamImpl
*impl_from_ISpatialAudioObjectRenderStream(ISpatialAudioObjectRenderStream
*iface
)
107 return CONTAINING_RECORD(iface
, SpatialAudioStreamImpl
, ISpatialAudioObjectRenderStream_iface
);
110 static inline SpatialAudioImpl
*impl_from_ISpatialAudioClient(ISpatialAudioClient
*iface
)
112 return CONTAINING_RECORD(iface
, SpatialAudioImpl
, ISpatialAudioClient_iface
);
115 static inline SpatialAudioImpl
*impl_from_IAudioFormatEnumerator(IAudioFormatEnumerator
*iface
)
117 return CONTAINING_RECORD(iface
, SpatialAudioImpl
, IAudioFormatEnumerator_iface
);
120 static HRESULT WINAPI
SAO_QueryInterface(ISpatialAudioObject
*iface
,
121 REFIID riid
, void **ppv
)
123 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
125 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
132 if (IsEqualIID(riid
, &IID_IUnknown
) ||
133 IsEqualIID(riid
, &IID_ISpatialAudioObjectBase
) ||
134 IsEqualIID(riid
, &IID_ISpatialAudioObject
)) {
135 *ppv
= &This
->ISpatialAudioObject_iface
;
138 return E_NOINTERFACE
;
140 IUnknown_AddRef((IUnknown
*)*ppv
);
145 static ULONG WINAPI
SAO_AddRef(ISpatialAudioObject
*iface
)
147 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
148 ULONG ref
= InterlockedIncrement(&This
->ref
);
149 TRACE("(%p) new ref %lu\n", This
, ref
);
153 static ULONG WINAPI
SAO_Release(ISpatialAudioObject
*iface
)
155 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
156 ULONG ref
= InterlockedDecrement(&This
->ref
);
157 TRACE("(%p) new ref %lu\n", This
, ref
);
159 EnterCriticalSection(&This
->sa_stream
->lock
);
160 list_remove(&This
->entry
);
161 LeaveCriticalSection(&This
->sa_stream
->lock
);
163 ISpatialAudioObjectRenderStream_Release(&This
->sa_stream
->ISpatialAudioObjectRenderStream_iface
);
164 heap_free(This
->buf
);
170 static HRESULT WINAPI
SAO_GetBuffer(ISpatialAudioObject
*iface
,
171 BYTE
**buffer
, UINT32
*bytes
)
173 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
175 TRACE("(%p)->(%p, %p)\n", This
, buffer
, bytes
);
177 EnterCriticalSection(&This
->sa_stream
->lock
);
179 if(This
->sa_stream
->update_frames
== ~0){
180 LeaveCriticalSection(&This
->sa_stream
->lock
);
181 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
184 *buffer
= (BYTE
*)This
->buf
;
185 *bytes
= This
->sa_stream
->update_frames
*
186 This
->sa_stream
->sa_client
->object_fmtex
.Format
.nBlockAlign
;
188 LeaveCriticalSection(&This
->sa_stream
->lock
);
193 static HRESULT WINAPI
SAO_SetEndOfStream(ISpatialAudioObject
*iface
, UINT32 frames
)
195 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
196 FIXME("(%p)->(%u)\n", This
, frames
);
200 static HRESULT WINAPI
SAO_IsActive(ISpatialAudioObject
*iface
, BOOL
*active
)
202 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
203 FIXME("(%p)->(%p)\n", This
, active
);
207 static HRESULT WINAPI
SAO_GetAudioObjectType(ISpatialAudioObject
*iface
,
208 AudioObjectType
*type
)
210 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
212 TRACE("(%p)->(%p)\n", This
, type
);
219 static HRESULT WINAPI
SAO_SetPosition(ISpatialAudioObject
*iface
, float x
,
222 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
223 FIXME("(%p)->(%f, %f, %f)\n", This
, x
, y
, z
);
227 static HRESULT WINAPI
SAO_SetVolume(ISpatialAudioObject
*iface
, float vol
)
229 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
230 FIXME("(%p)->(%f)\n", This
, vol
);
234 static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl
= {
241 SAO_GetAudioObjectType
,
246 static HRESULT WINAPI
SAORS_QueryInterface(ISpatialAudioObjectRenderStream
*iface
,
247 REFIID riid
, void **ppv
)
249 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
251 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
258 if (IsEqualIID(riid
, &IID_IUnknown
) ||
259 IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStreamBase
) ||
260 IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStream
)) {
261 *ppv
= &This
->ISpatialAudioObjectRenderStream_iface
;
264 return E_NOINTERFACE
;
266 IUnknown_AddRef((IUnknown
*)*ppv
);
271 static ULONG WINAPI
SAORS_AddRef(ISpatialAudioObjectRenderStream
*iface
)
273 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
274 ULONG ref
= InterlockedIncrement(&This
->ref
);
275 TRACE("(%p) new ref %lu\n", This
, ref
);
279 static ULONG WINAPI
SAORS_Release(ISpatialAudioObjectRenderStream
*iface
)
281 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
282 ULONG ref
= InterlockedDecrement(&This
->ref
);
283 TRACE("(%p) new ref %lu\n", This
, ref
);
285 IAudioClient_Stop(This
->client
);
286 if(This
->update_frames
!= ~0 && This
->update_frames
> 0)
287 IAudioRenderClient_ReleaseBuffer(This
->render
, This
->update_frames
, 0);
288 IAudioRenderClient_Release(This
->render
);
289 IAudioClient_Release(This
->client
);
290 if(This
->params
.NotifyObject
)
291 ISpatialAudioObjectRenderStreamNotify_Release(This
->params
.NotifyObject
);
292 heap_free((void*)This
->params
.ObjectFormat
);
293 CloseHandle(This
->params
.EventHandle
);
294 DeleteCriticalSection(&This
->lock
);
295 ISpatialAudioClient_Release(&This
->sa_client
->ISpatialAudioClient_iface
);
301 static HRESULT WINAPI
SAORS_GetAvailableDynamicObjectCount(
302 ISpatialAudioObjectRenderStream
*iface
, UINT32
*count
)
304 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
305 FIXME("(%p)->(%p)\n", This
, count
);
311 static HRESULT WINAPI
SAORS_GetService(ISpatialAudioObjectRenderStream
*iface
,
312 REFIID riid
, void **service
)
314 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
315 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), service
);
319 static HRESULT WINAPI
SAORS_Start(ISpatialAudioObjectRenderStream
*iface
)
321 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
324 TRACE("(%p)->()\n", This
);
326 hr
= IAudioClient_Start(This
->client
);
328 WARN("IAudioClient::Start failed: %08lx\n", hr
);
335 static HRESULT WINAPI
SAORS_Stop(ISpatialAudioObjectRenderStream
*iface
)
337 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
340 TRACE("(%p)->()\n", This
);
342 hr
= IAudioClient_Stop(This
->client
);
344 WARN("IAudioClient::Stop failed: %08lx\n", hr
);
351 static HRESULT WINAPI
SAORS_Reset(ISpatialAudioObjectRenderStream
*iface
)
353 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
354 FIXME("(%p)->()\n", This
);
358 static HRESULT WINAPI
SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
,
359 UINT32
*dyn_count
, UINT32
*frames
)
361 static BOOL fixme_once
= FALSE
;
362 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
363 SpatialAudioObjectImpl
*object
;
366 TRACE("(%p)->(%p, %p)\n", This
, dyn_count
, frames
);
368 EnterCriticalSection(&This
->lock
);
370 if(This
->update_frames
!= ~0){
371 LeaveCriticalSection(&This
->lock
);
372 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
375 This
->update_frames
= This
->period_frames
;
377 if(This
->update_frames
> 0){
378 hr
= IAudioRenderClient_GetBuffer(This
->render
, This
->update_frames
, (BYTE
**)&This
->buf
);
380 WARN("GetBuffer failed: %08lx\n", hr
);
381 This
->update_frames
= ~0;
382 LeaveCriticalSection(&This
->lock
);
386 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
387 memset(object
->buf
, 0, This
->update_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
389 }else if (!fixme_once
){
391 FIXME("Zero frame update.\n");
395 *frames
= This
->update_frames
;
397 LeaveCriticalSection(&This
->lock
);
402 static void mix_static_object(SpatialAudioStreamImpl
*stream
, SpatialAudioObjectImpl
*object
)
404 float *in
= object
->buf
, *out
;
406 if(object
->static_idx
== ~0 ||
407 stream
->static_object_map
[object
->static_idx
] == ~0){
408 WARN("Got unmapped static object?! Not mixing. Type: 0x%x\n", object
->type
);
411 out
= stream
->buf
+ stream
->static_object_map
[object
->static_idx
];
412 for(i
= 0; i
< stream
->update_frames
; ++i
){
415 out
+= stream
->stream_fmtex
.Format
.nChannels
;
419 static HRESULT WINAPI
SAORS_EndUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
)
421 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
422 SpatialAudioObjectImpl
*object
;
425 TRACE("(%p)->()\n", This
);
427 EnterCriticalSection(&This
->lock
);
429 if(This
->update_frames
== ~0){
430 LeaveCriticalSection(&This
->lock
);
431 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
434 if(This
->update_frames
> 0){
435 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
436 if(object
->type
!= AudioObjectType_Dynamic
)
437 mix_static_object(This
, object
);
439 WARN("Don't know how to mix dynamic object yet. %p\n", object
);
442 hr
= IAudioRenderClient_ReleaseBuffer(This
->render
, This
->update_frames
, 0);
444 WARN("ReleaseBuffer failed: %08lx\n", hr
);
447 This
->update_frames
= ~0;
449 LeaveCriticalSection(&This
->lock
);
454 static HRESULT WINAPI
SAORS_ActivateSpatialAudioObject(ISpatialAudioObjectRenderStream
*iface
,
455 AudioObjectType type
, ISpatialAudioObject
**object
)
457 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
458 SpatialAudioObjectImpl
*obj
;
460 TRACE("(%p)->(0x%x, %p)\n", This
, type
, object
);
462 if(type
== AudioObjectType_Dynamic
)
463 return SPTLAUDCLNT_E_NO_MORE_OBJECTS
;
465 if(type
& ~This
->params
.StaticObjectTypeMask
)
466 return SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE
;
468 LIST_FOR_EACH_ENTRY(obj
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
469 if(obj
->static_idx
== AudioObjectType_to_index(type
))
470 return SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE
;
473 obj
= heap_alloc_zero(sizeof(*obj
));
474 obj
->ISpatialAudioObject_iface
.lpVtbl
= &ISpatialAudioObject_vtbl
;
477 if(type
== AudioObjectType_None
){
478 FIXME("AudioObjectType_None not implemented yet!\n");
479 obj
->static_idx
= ~0;
481 obj
->static_idx
= AudioObjectType_to_index(type
);
484 obj
->sa_stream
= This
;
485 SAORS_AddRef(&This
->ISpatialAudioObjectRenderStream_iface
);
487 obj
->buf
= heap_alloc_zero(This
->period_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
489 EnterCriticalSection(&This
->lock
);
491 list_add_tail(&This
->objects
, &obj
->entry
);
493 LeaveCriticalSection(&This
->lock
);
495 *object
= &obj
->ISpatialAudioObject_iface
;
500 static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl
= {
501 SAORS_QueryInterface
,
504 SAORS_GetAvailableDynamicObjectCount
,
509 SAORS_BeginUpdatingAudioObjects
,
510 SAORS_EndUpdatingAudioObjects
,
511 SAORS_ActivateSpatialAudioObject
,
514 static HRESULT WINAPI
SAC_QueryInterface(ISpatialAudioClient
*iface
, REFIID riid
, void **ppv
)
516 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
518 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
525 if (IsEqualIID(riid
, &IID_IUnknown
) ||
526 IsEqualIID(riid
, &IID_ISpatialAudioClient
)) {
527 *ppv
= &This
->ISpatialAudioClient_iface
;
530 return E_NOINTERFACE
;
532 IUnknown_AddRef((IUnknown
*)*ppv
);
537 static ULONG WINAPI
SAC_AddRef(ISpatialAudioClient
*iface
)
539 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
540 ULONG ref
= InterlockedIncrement(&This
->ref
);
541 TRACE("(%p) new ref %lu\n", This
, ref
);
545 static ULONG WINAPI
SAC_Release(ISpatialAudioClient
*iface
)
547 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
548 ULONG ref
= InterlockedDecrement(&This
->ref
);
549 TRACE("(%p) new ref %lu\n", This
, ref
);
551 IMMDevice_Release(This
->mmdev
);
557 static HRESULT WINAPI
SAC_GetStaticObjectPosition(ISpatialAudioClient
*iface
,
558 AudioObjectType type
, float *x
, float *y
, float *z
)
560 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
561 FIXME("(%p)->(0x%x, %p, %p, %p)\n", This
, type
, x
, y
, z
);
565 static HRESULT WINAPI
SAC_GetNativeStaticObjectTypeMask(ISpatialAudioClient
*iface
,
566 AudioObjectType
*mask
)
568 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
569 FIXME("(%p)->(%p)\n", This
, mask
);
573 static HRESULT WINAPI
SAC_GetMaxDynamicObjectCount(ISpatialAudioClient
*iface
,
576 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
577 FIXME("(%p)->(%p)\n", This
, value
);
584 static HRESULT WINAPI
SAC_GetSupportedAudioObjectFormatEnumerator(
585 ISpatialAudioClient
*iface
, IAudioFormatEnumerator
**enumerator
)
587 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
589 TRACE("(%p)->(%p)\n", This
, enumerator
);
591 *enumerator
= &This
->IAudioFormatEnumerator_iface
;
597 static HRESULT WINAPI
SAC_GetMaxFrameCount(ISpatialAudioClient
*iface
,
598 const WAVEFORMATEX
*format
, UINT32
*count
)
600 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
602 /* FIXME: should get device period from the device */
603 static const REFERENCE_TIME period
= 100000;
605 TRACE("(%p)->(%p, %p)\n", This
, format
, count
);
607 *count
= MulDiv(period
, format
->nSamplesPerSec
, 10000000);
612 static HRESULT WINAPI
SAC_IsAudioObjectFormatSupported(ISpatialAudioClient
*iface
,
613 const WAVEFORMATEX
*format
)
615 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
616 FIXME("(%p)->(%p)\n", This
, format
);
620 static HRESULT WINAPI
SAC_IsSpatialAudioStreamAvailable(ISpatialAudioClient
*iface
,
621 REFIID stream_uuid
, const PROPVARIANT
*info
)
623 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
624 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(stream_uuid
), info
);
628 static WAVEFORMATEX
*clone_fmtex(const WAVEFORMATEX
*src
)
630 WAVEFORMATEX
*r
= heap_alloc(sizeof(WAVEFORMATEX
) + src
->cbSize
);
631 memcpy(r
, src
, sizeof(WAVEFORMATEX
) + src
->cbSize
);
635 static const char *debugstr_fmtex(const WAVEFORMATEX
*fmt
)
637 static char buf
[2048];
638 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
639 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
640 snprintf(buf
, sizeof(buf
), "tag: 0x%x (%s), ch: %u (mask: 0x%lx), rate: %lu, depth: %u",
641 fmt
->wFormatTag
, debugstr_guid(&fmtex
->SubFormat
),
642 fmt
->nChannels
, fmtex
->dwChannelMask
, fmt
->nSamplesPerSec
,
643 fmt
->wBitsPerSample
);
645 snprintf(buf
, sizeof(buf
), "tag: 0x%x, ch: %u, rate: %lu, depth: %u",
646 fmt
->wFormatTag
, fmt
->nChannels
, fmt
->nSamplesPerSec
,
647 fmt
->wBitsPerSample
);
652 static void static_mask_to_channels(AudioObjectType static_mask
, WORD
*count
, DWORD
*mask
, UINT32
*map
)
654 UINT32 out_chan
= 0, map_idx
= 0;
657 #define CONVERT_MASK(f, t) \
658 if(static_mask & f){ \
661 map[map_idx++] = out_chan++; \
662 TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
664 map[map_idx++] = ~0; \
666 CONVERT_MASK(AudioObjectType_FrontLeft
, SPEAKER_FRONT_LEFT
);
667 CONVERT_MASK(AudioObjectType_FrontRight
, SPEAKER_FRONT_RIGHT
);
668 CONVERT_MASK(AudioObjectType_FrontCenter
, SPEAKER_FRONT_CENTER
);
669 CONVERT_MASK(AudioObjectType_LowFrequency
, SPEAKER_LOW_FREQUENCY
);
670 CONVERT_MASK(AudioObjectType_SideLeft
, SPEAKER_SIDE_LEFT
);
671 CONVERT_MASK(AudioObjectType_SideRight
, SPEAKER_SIDE_RIGHT
);
672 CONVERT_MASK(AudioObjectType_BackLeft
, SPEAKER_BACK_LEFT
);
673 CONVERT_MASK(AudioObjectType_BackRight
, SPEAKER_BACK_RIGHT
);
674 CONVERT_MASK(AudioObjectType_TopFrontLeft
, SPEAKER_TOP_FRONT_LEFT
);
675 CONVERT_MASK(AudioObjectType_TopFrontRight
, SPEAKER_TOP_FRONT_RIGHT
);
676 CONVERT_MASK(AudioObjectType_TopBackLeft
, SPEAKER_TOP_BACK_LEFT
);
677 CONVERT_MASK(AudioObjectType_TopBackRight
, SPEAKER_TOP_BACK_RIGHT
);
678 CONVERT_MASK(AudioObjectType_BackCenter
, SPEAKER_BACK_CENTER
);
681 static HRESULT
activate_stream(SpatialAudioStreamImpl
*stream
)
683 WAVEFORMATEXTENSIBLE
*object_fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->params
.ObjectFormat
;
685 REFERENCE_TIME period
;
687 if(!(object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
688 (object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
689 IsEqualGUID(&object_fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)))){
690 FIXME("Only float formats are supported for now\n");
694 hr
= IMMDevice_Activate(stream
->sa_client
->mmdev
, &IID_IAudioClient
,
695 CLSCTX_INPROC_SERVER
, NULL
, (void**)&stream
->client
);
697 WARN("Activate failed: %08lx\n", hr
);
701 hr
= IAudioClient_GetDevicePeriod(stream
->client
, &period
, NULL
);
703 WARN("GetDevicePeriod failed: %08lx\n", hr
);
704 IAudioClient_Release(stream
->client
);
708 stream
->stream_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
709 static_mask_to_channels(stream
->params
.StaticObjectTypeMask
,
710 &stream
->stream_fmtex
.Format
.nChannels
, &stream
->stream_fmtex
.dwChannelMask
,
711 stream
->static_object_map
);
712 stream
->stream_fmtex
.Format
.nSamplesPerSec
= stream
->params
.ObjectFormat
->nSamplesPerSec
;
713 stream
->stream_fmtex
.Format
.wBitsPerSample
= stream
->params
.ObjectFormat
->wBitsPerSample
;
714 stream
->stream_fmtex
.Format
.nBlockAlign
= (stream
->stream_fmtex
.Format
.nChannels
* stream
->stream_fmtex
.Format
.wBitsPerSample
) / 8;
715 stream
->stream_fmtex
.Format
.nAvgBytesPerSec
= stream
->stream_fmtex
.Format
.nSamplesPerSec
* stream
->stream_fmtex
.Format
.nBlockAlign
;
716 stream
->stream_fmtex
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
717 stream
->stream_fmtex
.Samples
.wValidBitsPerSample
= stream
->stream_fmtex
.Format
.wBitsPerSample
;
718 stream
->stream_fmtex
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
720 hr
= IAudioClient_Initialize(stream
->client
, AUDCLNT_SHAREMODE_SHARED
,
721 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
| AUDCLNT_STREAMFLAGS_NOPERSIST
,
722 period
, 0, &stream
->stream_fmtex
.Format
, NULL
);
724 WARN("Initialize failed: %08lx\n", hr
);
725 IAudioClient_Release(stream
->client
);
729 hr
= IAudioClient_SetEventHandle(stream
->client
, stream
->params
.EventHandle
);
731 WARN("SetEventHandle failed: %08lx\n", hr
);
732 IAudioClient_Release(stream
->client
);
736 hr
= IAudioClient_GetService(stream
->client
, &IID_IAudioRenderClient
, (void**)&stream
->render
);
738 WARN("GetService(AudioRenderClient) failed: %08lx\n", hr
);
739 IAudioClient_Release(stream
->client
);
743 stream
->period_frames
= MulDiv(period
, stream
->stream_fmtex
.Format
.nSamplesPerSec
, 10000000);
748 static HRESULT WINAPI
SAC_ActivateSpatialAudioStream(ISpatialAudioClient
*iface
,
749 const PROPVARIANT
*prop
, REFIID riid
, void **stream
)
751 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
752 SpatialAudioObjectRenderStreamActivationParams
*params
;
755 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), stream
);
757 if(IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStream
)){
758 SpatialAudioStreamImpl
*obj
;
761 (prop
->vt
!= VT_BLOB
||
762 prop
->blob
.cbSize
!= sizeof(SpatialAudioObjectRenderStreamActivationParams
))){
763 WARN("Got invalid params\n");
768 params
= (SpatialAudioObjectRenderStreamActivationParams
*) prop
->blob
.pBlobData
;
770 if(params
->StaticObjectTypeMask
& AudioObjectType_Dynamic
){
775 if(params
->EventHandle
== INVALID_HANDLE_VALUE
||
776 params
->EventHandle
== 0){
781 if(!params
->ObjectFormat
||
782 memcmp(params
->ObjectFormat
, &This
->object_fmtex
.Format
, sizeof(*params
->ObjectFormat
) + params
->ObjectFormat
->cbSize
)){
784 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
787 obj
= heap_alloc_zero(sizeof(SpatialAudioStreamImpl
));
789 obj
->ISpatialAudioObjectRenderStream_iface
.lpVtbl
= &ISpatialAudioObjectRenderStream_vtbl
;
791 memcpy(&obj
->params
, params
, sizeof(obj
->params
));
793 obj
->update_frames
= ~0;
795 InitializeCriticalSection(&obj
->lock
);
796 list_init(&obj
->objects
);
798 obj
->sa_client
= This
;
799 SAC_AddRef(&This
->ISpatialAudioClient_iface
);
801 obj
->params
.ObjectFormat
= clone_fmtex(obj
->params
.ObjectFormat
);
803 DuplicateHandle(GetCurrentProcess(), obj
->params
.EventHandle
,
804 GetCurrentProcess(), &obj
->params
.EventHandle
, 0, FALSE
,
805 DUPLICATE_SAME_ACCESS
);
807 if(obj
->params
.NotifyObject
)
808 ISpatialAudioObjectRenderStreamNotify_AddRef(obj
->params
.NotifyObject
);
810 if(TRACE_ON(mmdevapi
)){
811 TRACE("ObjectFormat: {%s}\n", debugstr_fmtex(obj
->params
.ObjectFormat
));
812 TRACE("StaticObjectTypeMask: 0x%x\n", obj
->params
.StaticObjectTypeMask
);
813 TRACE("MinDynamicObjectCount: 0x%x\n", obj
->params
.MinDynamicObjectCount
);
814 TRACE("MaxDynamicObjectCount: 0x%x\n", obj
->params
.MaxDynamicObjectCount
);
815 TRACE("Category: 0x%x\n", obj
->params
.Category
);
816 TRACE("EventHandle: %p\n", obj
->params
.EventHandle
);
817 TRACE("NotifyObject: %p\n", obj
->params
.NotifyObject
);
820 hr
= activate_stream(obj
);
822 if(obj
->params
.NotifyObject
)
823 ISpatialAudioObjectRenderStreamNotify_Release(obj
->params
.NotifyObject
);
824 DeleteCriticalSection(&obj
->lock
);
825 heap_free((void*)obj
->params
.ObjectFormat
);
826 CloseHandle(obj
->params
.EventHandle
);
827 ISpatialAudioClient_Release(&obj
->sa_client
->ISpatialAudioClient_iface
);
833 *stream
= &obj
->ISpatialAudioObjectRenderStream_iface
;
835 FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid
));
843 static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl
= {
847 SAC_GetStaticObjectPosition
,
848 SAC_GetNativeStaticObjectTypeMask
,
849 SAC_GetMaxDynamicObjectCount
,
850 SAC_GetSupportedAudioObjectFormatEnumerator
,
851 SAC_GetMaxFrameCount
,
852 SAC_IsAudioObjectFormatSupported
,
853 SAC_IsSpatialAudioStreamAvailable
,
854 SAC_ActivateSpatialAudioStream
,
857 static HRESULT WINAPI
SAOFE_QueryInterface(IAudioFormatEnumerator
*iface
,
858 REFIID riid
, void **ppvObject
)
860 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
861 return SAC_QueryInterface(&This
->ISpatialAudioClient_iface
, riid
, ppvObject
);
864 static ULONG WINAPI
SAOFE_AddRef(IAudioFormatEnumerator
*iface
)
866 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
867 return SAC_AddRef(&This
->ISpatialAudioClient_iface
);
870 static ULONG WINAPI
SAOFE_Release(IAudioFormatEnumerator
*iface
)
872 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
873 return SAC_Release(&This
->ISpatialAudioClient_iface
);
876 static HRESULT WINAPI
SAOFE_GetCount(IAudioFormatEnumerator
*iface
, UINT32
*count
)
878 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
880 TRACE("(%p)->(%p)\n", This
, count
);
887 static HRESULT WINAPI
SAOFE_GetFormat(IAudioFormatEnumerator
*iface
,
888 UINT32 index
, WAVEFORMATEX
**format
)
890 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
892 TRACE("(%p)->(%u, %p)\n", This
, index
, format
);
897 *format
= &This
->object_fmtex
.Format
;
902 static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl
= {
903 SAOFE_QueryInterface
,
910 HRESULT
SpatialAudioClient_Create(IMMDevice
*mmdev
, ISpatialAudioClient
**out
)
912 SpatialAudioImpl
*obj
;
913 IAudioClient
*aclient
;
914 WAVEFORMATEX
*closest
;
917 obj
= heap_alloc_zero(sizeof(*obj
));
920 obj
->ISpatialAudioClient_iface
.lpVtbl
= &ISpatialAudioClient_vtbl
;
921 obj
->IAudioFormatEnumerator_iface
.lpVtbl
= &IAudioFormatEnumerator_vtbl
;
923 obj
->object_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
924 obj
->object_fmtex
.Format
.nChannels
= 1;
925 obj
->object_fmtex
.Format
.nSamplesPerSec
= 48000;
926 obj
->object_fmtex
.Format
.wBitsPerSample
= sizeof(float) * 8;
927 obj
->object_fmtex
.Format
.nBlockAlign
= (obj
->object_fmtex
.Format
.nChannels
* obj
->object_fmtex
.Format
.wBitsPerSample
) / 8;
928 obj
->object_fmtex
.Format
.nAvgBytesPerSec
= obj
->object_fmtex
.Format
.nSamplesPerSec
* obj
->object_fmtex
.Format
.nBlockAlign
;
929 obj
->object_fmtex
.Format
.cbSize
= 0;
931 hr
= IMMDevice_Activate(mmdev
, &IID_IAudioClient
,
932 CLSCTX_INPROC_SERVER
, NULL
, (void**)&aclient
);
934 WARN("Activate failed: %08lx\n", hr
);
939 hr
= IAudioClient_IsFormatSupported(aclient
, AUDCLNT_SHAREMODE_SHARED
, &obj
->object_fmtex
.Format
, &closest
);
941 IAudioClient_Release(aclient
);
944 if(sizeof(WAVEFORMATEX
) + closest
->cbSize
> sizeof(obj
->object_fmtex
)){
945 ERR("Returned format too large: %s\n", debugstr_fmtex(closest
));
946 CoTaskMemFree(closest
);
948 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
949 }else if(!((closest
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
950 (closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
951 IsEqualGUID(&((WAVEFORMATEXTENSIBLE
*)closest
)->SubFormat
,
952 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))) &&
953 closest
->wBitsPerSample
== 32)){
954 ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest
));
955 CoTaskMemFree(closest
);
957 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
959 WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest
));
960 memcpy(&obj
->object_fmtex
,
962 sizeof(WAVEFORMATEX
) + closest
->cbSize
);
963 CoTaskMemFree(closest
);
964 } else if(hr
!= S_OK
){
965 WARN("Checking supported formats failed: %08lx\n", hr
);
971 IMMDevice_AddRef(mmdev
);
973 *out
= &obj
->ISpatialAudioClient_iface
;