gdi32: Improve EMF DC cleanup when CloseEnhMetafile is not called.
[wine.git] / dlls / mmdevapi / spatialaudio.c
blob8feb8e66ed2bd642a7920b27a7fb40bf0c3449b4
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
20 #define NONAMELESSUNION
22 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winnls.h"
27 #include "winreg.h"
28 #include "wine/heap.h"
29 #include "wine/debug.h"
30 #include "wine/list.h"
32 #include "ole2.h"
33 #include "mmdeviceapi.h"
34 #include "mmsystem.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)
46 UINT32 o = 0;
47 while(type){
48 type >>= 1;
49 ++o;
51 return o - 2;
54 typedef struct SpatialAudioImpl SpatialAudioImpl;
55 typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl;
56 typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl;
58 struct SpatialAudioObjectImpl {
59 ISpatialAudioObject ISpatialAudioObject_iface;
60 LONG ref;
62 SpatialAudioStreamImpl *sa_stream;
63 AudioObjectType type;
64 UINT32 static_idx;
66 float *buf;
68 struct list entry;
71 struct SpatialAudioStreamImpl {
72 ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface;
73 LONG ref;
74 CRITICAL_SECTION lock;
76 SpatialAudioImpl *sa_client;
77 SpatialAudioObjectRenderStreamActivationParams params;
79 IAudioClient *client;
80 IAudioRenderClient *render;
82 UINT32 period_frames, update_frames;
83 WAVEFORMATEXTENSIBLE stream_fmtex;
85 float *buf;
87 UINT32 static_object_map[17];
89 struct list objects;
92 struct SpatialAudioImpl {
93 ISpatialAudioClient ISpatialAudioClient_iface;
94 IAudioFormatEnumerator IAudioFormatEnumerator_iface;
95 IMMDevice *mmdev;
96 LONG ref;
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);
127 if (!ppv)
128 return E_POINTER;
130 *ppv = NULL;
132 if (IsEqualIID(riid, &IID_IUnknown) ||
133 IsEqualIID(riid, &IID_ISpatialAudioObjectBase) ||
134 IsEqualIID(riid, &IID_ISpatialAudioObject)) {
135 *ppv = &This->ISpatialAudioObject_iface;
137 else
138 return E_NOINTERFACE;
140 IUnknown_AddRef((IUnknown *)*ppv);
142 return S_OK;
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);
150 return 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);
158 if(!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);
165 heap_free(This);
167 return ref;
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);
190 return S_OK;
193 static HRESULT WINAPI SAO_SetEndOfStream(ISpatialAudioObject *iface, UINT32 frames)
195 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
196 FIXME("(%p)->(%u)\n", This, frames);
197 return E_NOTIMPL;
200 static HRESULT WINAPI SAO_IsActive(ISpatialAudioObject *iface, BOOL *active)
202 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
203 FIXME("(%p)->(%p)\n", This, active);
204 return E_NOTIMPL;
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);
214 *type = This->type;
216 return S_OK;
219 static HRESULT WINAPI SAO_SetPosition(ISpatialAudioObject *iface, float x,
220 float y, float z)
222 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
223 FIXME("(%p)->(%f, %f, %f)\n", This, x, y, z);
224 return E_NOTIMPL;
227 static HRESULT WINAPI SAO_SetVolume(ISpatialAudioObject *iface, float vol)
229 SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
230 FIXME("(%p)->(%f)\n", This, vol);
231 return E_NOTIMPL;
234 static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl = {
235 SAO_QueryInterface,
236 SAO_AddRef,
237 SAO_Release,
238 SAO_GetBuffer,
239 SAO_SetEndOfStream,
240 SAO_IsActive,
241 SAO_GetAudioObjectType,
242 SAO_SetPosition,
243 SAO_SetVolume,
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);
253 if (!ppv)
254 return E_POINTER;
256 *ppv = NULL;
258 if (IsEqualIID(riid, &IID_IUnknown) ||
259 IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStreamBase) ||
260 IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)) {
261 *ppv = &This->ISpatialAudioObjectRenderStream_iface;
263 else
264 return E_NOINTERFACE;
266 IUnknown_AddRef((IUnknown *)*ppv);
268 return S_OK;
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);
276 return 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);
284 if(!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);
296 heap_free(This);
298 return ref;
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);
307 *count = 0;
308 return S_OK;
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);
316 return E_NOTIMPL;
319 static HRESULT WINAPI SAORS_Start(ISpatialAudioObjectRenderStream *iface)
321 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
322 HRESULT hr;
324 TRACE("(%p)->()\n", This);
326 hr = IAudioClient_Start(This->client);
327 if(FAILED(hr)){
328 WARN("IAudioClient::Start failed: %08lx\n", hr);
329 return hr;
332 return S_OK;
335 static HRESULT WINAPI SAORS_Stop(ISpatialAudioObjectRenderStream *iface)
337 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
338 HRESULT hr;
340 TRACE("(%p)->()\n", This);
342 hr = IAudioClient_Stop(This->client);
343 if(FAILED(hr)){
344 WARN("IAudioClient::Stop failed: %08lx\n", hr);
345 return hr;
348 return S_OK;
351 static HRESULT WINAPI SAORS_Reset(ISpatialAudioObjectRenderStream *iface)
353 SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
354 FIXME("(%p)->()\n", This);
355 return E_NOTIMPL;
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;
364 HRESULT hr;
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);
379 if(FAILED(hr)){
380 WARN("GetBuffer failed: %08lx\n", hr);
381 This->update_frames = ~0;
382 LeaveCriticalSection(&This->lock);
383 return hr;
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){
390 fixme_once = TRUE;
391 FIXME("Zero frame update.\n");
394 *dyn_count = 0;
395 *frames = This->update_frames;
397 LeaveCriticalSection(&This->lock);
399 return S_OK;
402 static void mix_static_object(SpatialAudioStreamImpl *stream, SpatialAudioObjectImpl *object)
404 float *in = object->buf, *out;
405 UINT32 i;
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);
409 return;
411 out = stream->buf + stream->static_object_map[object->static_idx];
412 for(i = 0; i < stream->update_frames; ++i){
413 *out += *in;
414 ++in;
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;
423 HRESULT hr;
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);
438 else
439 WARN("Don't know how to mix dynamic object yet. %p\n", object);
442 hr = IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
443 if(FAILED(hr))
444 WARN("ReleaseBuffer failed: %08lx\n", hr);
447 This->update_frames = ~0;
449 LeaveCriticalSection(&This->lock);
451 return S_OK;
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;
475 obj->ref = 1;
476 obj->type = type;
477 if(type == AudioObjectType_None){
478 FIXME("AudioObjectType_None not implemented yet!\n");
479 obj->static_idx = ~0;
480 }else{
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;
497 return S_OK;
500 static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl = {
501 SAORS_QueryInterface,
502 SAORS_AddRef,
503 SAORS_Release,
504 SAORS_GetAvailableDynamicObjectCount,
505 SAORS_GetService,
506 SAORS_Start,
507 SAORS_Stop,
508 SAORS_Reset,
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);
520 if (!ppv)
521 return E_POINTER;
523 *ppv = NULL;
525 if (IsEqualIID(riid, &IID_IUnknown) ||
526 IsEqualIID(riid, &IID_ISpatialAudioClient)) {
527 *ppv = &This->ISpatialAudioClient_iface;
529 else
530 return E_NOINTERFACE;
532 IUnknown_AddRef((IUnknown *)*ppv);
534 return S_OK;
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);
542 return 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);
550 if (!ref) {
551 IMMDevice_Release(This->mmdev);
552 heap_free(This);
554 return ref;
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);
562 return E_NOTIMPL;
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);
570 return E_NOTIMPL;
573 static HRESULT WINAPI SAC_GetMaxDynamicObjectCount(ISpatialAudioClient *iface,
574 UINT32 *value)
576 SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
577 FIXME("(%p)->(%p)\n", This, value);
579 *value = 0;
581 return S_OK;
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;
592 SAC_AddRef(iface);
594 return S_OK;
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);
609 return S_OK;
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);
617 return E_NOTIMPL;
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);
625 return E_NOTIMPL;
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);
632 return r;
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);
644 }else{
645 snprintf(buf, sizeof(buf), "tag: 0x%x, ch: %u, rate: %lu, depth: %u",
646 fmt->wFormatTag, fmt->nChannels, fmt->nSamplesPerSec,
647 fmt->wBitsPerSample);
649 return buf;
652 static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DWORD *mask, UINT32 *map)
654 UINT32 out_chan = 0, map_idx = 0;
655 *count = 0;
656 *mask = 0;
657 #define CONVERT_MASK(f, t) \
658 if(static_mask & f){ \
659 *count += 1; \
660 *mask |= t; \
661 map[map_idx++] = out_chan++; \
662 TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
663 }else{ \
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;
684 HRESULT hr;
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");
691 return E_INVALIDARG;
694 hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient,
695 CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client);
696 if(FAILED(hr)){
697 WARN("Activate failed: %08lx\n", hr);
698 return hr;
701 hr = IAudioClient_GetDevicePeriod(stream->client, &period, NULL);
702 if(FAILED(hr)){
703 WARN("GetDevicePeriod failed: %08lx\n", hr);
704 IAudioClient_Release(stream->client);
705 return hr;
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);
723 if(FAILED(hr)){
724 WARN("Initialize failed: %08lx\n", hr);
725 IAudioClient_Release(stream->client);
726 return hr;
729 hr = IAudioClient_SetEventHandle(stream->client, stream->params.EventHandle);
730 if(FAILED(hr)){
731 WARN("SetEventHandle failed: %08lx\n", hr);
732 IAudioClient_Release(stream->client);
733 return hr;
736 hr = IAudioClient_GetService(stream->client, &IID_IAudioRenderClient, (void**)&stream->render);
737 if(FAILED(hr)){
738 WARN("GetService(AudioRenderClient) failed: %08lx\n", hr);
739 IAudioClient_Release(stream->client);
740 return hr;
743 stream->period_frames = MulDiv(period, stream->stream_fmtex.Format.nSamplesPerSec, 10000000);
745 return S_OK;
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;
753 HRESULT hr;
755 TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), stream);
757 if(IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)){
758 SpatialAudioStreamImpl *obj;
760 if(prop &&
761 (prop->vt != VT_BLOB ||
762 prop->blob.cbSize != sizeof(SpatialAudioObjectRenderStreamActivationParams))){
763 WARN("Got invalid params\n");
764 *stream = NULL;
765 return E_INVALIDARG;
768 params = (SpatialAudioObjectRenderStreamActivationParams*) prop->blob.pBlobData;
770 if(params->StaticObjectTypeMask & AudioObjectType_Dynamic){
771 *stream = NULL;
772 return E_INVALIDARG;
775 if(params->EventHandle == INVALID_HANDLE_VALUE ||
776 params->EventHandle == 0){
777 *stream = NULL;
778 return E_INVALIDARG;
781 if(!params->ObjectFormat ||
782 memcmp(params->ObjectFormat, &This->object_fmtex.Format, sizeof(*params->ObjectFormat) + params->ObjectFormat->cbSize)){
783 *stream = NULL;
784 return AUDCLNT_E_UNSUPPORTED_FORMAT;
787 obj = heap_alloc_zero(sizeof(SpatialAudioStreamImpl));
789 obj->ISpatialAudioObjectRenderStream_iface.lpVtbl = &ISpatialAudioObjectRenderStream_vtbl;
790 obj->ref = 1;
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);
821 if(FAILED(hr)){
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);
828 heap_free(obj);
829 *stream = NULL;
830 return hr;
833 *stream = &obj->ISpatialAudioObjectRenderStream_iface;
834 }else{
835 FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid));
836 *stream = NULL;
837 return E_NOTIMPL;
840 return S_OK;
843 static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl = {
844 SAC_QueryInterface,
845 SAC_AddRef,
846 SAC_Release,
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);
882 *count = 1;
884 return S_OK;
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);
894 if(index > 0)
895 return E_INVALIDARG;
897 *format = &This->object_fmtex.Format;
899 return S_OK;
902 static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl = {
903 SAOFE_QueryInterface,
904 SAOFE_AddRef,
905 SAOFE_Release,
906 SAOFE_GetCount,
907 SAOFE_GetFormat,
910 HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out)
912 SpatialAudioImpl *obj;
913 IAudioClient *aclient;
914 WAVEFORMATEX *closest;
915 HRESULT hr;
917 obj = heap_alloc_zero(sizeof(*obj));
919 obj->ref = 1;
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);
933 if(FAILED(hr)){
934 WARN("Activate failed: %08lx\n", hr);
935 heap_free(obj);
936 return hr;
939 hr = IAudioClient_IsFormatSupported(aclient, AUDCLNT_SHAREMODE_SHARED, &obj->object_fmtex.Format, &closest);
941 IAudioClient_Release(aclient);
943 if(hr == S_FALSE){
944 if(sizeof(WAVEFORMATEX) + closest->cbSize > sizeof(obj->object_fmtex)){
945 ERR("Returned format too large: %s\n", debugstr_fmtex(closest));
946 CoTaskMemFree(closest);
947 heap_free(obj);
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);
956 heap_free(obj);
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,
961 closest,
962 sizeof(WAVEFORMATEX) + closest->cbSize);
963 CoTaskMemFree(closest);
964 } else if(hr != S_OK){
965 WARN("Checking supported formats failed: %08lx\n", hr);
966 heap_free(obj);
967 return hr;
970 obj->mmdev = mmdev;
971 IMMDevice_AddRef(mmdev);
973 *out = &obj->ISpatialAudioClient_iface;
975 return S_OK;