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"
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 %u\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 %u\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 %u\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 %u\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
);
309 static HRESULT WINAPI
SAORS_GetService(ISpatialAudioObjectRenderStream
*iface
,
310 REFIID riid
, void **service
)
312 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
313 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), service
);
317 static HRESULT WINAPI
SAORS_Start(ISpatialAudioObjectRenderStream
*iface
)
319 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
322 TRACE("(%p)->()\n", This
);
324 hr
= IAudioClient_Start(This
->client
);
326 WARN("IAudioClient::Start failed: %08x\n", hr
);
333 static HRESULT WINAPI
SAORS_Stop(ISpatialAudioObjectRenderStream
*iface
)
335 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
338 TRACE("(%p)->()\n", This
);
340 hr
= IAudioClient_Stop(This
->client
);
342 WARN("IAudioClient::Stop failed: %08x\n", hr
);
349 static HRESULT WINAPI
SAORS_Reset(ISpatialAudioObjectRenderStream
*iface
)
351 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
352 FIXME("(%p)->()\n", This
);
356 static HRESULT WINAPI
SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
,
357 UINT32
*dyn_count
, UINT32
*frames
)
359 static BOOL fixme_once
= FALSE
;
360 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
361 SpatialAudioObjectImpl
*object
;
364 TRACE("(%p)->(%p, %p)\n", This
, dyn_count
, frames
);
366 EnterCriticalSection(&This
->lock
);
368 if(This
->update_frames
!= ~0){
369 LeaveCriticalSection(&This
->lock
);
370 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
373 This
->update_frames
= This
->period_frames
;
375 if(This
->update_frames
> 0){
376 hr
= IAudioRenderClient_GetBuffer(This
->render
, This
->update_frames
, (BYTE
**)&This
->buf
);
378 WARN("GetBuffer failed: %08x\n", hr
);
379 This
->update_frames
= ~0;
380 LeaveCriticalSection(&This
->lock
);
384 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
385 memset(object
->buf
, 0, This
->update_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
387 }else if (!fixme_once
){
389 FIXME("Zero frame update.\n");
393 *frames
= This
->update_frames
;
395 LeaveCriticalSection(&This
->lock
);
400 static void mix_static_object(SpatialAudioStreamImpl
*stream
, SpatialAudioObjectImpl
*object
)
402 float *in
= object
->buf
, *out
;
404 if(object
->static_idx
== ~0 ||
405 stream
->static_object_map
[object
->static_idx
] == ~0){
406 WARN("Got unmapped static object?! Not mixing. Type: 0x%x\n", object
->type
);
409 out
= stream
->buf
+ stream
->static_object_map
[object
->static_idx
];
410 for(i
= 0; i
< stream
->update_frames
; ++i
){
413 out
+= stream
->stream_fmtex
.Format
.nChannels
;
417 static HRESULT WINAPI
SAORS_EndUpdatingAudioObjects(ISpatialAudioObjectRenderStream
*iface
)
419 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
420 SpatialAudioObjectImpl
*object
;
423 TRACE("(%p)->()\n", This
);
425 EnterCriticalSection(&This
->lock
);
427 if(This
->update_frames
== ~0){
428 LeaveCriticalSection(&This
->lock
);
429 return SPTLAUDCLNT_E_OUT_OF_ORDER
;
432 if(This
->update_frames
> 0){
433 LIST_FOR_EACH_ENTRY(object
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
434 if(object
->type
!= AudioObjectType_Dynamic
)
435 mix_static_object(This
, object
);
437 WARN("Don't know how to mix dynamic object yet. %p\n", object
);
440 hr
= IAudioRenderClient_ReleaseBuffer(This
->render
, This
->update_frames
, 0);
442 WARN("ReleaseBuffer failed: %08x\n", hr
);
445 This
->update_frames
= ~0;
447 LeaveCriticalSection(&This
->lock
);
452 static HRESULT WINAPI
SAORS_ActivateSpatialAudioObject(ISpatialAudioObjectRenderStream
*iface
,
453 AudioObjectType type
, ISpatialAudioObject
**object
)
455 SpatialAudioStreamImpl
*This
= impl_from_ISpatialAudioObjectRenderStream(iface
);
456 SpatialAudioObjectImpl
*obj
;
458 TRACE("(%p)->(0x%x, %p)\n", This
, type
, object
);
460 if(type
== AudioObjectType_Dynamic
)
461 return SPTLAUDCLNT_E_NO_MORE_OBJECTS
;
463 if(type
& ~This
->params
.StaticObjectTypeMask
)
464 return SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE
;
466 LIST_FOR_EACH_ENTRY(obj
, &This
->objects
, SpatialAudioObjectImpl
, entry
){
467 if(obj
->static_idx
== AudioObjectType_to_index(type
))
468 return SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE
;
471 obj
= heap_alloc_zero(sizeof(*obj
));
472 obj
->ISpatialAudioObject_iface
.lpVtbl
= &ISpatialAudioObject_vtbl
;
475 if(type
== AudioObjectType_None
){
476 FIXME("AudioObjectType_None not implemented yet!\n");
477 obj
->static_idx
= ~0;
479 obj
->static_idx
= AudioObjectType_to_index(type
);
482 obj
->sa_stream
= This
;
483 SAORS_AddRef(&This
->ISpatialAudioObjectRenderStream_iface
);
485 obj
->buf
= heap_alloc_zero(This
->period_frames
* This
->sa_client
->object_fmtex
.Format
.nBlockAlign
);
487 EnterCriticalSection(&This
->lock
);
489 list_add_tail(&This
->objects
, &obj
->entry
);
491 LeaveCriticalSection(&This
->lock
);
493 *object
= &obj
->ISpatialAudioObject_iface
;
498 static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl
= {
499 SAORS_QueryInterface
,
502 SAORS_GetAvailableDynamicObjectCount
,
507 SAORS_BeginUpdatingAudioObjects
,
508 SAORS_EndUpdatingAudioObjects
,
509 SAORS_ActivateSpatialAudioObject
,
512 static HRESULT WINAPI
SAC_QueryInterface(ISpatialAudioClient
*iface
, REFIID riid
, void **ppv
)
514 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
516 TRACE("(%p)->(%s,%p)\n", This
, debugstr_guid(riid
), ppv
);
523 if (IsEqualIID(riid
, &IID_IUnknown
) ||
524 IsEqualIID(riid
, &IID_ISpatialAudioClient
)) {
525 *ppv
= &This
->ISpatialAudioClient_iface
;
528 return E_NOINTERFACE
;
530 IUnknown_AddRef((IUnknown
*)*ppv
);
535 static ULONG WINAPI
SAC_AddRef(ISpatialAudioClient
*iface
)
537 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
538 ULONG ref
= InterlockedIncrement(&This
->ref
);
539 TRACE("(%p) new ref %u\n", This
, ref
);
543 static ULONG WINAPI
SAC_Release(ISpatialAudioClient
*iface
)
545 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
546 ULONG ref
= InterlockedDecrement(&This
->ref
);
547 TRACE("(%p) new ref %u\n", This
, ref
);
549 IMMDevice_Release(This
->mmdev
);
555 static HRESULT WINAPI
SAC_GetStaticObjectPosition(ISpatialAudioClient
*iface
,
556 AudioObjectType type
, float *x
, float *y
, float *z
)
558 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
559 FIXME("(%p)->(0x%x, %p, %p, %p)\n", This
, type
, x
, y
, z
);
563 static HRESULT WINAPI
SAC_GetNativeStaticObjectTypeMask(ISpatialAudioClient
*iface
,
564 AudioObjectType
*mask
)
566 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
567 FIXME("(%p)->(%p)\n", This
, mask
);
571 static HRESULT WINAPI
SAC_GetMaxDynamicObjectCount(ISpatialAudioClient
*iface
,
574 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
575 FIXME("(%p)->(%p)\n", This
, value
);
582 static HRESULT WINAPI
SAC_GetSupportedAudioObjectFormatEnumerator(
583 ISpatialAudioClient
*iface
, IAudioFormatEnumerator
**enumerator
)
585 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
587 TRACE("(%p)->(%p)\n", This
, enumerator
);
589 *enumerator
= &This
->IAudioFormatEnumerator_iface
;
595 static HRESULT WINAPI
SAC_GetMaxFrameCount(ISpatialAudioClient
*iface
,
596 const WAVEFORMATEX
*format
, UINT32
*count
)
598 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
600 /* FIXME: should get device period from the device */
601 static const REFERENCE_TIME period
= 100000;
603 TRACE("(%p)->(%p, %p)\n", This
, format
, count
);
605 *count
= MulDiv(period
, format
->nSamplesPerSec
, 10000000);
610 static HRESULT WINAPI
SAC_IsAudioObjectFormatSupported(ISpatialAudioClient
*iface
,
611 const WAVEFORMATEX
*format
)
613 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
614 FIXME("(%p)->(%p)\n", This
, format
);
618 static HRESULT WINAPI
SAC_IsSpatialAudioStreamAvailable(ISpatialAudioClient
*iface
,
619 REFIID stream_uuid
, const PROPVARIANT
*info
)
621 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
622 FIXME("(%p)->(%s, %p)\n", This
, debugstr_guid(stream_uuid
), info
);
626 static WAVEFORMATEX
*clone_fmtex(const WAVEFORMATEX
*src
)
628 WAVEFORMATEX
*r
= heap_alloc(sizeof(WAVEFORMATEX
) + src
->cbSize
);
629 memcpy(r
, src
, sizeof(WAVEFORMATEX
) + src
->cbSize
);
633 static const char *debugstr_fmtex(const WAVEFORMATEX
*fmt
)
635 static char buf
[2048];
636 if(fmt
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
){
637 const WAVEFORMATEXTENSIBLE
*fmtex
= (const WAVEFORMATEXTENSIBLE
*)fmt
;
638 snprintf(buf
, sizeof(buf
), "tag: 0x%x (%s), ch: %u (mask: 0x%x), rate: %u, depth: %u",
639 fmt
->wFormatTag
, debugstr_guid(&fmtex
->SubFormat
),
640 fmt
->nChannels
, fmtex
->dwChannelMask
, fmt
->nSamplesPerSec
,
641 fmt
->wBitsPerSample
);
643 snprintf(buf
, sizeof(buf
), "tag: 0x%x, ch: %u, rate: %u, depth: %u",
644 fmt
->wFormatTag
, fmt
->nChannels
, fmt
->nSamplesPerSec
,
645 fmt
->wBitsPerSample
);
650 static void static_mask_to_channels(AudioObjectType static_mask
, WORD
*count
, DWORD
*mask
, UINT32
*map
)
652 UINT32 out_chan
= 0, map_idx
= 0;
655 #define CONVERT_MASK(f, t) \
656 if(static_mask & f){ \
659 map[map_idx++] = out_chan++; \
660 TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
662 map[map_idx++] = ~0; \
664 CONVERT_MASK(AudioObjectType_FrontLeft
, SPEAKER_FRONT_LEFT
);
665 CONVERT_MASK(AudioObjectType_FrontRight
, SPEAKER_FRONT_RIGHT
);
666 CONVERT_MASK(AudioObjectType_FrontCenter
, SPEAKER_FRONT_CENTER
);
667 CONVERT_MASK(AudioObjectType_LowFrequency
, SPEAKER_LOW_FREQUENCY
);
668 CONVERT_MASK(AudioObjectType_SideLeft
, SPEAKER_SIDE_LEFT
);
669 CONVERT_MASK(AudioObjectType_SideRight
, SPEAKER_SIDE_RIGHT
);
670 CONVERT_MASK(AudioObjectType_BackLeft
, SPEAKER_BACK_LEFT
);
671 CONVERT_MASK(AudioObjectType_BackRight
, SPEAKER_BACK_RIGHT
);
672 CONVERT_MASK(AudioObjectType_TopFrontLeft
, SPEAKER_TOP_FRONT_LEFT
);
673 CONVERT_MASK(AudioObjectType_TopFrontRight
, SPEAKER_TOP_FRONT_RIGHT
);
674 CONVERT_MASK(AudioObjectType_TopBackLeft
, SPEAKER_TOP_BACK_LEFT
);
675 CONVERT_MASK(AudioObjectType_TopBackRight
, SPEAKER_TOP_BACK_RIGHT
);
676 CONVERT_MASK(AudioObjectType_BackCenter
, SPEAKER_BACK_CENTER
);
679 static HRESULT
activate_stream(SpatialAudioStreamImpl
*stream
)
681 WAVEFORMATEXTENSIBLE
*object_fmtex
= (WAVEFORMATEXTENSIBLE
*)stream
->params
.ObjectFormat
;
683 REFERENCE_TIME period
;
685 if(!(object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
686 (object_fmtex
->Format
.wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
687 IsEqualGUID(&object_fmtex
->SubFormat
, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
)))){
688 FIXME("Only float formats are supported for now\n");
692 hr
= IMMDevice_Activate(stream
->sa_client
->mmdev
, &IID_IAudioClient
,
693 CLSCTX_INPROC_SERVER
, NULL
, (void**)&stream
->client
);
695 WARN("Activate failed: %08x\n", hr
);
699 hr
= IAudioClient_GetDevicePeriod(stream
->client
, &period
, NULL
);
701 WARN("GetDevicePeriod failed: %08x\n", hr
);
702 IAudioClient_Release(stream
->client
);
706 stream
->stream_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_EXTENSIBLE
;
707 static_mask_to_channels(stream
->params
.StaticObjectTypeMask
,
708 &stream
->stream_fmtex
.Format
.nChannels
, &stream
->stream_fmtex
.dwChannelMask
,
709 stream
->static_object_map
);
710 stream
->stream_fmtex
.Format
.nSamplesPerSec
= stream
->params
.ObjectFormat
->nSamplesPerSec
;
711 stream
->stream_fmtex
.Format
.wBitsPerSample
= stream
->params
.ObjectFormat
->wBitsPerSample
;
712 stream
->stream_fmtex
.Format
.nBlockAlign
= (stream
->stream_fmtex
.Format
.nChannels
* stream
->stream_fmtex
.Format
.wBitsPerSample
) / 8;
713 stream
->stream_fmtex
.Format
.nAvgBytesPerSec
= stream
->stream_fmtex
.Format
.nSamplesPerSec
* stream
->stream_fmtex
.Format
.nBlockAlign
;
714 stream
->stream_fmtex
.Format
.cbSize
= sizeof(WAVEFORMATEXTENSIBLE
) - sizeof(WAVEFORMATEX
);
715 stream
->stream_fmtex
.Samples
.wValidBitsPerSample
= stream
->stream_fmtex
.Format
.wBitsPerSample
;
716 stream
->stream_fmtex
.SubFormat
= KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
;
718 hr
= IAudioClient_Initialize(stream
->client
, AUDCLNT_SHAREMODE_SHARED
,
719 AUDCLNT_STREAMFLAGS_EVENTCALLBACK
| AUDCLNT_STREAMFLAGS_NOPERSIST
,
720 period
, 0, &stream
->stream_fmtex
.Format
, NULL
);
722 WARN("Initialize failed: %08x\n", hr
);
723 IAudioClient_Release(stream
->client
);
727 hr
= IAudioClient_SetEventHandle(stream
->client
, stream
->params
.EventHandle
);
729 WARN("SetEventHandle failed: %08x\n", hr
);
730 IAudioClient_Release(stream
->client
);
734 hr
= IAudioClient_GetService(stream
->client
, &IID_IAudioRenderClient
, (void**)&stream
->render
);
736 WARN("GetService(AudioRenderClient) failed: %08x\n", hr
);
737 IAudioClient_Release(stream
->client
);
741 stream
->period_frames
= MulDiv(period
, stream
->stream_fmtex
.Format
.nSamplesPerSec
, 10000000);
746 static HRESULT WINAPI
SAC_ActivateSpatialAudioStream(ISpatialAudioClient
*iface
,
747 const PROPVARIANT
*prop
, REFIID riid
, void **stream
)
749 SpatialAudioImpl
*This
= impl_from_ISpatialAudioClient(iface
);
750 SpatialAudioObjectRenderStreamActivationParams
*params
;
753 TRACE("(%p)->(%s, %p)\n", This
, debugstr_guid(riid
), stream
);
755 if(IsEqualIID(riid
, &IID_ISpatialAudioObjectRenderStream
)){
756 SpatialAudioStreamImpl
*obj
;
759 (prop
->vt
!= VT_BLOB
||
760 prop
->u
.blob
.cbSize
!= sizeof(SpatialAudioObjectRenderStreamActivationParams
))){
761 WARN("Got invalid params\n");
766 params
= (SpatialAudioObjectRenderStreamActivationParams
*) prop
->u
.blob
.pBlobData
;
768 if(params
->StaticObjectTypeMask
& AudioObjectType_Dynamic
){
773 if(params
->EventHandle
== INVALID_HANDLE_VALUE
||
774 params
->EventHandle
== 0){
779 if(!params
->ObjectFormat
||
780 memcmp(params
->ObjectFormat
, &This
->object_fmtex
.Format
, sizeof(*params
->ObjectFormat
) + params
->ObjectFormat
->cbSize
)){
782 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
785 obj
= heap_alloc_zero(sizeof(SpatialAudioStreamImpl
));
787 obj
->ISpatialAudioObjectRenderStream_iface
.lpVtbl
= &ISpatialAudioObjectRenderStream_vtbl
;
789 memcpy(&obj
->params
, params
, sizeof(obj
->params
));
791 obj
->update_frames
= ~0;
793 InitializeCriticalSection(&obj
->lock
);
794 list_init(&obj
->objects
);
796 obj
->sa_client
= This
;
797 SAC_AddRef(&This
->ISpatialAudioClient_iface
);
799 obj
->params
.ObjectFormat
= clone_fmtex(obj
->params
.ObjectFormat
);
801 DuplicateHandle(GetCurrentProcess(), obj
->params
.EventHandle
,
802 GetCurrentProcess(), &obj
->params
.EventHandle
, 0, FALSE
,
803 DUPLICATE_SAME_ACCESS
);
805 if(obj
->params
.NotifyObject
)
806 ISpatialAudioObjectRenderStreamNotify_AddRef(obj
->params
.NotifyObject
);
808 if(TRACE_ON(mmdevapi
)){
809 TRACE("ObjectFormat: {%s}\n", debugstr_fmtex(obj
->params
.ObjectFormat
));
810 TRACE("StaticObjectTypeMask: 0x%x\n", obj
->params
.StaticObjectTypeMask
);
811 TRACE("MinDynamicObjectCount: 0x%x\n", obj
->params
.MinDynamicObjectCount
);
812 TRACE("MaxDynamicObjectCount: 0x%x\n", obj
->params
.MaxDynamicObjectCount
);
813 TRACE("Category: 0x%x\n", obj
->params
.Category
);
814 TRACE("EventHandle: %p\n", obj
->params
.EventHandle
);
815 TRACE("NotifyObject: %p\n", obj
->params
.NotifyObject
);
818 hr
= activate_stream(obj
);
820 if(obj
->params
.NotifyObject
)
821 ISpatialAudioObjectRenderStreamNotify_Release(obj
->params
.NotifyObject
);
822 DeleteCriticalSection(&obj
->lock
);
823 heap_free((void*)obj
->params
.ObjectFormat
);
824 CloseHandle(obj
->params
.EventHandle
);
825 ISpatialAudioClient_Release(&obj
->sa_client
->ISpatialAudioClient_iface
);
831 *stream
= &obj
->ISpatialAudioObjectRenderStream_iface
;
833 FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid
));
841 static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl
= {
845 SAC_GetStaticObjectPosition
,
846 SAC_GetNativeStaticObjectTypeMask
,
847 SAC_GetMaxDynamicObjectCount
,
848 SAC_GetSupportedAudioObjectFormatEnumerator
,
849 SAC_GetMaxFrameCount
,
850 SAC_IsAudioObjectFormatSupported
,
851 SAC_IsSpatialAudioStreamAvailable
,
852 SAC_ActivateSpatialAudioStream
,
855 static HRESULT WINAPI
SAOFE_QueryInterface(IAudioFormatEnumerator
*iface
,
856 REFIID riid
, void **ppvObject
)
858 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
859 return SAC_QueryInterface(&This
->ISpatialAudioClient_iface
, riid
, ppvObject
);
862 static ULONG WINAPI
SAOFE_AddRef(IAudioFormatEnumerator
*iface
)
864 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
865 return SAC_AddRef(&This
->ISpatialAudioClient_iface
);
868 static ULONG WINAPI
SAOFE_Release(IAudioFormatEnumerator
*iface
)
870 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
871 return SAC_Release(&This
->ISpatialAudioClient_iface
);
874 static HRESULT WINAPI
SAOFE_GetCount(IAudioFormatEnumerator
*iface
, UINT32
*count
)
876 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
878 TRACE("(%p)->(%p)\n", This
, count
);
885 static HRESULT WINAPI
SAOFE_GetFormat(IAudioFormatEnumerator
*iface
,
886 UINT32 index
, WAVEFORMATEX
**format
)
888 SpatialAudioImpl
*This
= impl_from_IAudioFormatEnumerator(iface
);
890 TRACE("(%p)->(%u, %p)\n", This
, index
, format
);
895 *format
= &This
->object_fmtex
.Format
;
900 static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl
= {
901 SAOFE_QueryInterface
,
908 HRESULT
SpatialAudioClient_Create(IMMDevice
*mmdev
, ISpatialAudioClient
**out
)
910 SpatialAudioImpl
*obj
;
911 IAudioClient
*aclient
;
912 WAVEFORMATEX
*closest
;
915 obj
= heap_alloc_zero(sizeof(*obj
));
918 obj
->ISpatialAudioClient_iface
.lpVtbl
= &ISpatialAudioClient_vtbl
;
919 obj
->IAudioFormatEnumerator_iface
.lpVtbl
= &IAudioFormatEnumerator_vtbl
;
921 obj
->object_fmtex
.Format
.wFormatTag
= WAVE_FORMAT_IEEE_FLOAT
;
922 obj
->object_fmtex
.Format
.nChannels
= 1;
923 obj
->object_fmtex
.Format
.nSamplesPerSec
= 48000;
924 obj
->object_fmtex
.Format
.wBitsPerSample
= sizeof(float) * 8;
925 obj
->object_fmtex
.Format
.nBlockAlign
= (obj
->object_fmtex
.Format
.nChannels
* obj
->object_fmtex
.Format
.wBitsPerSample
) / 8;
926 obj
->object_fmtex
.Format
.nAvgBytesPerSec
= obj
->object_fmtex
.Format
.nSamplesPerSec
* obj
->object_fmtex
.Format
.nBlockAlign
;
927 obj
->object_fmtex
.Format
.cbSize
= 0;
929 hr
= IMMDevice_Activate(mmdev
, &IID_IAudioClient
,
930 CLSCTX_INPROC_SERVER
, NULL
, (void**)&aclient
);
932 WARN("Activate failed: %08x\n", hr
);
937 hr
= IAudioClient_IsFormatSupported(aclient
, AUDCLNT_SHAREMODE_SHARED
, &obj
->object_fmtex
.Format
, &closest
);
939 IAudioClient_Release(aclient
);
942 if(sizeof(WAVEFORMATEX
) + closest
->cbSize
> sizeof(obj
->object_fmtex
)){
943 ERR("Returned format too large: %s\n", debugstr_fmtex(closest
));
944 CoTaskMemFree(closest
);
946 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
947 }else if(!((closest
->wFormatTag
== WAVE_FORMAT_IEEE_FLOAT
||
948 (closest
->wFormatTag
== WAVE_FORMAT_EXTENSIBLE
&&
949 IsEqualGUID(&((WAVEFORMATEXTENSIBLE
*)closest
)->SubFormat
,
950 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
))) &&
951 closest
->wBitsPerSample
== 32)){
952 ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest
));
953 CoTaskMemFree(closest
);
955 return AUDCLNT_E_UNSUPPORTED_FORMAT
;
957 WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest
));
958 memcpy(&obj
->object_fmtex
,
960 sizeof(WAVEFORMATEX
) + closest
->cbSize
);
961 CoTaskMemFree(closest
);
962 } else if(hr
!= S_OK
){
963 WARN("Checking supported formats failed: %08x\n", hr
);
969 IMMDevice_AddRef(mmdev
);
971 *out
= &obj
->ISpatialAudioClient_iface
;