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
27 #include "wine/heap.h"
28 #include "wine/debug.h"
29 #include "wine/list.h"
32 #include "mmdeviceapi.h"
34 #include "audioclient.h"
35 #include "endpointvolume.h"
36 #include "audiopolicy.h"
37 #include "spatialaudioclient.h"
39 #include "mmdevapi_private.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi
);
43 static UINT32
AudioObjectType_to_index(AudioObjectType type
)
53 typedef struct SpatialAudioImpl SpatialAudioImpl
;
54 typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl
;
55 typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl
;
57 struct SpatialAudioObjectImpl
{
58 ISpatialAudioObject ISpatialAudioObject_iface
;
61 SpatialAudioStreamImpl
*sa_stream
;
70 struct SpatialAudioStreamImpl
{
71 ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface
;
73 CRITICAL_SECTION lock
;
75 SpatialAudioImpl
*sa_client
;
76 SpatialAudioObjectRenderStreamActivationParams params
;
79 IAudioRenderClient
*render
;
81 UINT32 period_frames
, update_frames
;
82 WAVEFORMATEXTENSIBLE stream_fmtex
;
86 UINT32 static_object_map
[17];
91 struct SpatialAudioImpl
{
92 ISpatialAudioClient ISpatialAudioClient_iface
;
93 IAudioFormatEnumerator IAudioFormatEnumerator_iface
;
96 WAVEFORMATEXTENSIBLE object_fmtex
;
99 static inline SpatialAudioObjectImpl
*impl_from_ISpatialAudioObject(ISpatialAudioObject
*iface
)
101 return CONTAINING_RECORD(iface
, SpatialAudioObjectImpl
, ISpatialAudioObject_iface
);
104 static inline SpatialAudioStreamImpl
*impl_from_ISpatialAudioObjectRenderStream(ISpatialAudioObjectRenderStream
*iface
)
106 return CONTAINING_RECORD(iface
, SpatialAudioStreamImpl
, ISpatialAudioObjectRenderStream_iface
);
109 static inline SpatialAudioImpl
*impl_from_ISpatialAudioClient(ISpatialAudioClient
*iface
)
111 return CONTAINING_RECORD(iface
, SpatialAudioImpl
, ISpatialAudioClient_iface
);
114 static inline SpatialAudioImpl
*impl_from_IAudioFormatEnumerator(IAudioFormatEnumerator
*iface
)
116 return CONTAINING_RECORD(iface
, SpatialAudioImpl
, IAudioFormatEnumerator_iface
);
119 static HRESULT WINAPI
SAO_QueryInterface(ISpatialAudioObject
*iface
,
120 REFIID riid
, void **ppv
)
122 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
124 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
131 if (IsEqualIID(riid
, &IID_IUnknown
) ||
132 IsEqualIID(riid
, &IID_ISpatialAudioObjectBase
) ||
133 IsEqualIID(riid
, &IID_ISpatialAudioObject
)) {
134 *ppv
= &This
->ISpatialAudioObject_iface
;
137 return E_NOINTERFACE
;
139 IUnknown_AddRef((IUnknown
*)*ppv
);
144 static ULONG WINAPI
SAO_AddRef(ISpatialAudioObject
*iface
)
146 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
147 ULONG ref
= InterlockedIncrement(&This
->ref
);
148 TRACE("(%p) new ref %lu\n", This
, ref
);
152 static ULONG WINAPI
SAO_Release(ISpatialAudioObject
*iface
)
154 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
155 ULONG ref
= InterlockedDecrement(&This
->ref
);
156 TRACE("(%p) new ref %lu\n", This
, ref
);
158 EnterCriticalSection(&This
->sa_stream
->lock
);
159 list_remove(&This
->entry
);
160 LeaveCriticalSection(&This
->sa_stream
->lock
);
162 ISpatialAudioObjectRenderStream_Release(&This
->sa_stream
->ISpatialAudioObjectRenderStream_iface
);
163 heap_free(This
->buf
);
169 static HRESULT WINAPI
SAO_GetBuffer(ISpatialAudioObject
*iface
,
170 BYTE
**buffer
, UINT32
*bytes
)
172 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
174 TRACE("(%p)->(%p, %p)\n", This
, buffer
, bytes
);
176 EnterCriticalSection(&This
->sa_stream
->lock
);
178 if(This
->sa_stream
->update_frames
== ~0){
179 LeaveCriticalSection(&This
->sa_stream
->lock
);
180 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
183 *buffer
= (BYTE
*)This
->buf
;
184 *bytes
= This
->sa_stream
->update_frames
*
185 This
->sa_stream
->sa_client
->object_fmtex
.Format
.nBlockAlign
;
187 LeaveCriticalSection(&This
->sa_stream
->lock
);
192 static HRESULT WINAPI
SAO_SetEndOfStream(ISpatialAudioObject
*iface
, UINT32 frames
)
194 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
195 FIXME("(%p)->(%u)\n", This
, frames
);
199 static HRESULT WINAPI
SAO_IsActive(ISpatialAudioObject
*iface
, BOOL
*active
)
201 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
202 FIXME("(%p)->(%p)\n", This
, active
);
206 static HRESULT WINAPI
SAO_GetAudioObjectType(ISpatialAudioObject
*iface
,
207 AudioObjectType
*type
)
209 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
211 TRACE("(%p)->(%p)\n", This
, type
);
218 static HRESULT WINAPI
SAO_SetPosition(ISpatialAudioObject
*iface
, float x
,
221 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
222 FIXME("(%p)->(%f, %f, %f)\n", This
, x
, y
, z
);
226 static HRESULT WINAPI
SAO_SetVolume(ISpatialAudioObject
*iface
, float vol
)
228 SpatialAudioObjectImpl
*This
= impl_from_ISpatialAudioObject(iface
);
229 FIXME("(%p)->(%f)\n", This
, vol
);
233 static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl
= {
240 SAO_GetAudioObjectType
,
245 static HRESULT WINAPI
SAORS_QueryInterface(ISpatialAudioObjectRenderStream
*iface
,
246 REFIID riid
, void **ppv
)
248 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
250 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
257 if (IsEqualIID(riid
, &IID_IUnknown
) ||
258 IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStreamBase
) ||
259 IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStream
)) {
260 *ppv
= &This
->ISpatialAudioObjectRenderStream_iface
;
263 return E_NOINTERFACE
;
265 IUnknown_AddRef((IUnknown
*)*ppv
);
270 static ULONG WINAPI
SAORS_AddRef(ISpatialAudioObjectRenderStream
*iface
)
272 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
273 ULONG ref
= InterlockedIncrement(&This
->ref
);
274 TRACE("(%p) new ref %lu\n", This
, ref
);
278 static ULONG WINAPI
SAORS_Release(ISpatialAudioObjectRenderStream
*iface
)
280 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
281 ULONG ref
= InterlockedDecrement(&This
->ref
);
282 TRACE("(%p) new ref %lu\n", This
, ref
);
284 IAudioClient_Stop(This
->client
);
285 if(This
->update_frames
!= ~0 && This
->update_frames
> 0)
286 IAudioRenderClient_ReleaseBuffer(This
->render
, This
->update_frames
, 0);
287 IAudioRenderClient_Release(This
->render
);
288 IAudioClient_Release(This
->client
);
289 if(This
->params
.NotifyObject
)
290 ISpatialAudioObjectRenderStreamNotify_Release(This
->params
.NotifyObject
);
291 heap_free((void*)This
->params
.ObjectFormat
);
292 CloseHandle(This
->params
.EventHandle
);
293 DeleteCriticalSection(&This
->lock
);
294 ISpatialAudioClient_Release(&This
->sa_client
->ISpatialAudioClient_iface
);
300 static HRESULT WINAPI
SAORS_GetAvailableDynamicObjectCount(
301 ISpatialAudioObjectRenderStream
*iface
, UINT32
*count
)
303 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
304 FIXME("(%p)->(%p)\n", This
, count
);
310 static HRESULT WINAPI
SAORS_GetService(ISpatialAudioObjectRenderStream
*iface
,
311 REFIID riid
, void **service
)
313 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
314 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), service
);
318 static HRESULT WINAPI
SAORS_Start(ISpatialAudioObjectRenderStream
*iface
)
320 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
323 TRACE("(%p)->()\n", This
);
325 hr
= IAudioClient_Start(This
->client
);
327 WARN("IAudioClient::Start failed: %08lx\n", hr
);
334 static HRESULT WINAPI
SAORS_Stop(ISpatialAudioObjectRenderStream
*iface
)
336 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
339 TRACE("(%p)->()\n", This
);
341 hr
= IAudioClient_Stop(This
->client
);
343 WARN("IAudioClient::Stop failed: %08lx\n", hr
);
350 static HRESULT WINAPI
SAORS_Reset(ISpatialAudioObjectRenderStream
*iface
)
352 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
353 FIXME("(%p)->()\n", This
);
357 static HRESULT WINAPI
SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
,
358 UINT32
*dyn_count
, UINT32
*frames
)
360 static BOOL fixme_once
= FALSE
;
361 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
362 SpatialAudioObjectImpl
*object
;
365 TRACE("(%p)->(%p, %p)\n", This
, dyn_count
, frames
);
367 EnterCriticalSection(&This
->lock
);
369 if(This
->update_frames
!= ~0){
370 LeaveCriticalSection(&This
->lock
);
371 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
374 This
->update_frames
= This
->period_frames
;
376 if(This
->update_frames
> 0){
377 hr
= IAudioRenderClient_GetBuffer(This
->render
, This
->update_frames
, (BYTE
**)&This
->buf
);
379 WARN("GetBuffer failed: %08lx\n", hr
);
380 This
->update_frames
= ~0;
381 LeaveCriticalSection(&This
->lock
);
385 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
386 memset(object
->buf
, 0, This
->update_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
388 }else if (!fixme_once
){
390 FIXME("Zero frame update.\n");
394 *frames
= This
->update_frames
;
396 LeaveCriticalSection(&This
->lock
);
401 static void mix_static_object(SpatialAudioStreamImpl
*stream
, SpatialAudioObjectImpl
*object
)
403 float *in
= object
->buf
, *out
;
405 if(object
->static_idx
== ~0 ||
406 stream
->static_object_map
[object
->static_idx
] == ~0){
407 WARN("Got unmapped static object?! Not mixing. Type: 0x%x\n", object
->type
);
410 out
= stream
->buf
+ stream
->static_object_map
[object
->static_idx
];
411 for(i
= 0; i
< stream
->update_frames
; ++i
){
414 out
+= stream
->stream_fmtex
.Format
.nChannels
;
418 static HRESULT WINAPI
SAORS_EndUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
)
420 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
421 SpatialAudioObjectImpl
*object
;
424 TRACE("(%p)->()\n", This
);
426 EnterCriticalSection(&This
->lock
);
428 if(This
->update_frames
== ~0){
429 LeaveCriticalSection(&This
->lock
);
430 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
433 if(This
->update_frames
> 0){
434 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
435 if(object
->type
!= AudioObjectType_Dynamic
)
436 mix_static_object(This
, object
);
438 WARN("Don't know how to mix dynamic object yet. %p\n", object
);
441 hr
= IAudioRenderClient_ReleaseBuffer(This
->render
, This
->update_frames
, 0);
443 WARN("ReleaseBuffer failed: %08lx\n", hr
);
446 This
->update_frames
= ~0;
448 LeaveCriticalSection(&This
->lock
);
453 static HRESULT WINAPI
SAORS_ActivateSpatialAudioObject(ISpatialAudioObjectRenderStream
*iface
,
454 AudioObjectType type
, ISpatialAudioObject
**object
)
456 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
457 SpatialAudioObjectImpl
*obj
;
459 TRACE("(%p)->(0x%x, %p)\n", This
, type
, object
);
461 if(type
== AudioObjectType_Dynamic
)
462 return SPTLAUDCLNT_E_NO_MORE_OBJECTS
;
464 if(type
& ~This
->params
.StaticObjectTypeMask
)
465 return SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE
;
467 LIST_FOR_EACH_ENTRY(obj
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
468 if(obj
->static_idx
== AudioObjectType_to_index(type
))
469 return SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE
;
472 obj
= heap_alloc_zero(sizeof(*obj
));
473 obj
->ISpatialAudioObject_iface
.lpVtbl
= &ISpatialAudioObject_vtbl
;
476 if(type
== AudioObjectType_None
){
477 FIXME("AudioObjectType_None not implemented yet!\n");
478 obj
->static_idx
= ~0;
480 obj
->static_idx
= AudioObjectType_to_index(type
);
483 obj
->sa_stream
= This
;
484 SAORS_AddRef(&This
->ISpatialAudioObjectRenderStream_iface
);
486 obj
->buf
= heap_alloc_zero(This
->period_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
488 EnterCriticalSection(&This
->lock
);
490 list_add_tail(&This
->objects
, &obj
->entry
);
492 LeaveCriticalSection(&This
->lock
);
494 *object
= &obj
->ISpatialAudioObject_iface
;
499 static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl
= {
500 SAORS_QueryInterface
,
503 SAORS_GetAvailableDynamicObjectCount
,
508 SAORS_BeginUpdatingAudioObjects
,
509 SAORS_EndUpdatingAudioObjects
,
510 SAORS_ActivateSpatialAudioObject
,
513 static HRESULT WINAPI
SAC_QueryInterface(ISpatialAudioClient
*iface
, REFIID riid
, void **ppv
)
515 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
517 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
524 if (IsEqualIID(riid
, &IID_IUnknown
) ||
525 IsEqualIID(riid
, &IID_ISpatialAudioClient
)) {
526 *ppv
= &This
->ISpatialAudioClient_iface
;
529 return E_NOINTERFACE
;
531 IUnknown_AddRef((IUnknown
*)*ppv
);
536 static ULONG WINAPI
SAC_AddRef(ISpatialAudioClient
*iface
)
538 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
539 ULONG ref
= InterlockedIncrement(&This
->ref
);
540 TRACE("(%p) new ref %lu\n", This
, ref
);
544 static ULONG WINAPI
SAC_Release(ISpatialAudioClient
*iface
)
546 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
547 ULONG ref
= InterlockedDecrement(&This
->ref
);
548 TRACE("(%p) new ref %lu\n", This
, ref
);
550 IMMDevice_Release(This
->mmdev
);
556 static HRESULT WINAPI
SAC_GetStaticObjectPosition(ISpatialAudioClient
*iface
,
557 AudioObjectType type
, float *x
, float *y
, float *z
)
559 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
560 FIXME("(%p)->(0x%x, %p, %p, %p)\n", This
, type
, x
, y
, z
);
564 static HRESULT WINAPI
SAC_GetNativeStaticObjectTypeMask(ISpatialAudioClient
*iface
,
565 AudioObjectType
*mask
)
567 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
568 FIXME("(%p)->(%p)\n", This
, mask
);
572 static HRESULT WINAPI
SAC_GetMaxDynamicObjectCount(ISpatialAudioClient
*iface
,
575 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
576 FIXME("(%p)->(%p)\n", This
, value
);
583 static HRESULT WINAPI
SAC_GetSupportedAudioObjectFormatEnumerator(
584 ISpatialAudioClient
*iface
, IAudioFormatEnumerator
**enumerator
)
586 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
588 TRACE("(%p)->(%p)\n", This
, enumerator
);
590 *enumerator
= &This
->IAudioFormatEnumerator_iface
;
596 static HRESULT WINAPI
SAC_GetMaxFrameCount(ISpatialAudioClient
*iface
,
597 const WAVEFORMATEX
*format
, UINT32
*count
)
599 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
601 /* FIXME: should get device period from the device */
602 static const REFERENCE_TIME period
= 100000;
604 TRACE("(%p)->(%p, %p)\n", This
, format
, count
);
606 *count
= MulDiv(period
, format
->nSamplesPerSec
, 10000000);
611 static HRESULT WINAPI
SAC_IsAudioObjectFormatSupported(ISpatialAudioClient
*iface
,
612 const WAVEFORMATEX
*format
)
614 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
615 FIXME("(%p)->(%p)\n", This
, format
);
619 static HRESULT WINAPI
SAC_IsSpatialAudioStreamAvailable(ISpatialAudioClient
*iface
,
620 REFIID stream_uuid
, const PROPVARIANT
*info
)
622 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
623 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(stream_uuid
), info
);
627 static WAVEFORMATEX
*clone_fmtex(const WAVEFORMATEX
*src
)
629 WAVEFORMATEX
*r
= heap_alloc(sizeof(WAVEFORMATEX
) + src
->cbSize
);
630 memcpy(r
, src
, sizeof(WAVEFORMATEX
) + src
->cbSize
);
634 static const char *debugstr_fmtex(const WAVEFORMATEX
*fmt
)
636 static char buf
[2048];
637 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
638 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
639 snprintf(buf
, sizeof(buf
), "tag: 0x%x (%s), ch: %u (mask: 0x%lx), rate: %lu, depth: %u",
640 fmt
->wFormatTag
, debugstr_guid(&fmtex
->SubFormat
),
641 fmt
->nChannels
, fmtex
->dwChannelMask
, fmt
->nSamplesPerSec
,
642 fmt
->wBitsPerSample
);
644 snprintf(buf
, sizeof(buf
), "tag: 0x%x, ch: %u, rate: %lu, depth: %u",
645 fmt
->wFormatTag
, fmt
->nChannels
, fmt
->nSamplesPerSec
,
646 fmt
->wBitsPerSample
);
651 static void static_mask_to_channels(AudioObjectType static_mask
, WORD
*count
, DWORD
*mask
, UINT32
*map
)
653 UINT32 out_chan
= 0, map_idx
= 0;
656 #define CONVERT_MASK(f, t) \
657 if(static_mask & f){ \
660 map[map_idx++] = out_chan++; \
661 TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
663 map[map_idx++] = ~0; \
665 CONVERT_MASK(AudioObjectType_FrontLeft
, SPEAKER_FRONT_LEFT
);
666 CONVERT_MASK(AudioObjectType_FrontRight
, SPEAKER_FRONT_RIGHT
);
667 CONVERT_MASK(AudioObjectType_FrontCenter
, SPEAKER_FRONT_CENTER
);
668 CONVERT_MASK(AudioObjectType_LowFrequency
, SPEAKER_LOW_FREQUENCY
);
669 CONVERT_MASK(AudioObjectType_SideLeft
, SPEAKER_SIDE_LEFT
);
670 CONVERT_MASK(AudioObjectType_SideRight
, SPEAKER_SIDE_RIGHT
);
671 CONVERT_MASK(AudioObjectType_BackLeft
, SPEAKER_BACK_LEFT
);
672 CONVERT_MASK(AudioObjectType_BackRight
, SPEAKER_BACK_RIGHT
);
673 CONVERT_MASK(AudioObjectType_TopFrontLeft
, SPEAKER_TOP_FRONT_LEFT
);
674 CONVERT_MASK(AudioObjectType_TopFrontRight
, SPEAKER_TOP_FRONT_RIGHT
);
675 CONVERT_MASK(AudioObjectType_TopBackLeft
, SPEAKER_TOP_BACK_LEFT
);
676 CONVERT_MASK(AudioObjectType_TopBackRight
, SPEAKER_TOP_BACK_RIGHT
);
677 CONVERT_MASK(AudioObjectType_BackCenter
, SPEAKER_BACK_CENTER
);
680 static HRESULT
activate_stream(SpatialAudioStreamImpl
*stream
)
682 WAVEFORMATEXTENSIBLE
*object_fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->params
.ObjectFormat
;
684 REFERENCE_TIME period
;
686 if(!(object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
687 (object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
688 IsEqualGUID(&object_fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)))){
689 FIXME("Only float formats are supported for now\n");
693 hr
= IMMDevice_Activate(stream
->sa_client
->mmdev
, &IID_IAudioClient
,
694 CLSCTX_INPROC_SERVER
, NULL
, (void**)&stream
->client
);
696 WARN("Activate failed: %08lx\n", hr
);
700 hr
= IAudioClient_GetDevicePeriod(stream
->client
, &period
, NULL
);
702 WARN("GetDevicePeriod failed: %08lx\n", hr
);
703 IAudioClient_Release(stream
->client
);
707 stream
->stream_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
708 static_mask_to_channels(stream
->params
.StaticObjectTypeMask
,
709 &stream
->stream_fmtex
.Format
.nChannels
, &stream
->stream_fmtex
.dwChannelMask
,
710 stream
->static_object_map
);
711 stream
->stream_fmtex
.Format
.nSamplesPerSec
= stream
->params
.ObjectFormat
->nSamplesPerSec
;
712 stream
->stream_fmtex
.Format
.wBitsPerSample
= stream
->params
.ObjectFormat
->wBitsPerSample
;
713 stream
->stream_fmtex
.Format
.nBlockAlign
= (stream
->stream_fmtex
.Format
.nChannels
* stream
->stream_fmtex
.Format
.wBitsPerSample
) / 8;
714 stream
->stream_fmtex
.Format
.nAvgBytesPerSec
= stream
->stream_fmtex
.Format
.nSamplesPerSec
* stream
->stream_fmtex
.Format
.nBlockAlign
;
715 stream
->stream_fmtex
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
716 stream
->stream_fmtex
.Samples
.wValidBitsPerSample
= stream
->stream_fmtex
.Format
.wBitsPerSample
;
717 stream
->stream_fmtex
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
719 hr
= IAudioClient_Initialize(stream
->client
, AUDCLNT_SHAREMODE_SHARED
,
720 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
| AUDCLNT_STREAMFLAGS_NOPERSIST
,
721 period
, 0, &stream
->stream_fmtex
.Format
, NULL
);
723 WARN("Initialize failed: %08lx\n", hr
);
724 IAudioClient_Release(stream
->client
);
728 hr
= IAudioClient_SetEventHandle(stream
->client
, stream
->params
.EventHandle
);
730 WARN("SetEventHandle failed: %08lx\n", hr
);
731 IAudioClient_Release(stream
->client
);
735 hr
= IAudioClient_GetService(stream
->client
, &IID_IAudioRenderClient
, (void**)&stream
->render
);
737 WARN("GetService(AudioRenderClient) failed: %08lx\n", hr
);
738 IAudioClient_Release(stream
->client
);
742 stream
->period_frames
= MulDiv(period
, stream
->stream_fmtex
.Format
.nSamplesPerSec
, 10000000);
747 static HRESULT WINAPI
SAC_ActivateSpatialAudioStream(ISpatialAudioClient
*iface
,
748 const PROPVARIANT
*prop
, REFIID riid
, void **stream
)
750 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
751 SpatialAudioObjectRenderStreamActivationParams
*params
;
754 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), stream
);
756 if(IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStream
)){
757 SpatialAudioStreamImpl
*obj
;
760 (prop
->vt
!= VT_BLOB
||
761 prop
->blob
.cbSize
!= sizeof(SpatialAudioObjectRenderStreamActivationParams
))){
762 WARN("Got invalid params\n");
767 params
= (SpatialAudioObjectRenderStreamActivationParams
*) prop
->blob
.pBlobData
;
769 if(params
->StaticObjectTypeMask
& AudioObjectType_Dynamic
){
774 if(params
->EventHandle
== INVALID_HANDLE_VALUE
||
775 params
->EventHandle
== 0){
780 if(!params
->ObjectFormat
||
781 memcmp(params
->ObjectFormat
, &This
->object_fmtex
.Format
, sizeof(*params
->ObjectFormat
) + params
->ObjectFormat
->cbSize
)){
783 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
786 obj
= heap_alloc_zero(sizeof(SpatialAudioStreamImpl
));
788 obj
->ISpatialAudioObjectRenderStream_iface
.lpVtbl
= &ISpatialAudioObjectRenderStream_vtbl
;
790 memcpy(&obj
->params
, params
, sizeof(obj
->params
));
792 obj
->update_frames
= ~0;
794 InitializeCriticalSection(&obj
->lock
);
795 list_init(&obj
->objects
);
797 obj
->sa_client
= This
;
798 SAC_AddRef(&This
->ISpatialAudioClient_iface
);
800 obj
->params
.ObjectFormat
= clone_fmtex(obj
->params
.ObjectFormat
);
802 DuplicateHandle(GetCurrentProcess(), obj
->params
.EventHandle
,
803 GetCurrentProcess(), &obj
->params
.EventHandle
, 0, FALSE
,
804 DUPLICATE_SAME_ACCESS
);
806 if(obj
->params
.NotifyObject
)
807 ISpatialAudioObjectRenderStreamNotify_AddRef(obj
->params
.NotifyObject
);
809 if(TRACE_ON(mmdevapi
)){
810 TRACE("ObjectFormat: {%s}\n", debugstr_fmtex(obj
->params
.ObjectFormat
));
811 TRACE("StaticObjectTypeMask: 0x%x\n", obj
->params
.StaticObjectTypeMask
);
812 TRACE("MinDynamicObjectCount: 0x%x\n", obj
->params
.MinDynamicObjectCount
);
813 TRACE("MaxDynamicObjectCount: 0x%x\n", obj
->params
.MaxDynamicObjectCount
);
814 TRACE("Category: 0x%x\n", obj
->params
.Category
);
815 TRACE("EventHandle: %p\n", obj
->params
.EventHandle
);
816 TRACE("NotifyObject: %p\n", obj
->params
.NotifyObject
);
819 hr
= activate_stream(obj
);
821 if(obj
->params
.NotifyObject
)
822 ISpatialAudioObjectRenderStreamNotify_Release(obj
->params
.NotifyObject
);
823 DeleteCriticalSection(&obj
->lock
);
824 heap_free((void*)obj
->params
.ObjectFormat
);
825 CloseHandle(obj
->params
.EventHandle
);
826 ISpatialAudioClient_Release(&obj
->sa_client
->ISpatialAudioClient_iface
);
832 *stream
= &obj
->ISpatialAudioObjectRenderStream_iface
;
834 FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid
));
842 static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl
= {
846 SAC_GetStaticObjectPosition
,
847 SAC_GetNativeStaticObjectTypeMask
,
848 SAC_GetMaxDynamicObjectCount
,
849 SAC_GetSupportedAudioObjectFormatEnumerator
,
850 SAC_GetMaxFrameCount
,
851 SAC_IsAudioObjectFormatSupported
,
852 SAC_IsSpatialAudioStreamAvailable
,
853 SAC_ActivateSpatialAudioStream
,
856 static HRESULT WINAPI
SAOFE_QueryInterface(IAudioFormatEnumerator
*iface
,
857 REFIID riid
, void **ppvObject
)
859 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
860 return SAC_QueryInterface(&This
->ISpatialAudioClient_iface
, riid
, ppvObject
);
863 static ULONG WINAPI
SAOFE_AddRef(IAudioFormatEnumerator
*iface
)
865 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
866 return SAC_AddRef(&This
->ISpatialAudioClient_iface
);
869 static ULONG WINAPI
SAOFE_Release(IAudioFormatEnumerator
*iface
)
871 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
872 return SAC_Release(&This
->ISpatialAudioClient_iface
);
875 static HRESULT WINAPI
SAOFE_GetCount(IAudioFormatEnumerator
*iface
, UINT32
*count
)
877 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
879 TRACE("(%p)->(%p)\n", This
, count
);
886 static HRESULT WINAPI
SAOFE_GetFormat(IAudioFormatEnumerator
*iface
,
887 UINT32 index
, WAVEFORMATEX
**format
)
889 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
891 TRACE("(%p)->(%u, %p)\n", This
, index
, format
);
896 *format
= &This
->object_fmtex
.Format
;
901 static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl
= {
902 SAOFE_QueryInterface
,
909 HRESULT
SpatialAudioClient_Create(IMMDevice
*mmdev
, ISpatialAudioClient
**out
)
911 SpatialAudioImpl
*obj
;
912 IAudioClient
*aclient
;
913 WAVEFORMATEX
*closest
;
916 obj
= heap_alloc_zero(sizeof(*obj
));
919 obj
->ISpatialAudioClient_iface
.lpVtbl
= &ISpatialAudioClient_vtbl
;
920 obj
->IAudioFormatEnumerator_iface
.lpVtbl
= &IAudioFormatEnumerator_vtbl
;
922 obj
->object_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
923 obj
->object_fmtex
.Format
.nChannels
= 1;
924 obj
->object_fmtex
.Format
.nSamplesPerSec
= 48000;
925 obj
->object_fmtex
.Format
.wBitsPerSample
= sizeof(float) * 8;
926 obj
->object_fmtex
.Format
.nBlockAlign
= (obj
->object_fmtex
.Format
.nChannels
* obj
->object_fmtex
.Format
.wBitsPerSample
) / 8;
927 obj
->object_fmtex
.Format
.nAvgBytesPerSec
= obj
->object_fmtex
.Format
.nSamplesPerSec
* obj
->object_fmtex
.Format
.nBlockAlign
;
928 obj
->object_fmtex
.Format
.cbSize
= 0;
930 hr
= IMMDevice_Activate(mmdev
, &IID_IAudioClient
,
931 CLSCTX_INPROC_SERVER
, NULL
, (void**)&aclient
);
933 WARN("Activate failed: %08lx\n", hr
);
938 hr
= IAudioClient_IsFormatSupported(aclient
, AUDCLNT_SHAREMODE_SHARED
, &obj
->object_fmtex
.Format
, &closest
);
940 IAudioClient_Release(aclient
);
943 if(sizeof(WAVEFORMATEX
) + closest
->cbSize
> sizeof(obj
->object_fmtex
)){
944 ERR("Returned format too large: %s\n", debugstr_fmtex(closest
));
945 CoTaskMemFree(closest
);
947 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
948 }else if(!((closest
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
949 (closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
950 IsEqualGUID(&((WAVEFORMATEXTENSIBLE
*)closest
)->SubFormat
,
951 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))) &&
952 closest
->wBitsPerSample
== 32)){
953 ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest
));
954 CoTaskMemFree(closest
);
956 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
958 WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest
));
959 memcpy(&obj
->object_fmtex
,
961 sizeof(WAVEFORMATEX
) + closest
->cbSize
);
962 CoTaskMemFree(closest
);
963 } else if(hr
!= S_OK
){
964 WARN("Checking supported formats failed: %08lx\n", hr
);
970 IMMDevice_AddRef(mmdev
);
972 *out
= &obj
->ISpatialAudioClient_iface
;