dxdiagn: Remove DECLSPEC_HIDDEN usage.
[wine.git] / dlls / mmdevapi / spatialaudio.c
blob9bd14038e1db6ab605ed316c03665a8cc666e5c4
1 /*
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
19 #define COBJMACROS
21 #include <stdarg.h>
23 #include "windef.h"
24 #include "winbase.h"
25 #include "winnls.h"
26 #include "winreg.h"
27 #include "wine/heap.h"
28 #include "wine/debug.h"
29 #include "wine/list.h"
31 #include "ole2.h"
32 #include "mmdeviceapi.h"
33 #include "mmsystem.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)
45 UINT32 o = 0;
46 while(type){
47 type >>= 1;
48 ++o;
50 return o - 2;
53 typedef struct SpatialAudioImpl SpatialAudioImpl;
54 typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl;
55 typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl;
57 struct SpatialAudioObjectImpl {
58 ISpatialAudioObject ISpatialAudioObject_iface;
59 LONG ref;
61 SpatialAudioStreamImpl *sa_stream;
62 AudioObjectType type;
63 UINT32 static_idx;
65 float *buf;
67 struct list entry;
70 struct SpatialAudioStreamImpl {
71 ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface;
72 LONG ref;
73 CRITICAL_SECTION lock;
75 SpatialAudioImpl *sa_client;
76 SpatialAudioObjectRenderStreamActivationParams params;
78 IAudioClient *client;
79 IAudioRenderClient *render;
81 UINT32 period_frames, update_frames;
82 WAVEFORMATEXTENSIBLE stream_fmtex;
84 float *buf;
86 UINT32 static_object_map[17];
88 struct list objects;
91 struct SpatialAudioImpl {
92 ISpatialAudioClient ISpatialAudioClient_iface;
93 IAudioFormatEnumerator IAudioFormatEnumerator_iface;
94 IMMDevice *mmdev;
95 LONG ref;
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);
126 if (!ppv)
127 return E_POINTER;
129 *ppv = NULL;
131 if (IsEqualIID(riid, &IID_IUnknown) ||
132 IsEqualIID(riid, &IID_ISpatialAudioObjectBase) ||
133 IsEqualIID(riid, &IID_ISpatialAudioObject)) {
134 *ppv = &This->ISpatialAudioObject_iface;
136 else
137 return E_NOINTERFACE;
139 IUnknown_AddRef((IUnknown *)*ppv);
141 return S_OK;
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);
149 return 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);
157 if(!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);
164 heap_free(This);
166 return ref;
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);
189 return S_OK;
192 static HRESULT WINAPI SAO_SetEndOfStream(ISpatialAudioObject *iface, UINT32 frames)
194 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
195 FIXME("(%p)->(%u)\n", This, frames);
196 return E_NOTIMPL;
199 static HRESULT WINAPI SAO_IsActive(ISpatialAudioObject *iface, BOOL *active)
201 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
202 FIXME("(%p)->(%p)\n", This, active);
203 return E_NOTIMPL;
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);
213 *type = This->type;
215 return S_OK;
218 static HRESULT WINAPI SAO_SetPosition(ISpatialAudioObject *iface, float x,
219 float y, float z)
221 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
222 FIXME("(%p)->(%f, %f, %f)\n", This, x, y, z);
223 return E_NOTIMPL;
226 static HRESULT WINAPI SAO_SetVolume(ISpatialAudioObject *iface, float vol)
228 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
229 FIXME("(%p)->(%f)\n", This, vol);
230 return E_NOTIMPL;
233 static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl = {
234 SAO_QueryInterface,
235 SAO_AddRef,
236 SAO_Release,
237 SAO_GetBuffer,
238 SAO_SetEndOfStream,
239 SAO_IsActive,
240 SAO_GetAudioObjectType,
241 SAO_SetPosition,
242 SAO_SetVolume,
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);
252 if (!ppv)
253 return E_POINTER;
255 *ppv = NULL;
257 if (IsEqualIID(riid, &IID_IUnknown) ||
258 IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStreamBase) ||
259 IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)) {
260 *ppv = &This->ISpatialAudioObjectRenderStream_iface;
262 else
263 return E_NOINTERFACE;
265 IUnknown_AddRef((IUnknown *)*ppv);
267 return S_OK;
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);
275 return 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);
283 if(!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);
295 heap_free(This);
297 return ref;
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);
306 *count = 0;
307 return S_OK;
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);
315 return E_NOTIMPL;
318 static HRESULT WINAPI SAORS_Start(ISpatialAudioObjectRenderStream *iface)
320 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
321 HRESULT hr;
323 TRACE("(%p)->()\n", This);
325 hr = IAudioClient_Start(This->client);
326 if(FAILED(hr)){
327 WARN("IAudioClient::Start failed: %08lx\n", hr);
328 return hr;
331 return S_OK;
334 static HRESULT WINAPI SAORS_Stop(ISpatialAudioObjectRenderStream *iface)
336 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
337 HRESULT hr;
339 TRACE("(%p)->()\n", This);
341 hr = IAudioClient_Stop(This->client);
342 if(FAILED(hr)){
343 WARN("IAudioClient::Stop failed: %08lx\n", hr);
344 return hr;
347 return S_OK;
350 static HRESULT WINAPI SAORS_Reset(ISpatialAudioObjectRenderStream *iface)
352 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
353 FIXME("(%p)->()\n", This);
354 return E_NOTIMPL;
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;
363 HRESULT hr;
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);
378 if(FAILED(hr)){
379 WARN("GetBuffer failed: %08lx\n", hr);
380 This->update_frames = ~0;
381 LeaveCriticalSection(&This->lock);
382 return hr;
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){
389 fixme_once = TRUE;
390 FIXME("Zero frame update.\n");
393 *dyn_count = 0;
394 *frames = This->update_frames;
396 LeaveCriticalSection(&This->lock);
398 return S_OK;
401 static void mix_static_object(SpatialAudioStreamImpl *stream, SpatialAudioObjectImpl *object)
403 float *in = object->buf, *out;
404 UINT32 i;
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);
408 return;
410 out = stream->buf + stream->static_object_map[object->static_idx];
411 for(i = 0; i < stream->update_frames; ++i){
412 *out += *in;
413 ++in;
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;
422 HRESULT hr;
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);
437 else
438 WARN("Don't know how to mix dynamic object yet. %p\n", object);
441 hr = IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
442 if(FAILED(hr))
443 WARN("ReleaseBuffer failed: %08lx\n", hr);
446 This->update_frames = ~0;
448 LeaveCriticalSection(&This->lock);
450 return S_OK;
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;
474 obj->ref = 1;
475 obj->type = type;
476 if(type == AudioObjectType_None){
477 FIXME("AudioObjectType_None not implemented yet!\n");
478 obj->static_idx = ~0;
479 }else{
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;
496 return S_OK;
499 static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl = {
500 SAORS_QueryInterface,
501 SAORS_AddRef,
502 SAORS_Release,
503 SAORS_GetAvailableDynamicObjectCount,
504 SAORS_GetService,
505 SAORS_Start,
506 SAORS_Stop,
507 SAORS_Reset,
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);
519 if (!ppv)
520 return E_POINTER;
522 *ppv = NULL;
524 if (IsEqualIID(riid, &IID_IUnknown) ||
525 IsEqualIID(riid, &IID_ISpatialAudioClient)) {
526 *ppv = &This->ISpatialAudioClient_iface;
528 else
529 return E_NOINTERFACE;
531 IUnknown_AddRef((IUnknown *)*ppv);
533 return S_OK;
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);
541 return 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);
549 if (!ref) {
550 IMMDevice_Release(This->mmdev);
551 heap_free(This);
553 return ref;
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);
561 return E_NOTIMPL;
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);
569 return E_NOTIMPL;
572 static HRESULT WINAPI SAC_GetMaxDynamicObjectCount(ISpatialAudioClient *iface,
573 UINT32 *value)
575 SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
576 FIXME("(%p)->(%p)\n", This, value);
578 *value = 0;
580 return S_OK;
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;
591 SAC_AddRef(iface);
593 return S_OK;
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);
608 return S_OK;
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);
616 return E_NOTIMPL;
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);
624 return E_NOTIMPL;
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);
631 return r;
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);
643 }else{
644 snprintf(buf, sizeof(buf), "tag: 0x%x, ch: %u, rate: %lu, depth: %u",
645 fmt->wFormatTag, fmt->nChannels, fmt->nSamplesPerSec,
646 fmt->wBitsPerSample);
648 return buf;
651 static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DWORD *mask, UINT32 *map)
653 UINT32 out_chan = 0, map_idx = 0;
654 *count = 0;
655 *mask = 0;
656 #define CONVERT_MASK(f, t) \
657 if(static_mask & f){ \
658 *count += 1; \
659 *mask |= t; \
660 map[map_idx++] = out_chan++; \
661 TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
662 }else{ \
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;
683 HRESULT hr;
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");
690 return E_INVALIDARG;
693 hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient,
694 CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client);
695 if(FAILED(hr)){
696 WARN("Activate failed: %08lx\n", hr);
697 return hr;
700 hr = IAudioClient_GetDevicePeriod(stream->client, &period, NULL);
701 if(FAILED(hr)){
702 WARN("GetDevicePeriod failed: %08lx\n", hr);
703 IAudioClient_Release(stream->client);
704 return hr;
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);
722 if(FAILED(hr)){
723 WARN("Initialize failed: %08lx\n", hr);
724 IAudioClient_Release(stream->client);
725 return hr;
728 hr = IAudioClient_SetEventHandle(stream->client, stream->params.EventHandle);
729 if(FAILED(hr)){
730 WARN("SetEventHandle failed: %08lx\n", hr);
731 IAudioClient_Release(stream->client);
732 return hr;
735 hr = IAudioClient_GetService(stream->client, &IID_IAudioRenderClient, (void**)&stream->render);
736 if(FAILED(hr)){
737 WARN("GetService(AudioRenderClient) failed: %08lx\n", hr);
738 IAudioClient_Release(stream->client);
739 return hr;
742 stream->period_frames = MulDiv(period, stream->stream_fmtex.Format.nSamplesPerSec, 10000000);
744 return S_OK;
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;
752 HRESULT hr;
754 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), stream);
756 if(IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)){
757 SpatialAudioStreamImpl *obj;
759 if(prop &&
760 (prop->vt != VT_BLOB ||
761 prop->blob.cbSize != sizeof(SpatialAudioObjectRenderStreamActivationParams))){
762 WARN("Got invalid params\n");
763 *stream = NULL;
764 return E_INVALIDARG;
767 params = (SpatialAudioObjectRenderStreamActivationParams*) prop->blob.pBlobData;
769 if(params->StaticObjectTypeMask & AudioObjectType_Dynamic){
770 *stream = NULL;
771 return E_INVALIDARG;
774 if(params->EventHandle == INVALID_HANDLE_VALUE ||
775 params->EventHandle == 0){
776 *stream = NULL;
777 return E_INVALIDARG;
780 if(!params->ObjectFormat ||
781 memcmp(params->ObjectFormat, &This->object_fmtex.Format, sizeof(*params->ObjectFormat) + params->ObjectFormat->cbSize)){
782 *stream = NULL;
783 return AUDCLNT_E_UNSUPPORTED_FORMAT;
786 obj = heap_alloc_zero(sizeof(SpatialAudioStreamImpl));
788 obj->ISpatialAudioObjectRenderStream_iface.lpVtbl = &ISpatialAudioObjectRenderStream_vtbl;
789 obj->ref = 1;
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);
820 if(FAILED(hr)){
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);
827 heap_free(obj);
828 *stream = NULL;
829 return hr;
832 *stream = &obj->ISpatialAudioObjectRenderStream_iface;
833 }else{
834 FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid));
835 *stream = NULL;
836 return E_NOTIMPL;
839 return S_OK;
842 static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl = {
843 SAC_QueryInterface,
844 SAC_AddRef,
845 SAC_Release,
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);
881 *count = 1;
883 return S_OK;
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);
893 if(index > 0)
894 return E_INVALIDARG;
896 *format = &This->object_fmtex.Format;
898 return S_OK;
901 static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl = {
902 SAOFE_QueryInterface,
903 SAOFE_AddRef,
904 SAOFE_Release,
905 SAOFE_GetCount,
906 SAOFE_GetFormat,
909 HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out)
911 SpatialAudioImpl *obj;
912 IAudioClient *aclient;
913 WAVEFORMATEX *closest;
914 HRESULT hr;
916 obj = heap_alloc_zero(sizeof(*obj));
918 obj->ref = 1;
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);
932 if(FAILED(hr)){
933 WARN("Activate failed: %08lx\n", hr);
934 heap_free(obj);
935 return hr;
938 hr = IAudioClient_IsFormatSupported(aclient, AUDCLNT_SHAREMODE_SHARED, &obj->object_fmtex.Format, &closest);
940 IAudioClient_Release(aclient);
942 if(hr == S_FALSE){
943 if(sizeof(WAVEFORMATEX) + closest->cbSize > sizeof(obj->object_fmtex)){
944 ERR("Returned format too large: %s\n", debugstr_fmtex(closest));
945 CoTaskMemFree(closest);
946 heap_free(obj);
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);
955 heap_free(obj);
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,
960 closest,
961 sizeof(WAVEFORMATEX) + closest->cbSize);
962 CoTaskMemFree(closest);
963 } else if(hr != S_OK){
964 WARN("Checking supported formats failed: %08lx\n", hr);
965 heap_free(obj);
966 return hr;
969 obj->mmdev = mmdev;
970 IMMDevice_AddRef(mmdev);
972 *out = &obj->ISpatialAudioClient_iface;
974 return S_OK;