[PATCH v4 1/2] mmdevapi: Implement SpatialAudio features.

Andrew Eikum aeikum at codeweavers.com
Thu Feb 4 08:18:14 CST 2021


Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>

On Thu, Feb 04, 2021 at 04:09:56PM +0200, Arkadiusz Hiler wrote:
> From: Andrew Eikum <aeikum at codeweavers.com>
> 
> This makes car radio / ambient noises audible in Cyberpunk 2077.
> 
> Signed-off-by: Arkadiusz Hiler <ahiler at codeweavers.com>
> ---
>  dlls/mmdevapi/Makefile.in      |   3 +-
>  dlls/mmdevapi/audiovolume.c    |   1 +
>  dlls/mmdevapi/devenum.c        |   5 +
>  dlls/mmdevapi/main.c           |   1 +
>  dlls/mmdevapi/mmdevapi.h       |   1 +
>  dlls/mmdevapi/spatialaudio.c   | 988 +++++++++++++++++++++++++++++++++
>  include/spatialaudioclient.idl | 121 ++++
>  7 files changed, 1119 insertions(+), 1 deletion(-)
>  create mode 100644 dlls/mmdevapi/spatialaudio.c
> 
> diff --git a/dlls/mmdevapi/Makefile.in b/dlls/mmdevapi/Makefile.in
> index 5f44f7ba53b..903b14335a9 100644
> --- a/dlls/mmdevapi/Makefile.in
> +++ b/dlls/mmdevapi/Makefile.in
> @@ -6,6 +6,7 @@ EXTRADLLFLAGS = -mno-cygwin
>  C_SRCS = \
>  	audiovolume.c \
>  	devenum.c \
> -	main.c
> +	main.c \
> +	spatialaudio.c
>  
>  IDL_SRCS = mmdevapi_classes.idl
> diff --git a/dlls/mmdevapi/audiovolume.c b/dlls/mmdevapi/audiovolume.c
> index 9214980120e..6f403cf348a 100644
> --- a/dlls/mmdevapi/audiovolume.c
> +++ b/dlls/mmdevapi/audiovolume.c
> @@ -33,6 +33,7 @@
>  #include "audioclient.h"
>  #include "endpointvolume.h"
>  #include "audiopolicy.h"
> +#include "spatialaudioclient.h"
>  
>  #include "mmdevapi.h"
>  
> diff --git a/dlls/mmdevapi/devenum.c b/dlls/mmdevapi/devenum.c
> index 9e4a29816ce..07b4dca028b 100644
> --- a/dlls/mmdevapi/devenum.c
> +++ b/dlls/mmdevapi/devenum.c
> @@ -35,6 +35,7 @@
>  #include "audioclient.h"
>  #include "endpointvolume.h"
>  #include "audiopolicy.h"
> +#include "spatialaudioclient.h"
>  
>  #include "mmdevapi.h"
>  #include "devpkey.h"
> @@ -635,6 +636,10 @@ static HRESULT WINAPI MMDevice_Activate(IMMDevice *iface, REFIID riid, DWORD cls
>                  IDirectSoundCapture_Release((IDirectSoundCapture*)*ppv);
>          }
>      }
> +    else if (IsEqualIID(riid, &IID_ISpatialAudioClient))
> +    {
> +        hr = SpatialAudioClient_Create(iface, (ISpatialAudioClient**)ppv);
> +    }
>      else
>          ERR("Invalid/unknown iid %s\n", debugstr_guid(riid));
>  
> diff --git a/dlls/mmdevapi/main.c b/dlls/mmdevapi/main.c
> index 247ebc3b001..eac1da28f18 100644
> --- a/dlls/mmdevapi/main.c
> +++ b/dlls/mmdevapi/main.c
> @@ -37,6 +37,7 @@
>  #include "audiopolicy.h"
>  #include "devpkey.h"
>  #include "winreg.h"
> +#include "spatialaudioclient.h"
>  
>  #include "mmdevapi.h"
>  #include "wine/debug.h"
> diff --git a/dlls/mmdevapi/mmdevapi.h b/dlls/mmdevapi/mmdevapi.h
> index bc9788e95c8..3bcf568cddf 100644
> --- a/dlls/mmdevapi/mmdevapi.h
> +++ b/dlls/mmdevapi/mmdevapi.h
> @@ -71,5 +71,6 @@ typedef struct MMDevice {
>  
>  extern HRESULT AudioClient_Create(MMDevice *parent, IAudioClient **ppv) DECLSPEC_HIDDEN;
>  extern HRESULT AudioEndpointVolume_Create(MMDevice *parent, IAudioEndpointVolumeEx **ppv) DECLSPEC_HIDDEN;
> +extern HRESULT SpatialAudioClient_Create(IMMDevice *device, ISpatialAudioClient **out) DECLSPEC_HIDDEN;
>  
>  extern const WCHAR drv_keyW[] DECLSPEC_HIDDEN;
> diff --git a/dlls/mmdevapi/spatialaudio.c b/dlls/mmdevapi/spatialaudio.c
> new file mode 100644
> index 00000000000..cbca57b4890
> --- /dev/null
> +++ b/dlls/mmdevapi/spatialaudio.c
> @@ -0,0 +1,988 @@
> +/*
> + * Copyright 2020 Andrew Eikum for CodeWeavers
> + *
> + * This library is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * This library is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with this library; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
> + */
> +
> +#define COBJMACROS
> +#define NONAMELESSUNION
> +
> +#include <stdarg.h>
> +
> +#include "windef.h"
> +#include "winbase.h"
> +#include "winnls.h"
> +#include "winreg.h"
> +#include "wine/heap.h"
> +#include "wine/debug.h"
> +#include "wine/list.h"
> +
> +#include "ole2.h"
> +#include "mmdeviceapi.h"
> +#include "mmsystem.h"
> +#include "audioclient.h"
> +#include "endpointvolume.h"
> +#include "audiopolicy.h"
> +#include "spatialaudioclient.h"
> +
> +#include "mmdevapi.h"
> +
> +WINE_DEFAULT_DEBUG_CHANNEL(mmdevapi);
> +
> +#define MAX_PERIODS 3
> +
> +static UINT32 AudioObjectType_to_index(AudioObjectType type)
> +{
> +    UINT32 o = 0;
> +    while(type){
> +        type >>= 1;
> +        ++o;
> +    }
> +    return o - 2;
> +}
> +
> +typedef struct SpatialAudioImpl SpatialAudioImpl;
> +typedef struct SpatialAudioStreamImpl SpatialAudioStreamImpl;
> +typedef struct SpatialAudioObjectImpl SpatialAudioObjectImpl;
> +
> +struct SpatialAudioObjectImpl {
> +    ISpatialAudioObject ISpatialAudioObject_iface;
> +    LONG ref;
> +
> +    SpatialAudioStreamImpl *sa_stream;
> +    AudioObjectType type;
> +    UINT32 static_idx;
> +
> +    float *buf;
> +
> +    struct list entry;
> +};
> +
> +struct SpatialAudioStreamImpl {
> +    ISpatialAudioObjectRenderStream ISpatialAudioObjectRenderStream_iface;
> +    LONG ref;
> +    CRITICAL_SECTION lock;
> +
> +    SpatialAudioImpl *sa_client;
> +    SpatialAudioObjectRenderStreamActivationParams params;
> +
> +    IAudioClient *client;
> +    IAudioRenderClient *render;
> +
> +    UINT32 period_frames, update_frames;
> +    WAVEFORMATEXTENSIBLE stream_fmtex;
> +
> +    float *buf;
> +
> +    UINT32 static_object_map[17];
> +
> +    struct list objects;
> +};
> +
> +struct SpatialAudioImpl {
> +    ISpatialAudioClient ISpatialAudioClient_iface;
> +    IAudioFormatEnumerator IAudioFormatEnumerator_iface;
> +    IMMDevice *mmdev;
> +    LONG ref;
> +    WAVEFORMATEXTENSIBLE object_fmtex;
> +};
> +
> +static inline SpatialAudioObjectImpl *impl_from_ISpatialAudioObject(ISpatialAudioObject *iface)
> +{
> +    return CONTAINING_RECORD(iface, SpatialAudioObjectImpl, ISpatialAudioObject_iface);
> +}
> +
> +static inline SpatialAudioStreamImpl *impl_from_ISpatialAudioObjectRenderStream(ISpatialAudioObjectRenderStream *iface)
> +{
> +    return CONTAINING_RECORD(iface, SpatialAudioStreamImpl, ISpatialAudioObjectRenderStream_iface);
> +}
> +
> +static inline SpatialAudioImpl *impl_from_ISpatialAudioClient(ISpatialAudioClient *iface)
> +{
> +    return CONTAINING_RECORD(iface, SpatialAudioImpl, ISpatialAudioClient_iface);
> +}
> +
> +static inline SpatialAudioImpl *impl_from_IAudioFormatEnumerator(IAudioFormatEnumerator *iface)
> +{
> +    return CONTAINING_RECORD(iface, SpatialAudioImpl, IAudioFormatEnumerator_iface);
> +}
> +
> +static HRESULT WINAPI SAO_QueryInterface(ISpatialAudioObject *iface,
> +        REFIID riid, void **ppv)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +
> +    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
> +
> +    if (!ppv)
> +        return E_POINTER;
> +
> +    *ppv = NULL;
> +
> +    if (IsEqualIID(riid, &IID_IUnknown) ||
> +            IsEqualIID(riid, &IID_ISpatialAudioObjectBase) ||
> +            IsEqualIID(riid, &IID_ISpatialAudioObject)) {
> +        *ppv = &This->ISpatialAudioObject_iface;
> +    }
> +    else
> +        return E_NOINTERFACE;
> +
> +    IUnknown_AddRef((IUnknown *)*ppv);
> +
> +    return S_OK;
> +}
> +
> +static ULONG WINAPI SAO_AddRef(ISpatialAudioObject *iface)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    ULONG ref = InterlockedIncrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    return ref;
> +}
> +
> +static ULONG WINAPI SAO_Release(ISpatialAudioObject *iface)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    ULONG ref = InterlockedDecrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    if(!ref){
> +        EnterCriticalSection(&This->sa_stream->lock);
> +        list_remove(&This->entry);
> +        LeaveCriticalSection(&This->sa_stream->lock);
> +
> +        ISpatialAudioObjectRenderStream_Release(&This->sa_stream->ISpatialAudioObjectRenderStream_iface);
> +        heap_free(This->buf);
> +        heap_free(This);
> +    }
> +    return ref;
> +}
> +
> +static HRESULT WINAPI SAO_GetBuffer(ISpatialAudioObject *iface,
> +        BYTE **buffer, UINT32 *bytes)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +
> +    TRACE("(%p)->(%p, %p)\n", This, buffer, bytes);
> +
> +    EnterCriticalSection(&This->sa_stream->lock);
> +
> +    if(This->sa_stream->update_frames == ~0){
> +        LeaveCriticalSection(&This->sa_stream->lock);
> +        return SPTLAUDCLNT_E_OUT_OF_ORDER;
> +    }
> +
> +    *buffer = (BYTE *)This->buf;
> +    *bytes = This->sa_stream->update_frames *
> +        This->sa_stream->sa_client->object_fmtex.Format.nBlockAlign;
> +
> +    LeaveCriticalSection(&This->sa_stream->lock);
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAO_SetEndOfStream(ISpatialAudioObject *iface, UINT32 frames)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    FIXME("(%p)->(%u)\n", This, frames);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAO_IsActive(ISpatialAudioObject *iface, BOOL *active)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    FIXME("(%p)->(%p)\n", This, active);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAO_GetAudioObjectType(ISpatialAudioObject *iface,
> +        AudioObjectType *type)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +
> +    TRACE("(%p)->(%p)\n", This, type);
> +
> +    *type = This->type;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAO_SetPosition(ISpatialAudioObject *iface, float x,
> +        float y, float z)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    FIXME("(%p)->(%f, %f, %f)\n", This, x, y, z);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAO_SetVolume(ISpatialAudioObject *iface, float vol)
> +{
> +    SpatialAudioObjectImpl *This = impl_from_ISpatialAudioObject(iface);
> +    FIXME("(%p)->(%f)\n", This, vol);
> +    return E_NOTIMPL;
> +}
> +
> +static ISpatialAudioObjectVtbl ISpatialAudioObject_vtbl = {
> +    SAO_QueryInterface,
> +    SAO_AddRef,
> +    SAO_Release,
> +    SAO_GetBuffer,
> +    SAO_SetEndOfStream,
> +    SAO_IsActive,
> +    SAO_GetAudioObjectType,
> +    SAO_SetPosition,
> +    SAO_SetVolume,
> +};
> +
> +static HRESULT WINAPI SAORS_QueryInterface(ISpatialAudioObjectRenderStream *iface,
> +        REFIID riid, void **ppv)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +
> +    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
> +
> +    if (!ppv)
> +        return E_POINTER;
> +
> +    *ppv = NULL;
> +
> +    if (IsEqualIID(riid, &IID_IUnknown) ||
> +            IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStreamBase) ||
> +            IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)) {
> +        *ppv = &This->ISpatialAudioObjectRenderStream_iface;
> +    }
> +    else
> +        return E_NOINTERFACE;
> +
> +    IUnknown_AddRef((IUnknown *)*ppv);
> +
> +    return S_OK;
> +}
> +
> +static ULONG WINAPI SAORS_AddRef(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    ULONG ref = InterlockedIncrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    return ref;
> +}
> +
> +static ULONG WINAPI SAORS_Release(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    ULONG ref = InterlockedDecrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    if(!ref){
> +        IAudioClient_Stop(This->client);
> +        if(This->update_frames != ~0 && This->update_frames > 0)
> +            IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
> +        IAudioRenderClient_Release(This->render);
> +        IAudioClient_Release(This->client);
> +        if(This->params.NotifyObject)
> +            ISpatialAudioObjectRenderStreamNotify_Release(This->params.NotifyObject);
> +        heap_free((void*)This->params.ObjectFormat);
> +        CloseHandle(This->params.EventHandle);
> +        DeleteCriticalSection(&This->lock);
> +        ISpatialAudioClient_Release(&This->sa_client->ISpatialAudioClient_iface);
> +        heap_free(This);
> +    }
> +    return ref;
> +}
> +
> +static HRESULT WINAPI SAORS_GetAvailableDynamicObjectCount(
> +        ISpatialAudioObjectRenderStream *iface, UINT32 *count)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    FIXME("(%p)->(%p)\n", This, count);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAORS_GetService(ISpatialAudioObjectRenderStream *iface,
> +        REFIID riid, void **service)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    FIXME("(%p)->(%s, %p)\n", This, debugstr_guid(riid), service);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAORS_Start(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    HRESULT hr;
> +
> +    TRACE("(%p)->()\n", This);
> +
> +    hr = IAudioClient_Start(This->client);
> +    if(FAILED(hr)){
> +        WARN("IAudioClient::Start failed: %08x\n", hr);
> +        return hr;
> +    }
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAORS_Stop(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    HRESULT hr;
> +
> +    TRACE("(%p)->()\n", This);
> +
> +    hr = IAudioClient_Stop(This->client);
> +    if(FAILED(hr)){
> +        WARN("IAudioClient::Stop failed: %08x\n", hr);
> +        return hr;
> +    }
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAORS_Reset(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    FIXME("(%p)->()\n", This);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAORS_BeginUpdatingAudioObjects(ISpatialAudioObjectRenderStream *iface,
> +        UINT32 *dyn_count, UINT32 *frames)
> +{
> +    static BOOL fixme_once = FALSE;
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    SpatialAudioObjectImpl *object;
> +    UINT32 pad;
> +    HRESULT hr;
> +
> +    TRACE("(%p)->(%p, %p)\n", This, dyn_count, frames);
> +
> +    EnterCriticalSection(&This->lock);
> +
> +    if(This->update_frames != ~0){
> +        LeaveCriticalSection(&This->lock);
> +        return SPTLAUDCLNT_E_OUT_OF_ORDER;
> +    }
> +
> +    hr = IAudioClient_GetCurrentPadding(This->client, &pad);
> +    if(FAILED(hr)){
> +        WARN("GetCurrentPadding failed: %08x\n", hr);
> +        LeaveCriticalSection(&This->lock);
> +        return hr;
> +    }
> +
> +    if(pad < This->period_frames * MAX_PERIODS){
> +        This->update_frames = This->period_frames * MAX_PERIODS - pad;
> +    }else{
> +        This->update_frames = 0;
> +    }
> +
> +    if(This->update_frames > 0){
> +        hr = IAudioRenderClient_GetBuffer(This->render, This->update_frames, (BYTE **)&This->buf);
> +        if(FAILED(hr)){
> +            WARN("GetBuffer failed: %08x\n", hr);
> +            This->update_frames = ~0;
> +            LeaveCriticalSection(&This->lock);
> +            return hr;
> +        }
> +
> +        LIST_FOR_EACH_ENTRY(object, &This->objects, SpatialAudioObjectImpl, entry){
> +            memset(object->buf, 0, This->update_frames * This->sa_client->object_fmtex.Format.nBlockAlign);
> +        }
> +    }else if (!fixme_once){
> +        fixme_once = TRUE;
> +        FIXME("Zero frame update.\n");
> +    }
> +
> +    *dyn_count = 0;
> +    *frames = This->update_frames;
> +
> +    LeaveCriticalSection(&This->lock);
> +
> +    return S_OK;
> +}
> +
> +static void mix_static_object(SpatialAudioStreamImpl *stream, SpatialAudioObjectImpl *object)
> +{
> +    float *in = object->buf, *out;
> +    UINT32 i;
> +    if(object->static_idx == ~0 ||
> +            stream->static_object_map[object->static_idx] == ~0){
> +        WARN("Got unmapped static object?! Not mixing. Type: 0x%x\n", object->type);
> +        return;
> +    }
> +    out = stream->buf + stream->static_object_map[object->static_idx];
> +    for(i = 0; i < stream->update_frames; ++i){
> +        *out += *in;
> +        ++in;
> +        out += stream->stream_fmtex.Format.nChannels;
> +    }
> +}
> +
> +static HRESULT WINAPI SAORS_EndUpdatingAudioObjects(ISpatialAudioObjectRenderStream *iface)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    SpatialAudioObjectImpl *object;
> +    HRESULT hr;
> +
> +    TRACE("(%p)->()\n", This);
> +
> +    EnterCriticalSection(&This->lock);
> +
> +    if(This->update_frames == ~0){
> +        LeaveCriticalSection(&This->lock);
> +        return SPTLAUDCLNT_E_OUT_OF_ORDER;
> +    }
> +
> +    if(This->update_frames > 0){
> +        LIST_FOR_EACH_ENTRY(object, &This->objects, SpatialAudioObjectImpl, entry){
> +            if(object->type != AudioObjectType_Dynamic)
> +                mix_static_object(This, object);
> +            else
> +                WARN("Don't know how to mix dynamic object yet. %p\n", object);
> +        }
> +
> +        hr = IAudioRenderClient_ReleaseBuffer(This->render, This->update_frames, 0);
> +        if(FAILED(hr))
> +            WARN("ReleaseBuffer failed: %08x\n", hr);
> +    }
> +
> +    This->update_frames = ~0;
> +
> +    LeaveCriticalSection(&This->lock);
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAORS_ActivateSpatialAudioObject(ISpatialAudioObjectRenderStream *iface,
> +        AudioObjectType type, ISpatialAudioObject **object)
> +{
> +    SpatialAudioStreamImpl *This = impl_from_ISpatialAudioObjectRenderStream(iface);
> +    SpatialAudioObjectImpl *obj;
> +
> +    TRACE("(%p)->(0x%x, %p)\n", This, type, object);
> +
> +    if(type == AudioObjectType_Dynamic)
> +        return SPTLAUDCLNT_E_NO_MORE_OBJECTS;
> +
> +    if(type & ~This->params.StaticObjectTypeMask)
> +        return SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE;
> +
> +    LIST_FOR_EACH_ENTRY(obj, &This->objects, SpatialAudioObjectImpl, entry){
> +        if(obj->static_idx == AudioObjectType_to_index(type))
> +            return SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE;
> +    }
> +
> +    obj = heap_alloc_zero(sizeof(*obj));
> +    obj->ISpatialAudioObject_iface.lpVtbl = &ISpatialAudioObject_vtbl;
> +    obj->ref = 1;
> +    obj->type = type;
> +    if(type == AudioObjectType_None){
> +        FIXME("AudioObjectType_None not implemented yet!\n");
> +        obj->static_idx = ~0;
> +    }else{
> +        obj->static_idx = AudioObjectType_to_index(type);
> +    }
> +
> +    obj->sa_stream = This;
> +    SAORS_AddRef(&This->ISpatialAudioObjectRenderStream_iface);
> +
> +    obj->buf = heap_alloc_zero(This->period_frames * MAX_PERIODS * This->sa_client->object_fmtex.Format.nBlockAlign);
> +
> +    EnterCriticalSection(&This->lock);
> +
> +    list_add_tail(&This->objects, &obj->entry);
> +
> +    LeaveCriticalSection(&This->lock);
> +
> +    *object = &obj->ISpatialAudioObject_iface;
> +
> +    return S_OK;
> +}
> +
> +static ISpatialAudioObjectRenderStreamVtbl ISpatialAudioObjectRenderStream_vtbl = {
> +    SAORS_QueryInterface,
> +    SAORS_AddRef,
> +    SAORS_Release,
> +    SAORS_GetAvailableDynamicObjectCount,
> +    SAORS_GetService,
> +    SAORS_Start,
> +    SAORS_Stop,
> +    SAORS_Reset,
> +    SAORS_BeginUpdatingAudioObjects,
> +    SAORS_EndUpdatingAudioObjects,
> +    SAORS_ActivateSpatialAudioObject,
> +};
> +
> +static HRESULT WINAPI SAC_QueryInterface(ISpatialAudioClient *iface, REFIID riid, void **ppv)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +
> +    TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
> +
> +    if (!ppv)
> +        return E_POINTER;
> +
> +    *ppv = NULL;
> +
> +    if (IsEqualIID(riid, &IID_IUnknown) ||
> +            IsEqualIID(riid, &IID_ISpatialAudioClient)) {
> +        *ppv = &This->ISpatialAudioClient_iface;
> +    }
> +    else
> +        return E_NOINTERFACE;
> +
> +    IUnknown_AddRef((IUnknown *)*ppv);
> +
> +    return S_OK;
> +}
> +
> +static ULONG WINAPI SAC_AddRef(ISpatialAudioClient *iface)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    ULONG ref = InterlockedIncrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    return ref;
> +}
> +
> +static ULONG WINAPI SAC_Release(ISpatialAudioClient *iface)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    ULONG ref = InterlockedDecrement(&This->ref);
> +    TRACE("(%p) new ref %u\n", This, ref);
> +    if (!ref) {
> +        IMMDevice_Release(This->mmdev);
> +        heap_free(This);
> +    }
> +    return ref;
> +}
> +
> +static HRESULT WINAPI SAC_GetStaticObjectPosition(ISpatialAudioClient *iface,
> +        AudioObjectType type, float *x, float *y, float *z)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    FIXME("(%p)->(0x%x, %p, %p, %p)\n", This, type, x, y, z);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAC_GetNativeStaticObjectTypeMask(ISpatialAudioClient *iface,
> +        AudioObjectType *mask)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    FIXME("(%p)->(%p)\n", This, mask);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAC_GetMaxDynamicObjectCount(ISpatialAudioClient *iface,
> +        UINT32 *value)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    FIXME("(%p)->(%p)\n", This, value);
> +
> +    *value = 0;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAC_GetSupportedAudioObjectFormatEnumerator(
> +        ISpatialAudioClient *iface, IAudioFormatEnumerator **enumerator)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +
> +    TRACE("(%p)->(%p)\n", This, enumerator);
> +
> +    *enumerator = &This->IAudioFormatEnumerator_iface;
> +    SAC_AddRef(iface);
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAC_GetMaxFrameCount(ISpatialAudioClient *iface,
> +        const WAVEFORMATEX *format, UINT32 *count)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +
> +    /* FIXME: should get device period from the device */
> +    static const REFERENCE_TIME period = 100000;
> +
> +    TRACE("(%p)->(%p, %p)\n", This, format, count);
> +
> +    *count = MulDiv(period, format->nSamplesPerSec, 10000000) * MAX_PERIODS;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAC_IsAudioObjectFormatSupported(ISpatialAudioClient *iface,
> +        const WAVEFORMATEX *format)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    FIXME("(%p)->(%p)\n", This, format);
> +    return E_NOTIMPL;
> +}
> +
> +static HRESULT WINAPI SAC_IsSpatialAudioStreamAvailable(ISpatialAudioClient *iface,
> +        REFIID stream_uuid, const PROPVARIANT *info)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    FIXME("(%p)->(%s, %p)\n", This, debugstr_guid(stream_uuid), info);
> +    return E_NOTIMPL;
> +}
> +
> +static WAVEFORMATEX *clone_fmtex(const WAVEFORMATEX *src)
> +{
> +    WAVEFORMATEX *r = heap_alloc(sizeof(WAVEFORMATEX) + src->cbSize);
> +    memcpy(r, src, sizeof(WAVEFORMATEX) + src->cbSize);
> +    return r;
> +}
> +
> +static const char *debugstr_fmtex(const WAVEFORMATEX *fmt)
> +{
> +    static char buf[2048];
> +    if(fmt->wFormatTag == WAVE_FORMAT_EXTENSIBLE){
> +        const WAVEFORMATEXTENSIBLE *fmtex = (const WAVEFORMATEXTENSIBLE *)fmt;
> +        snprintf(buf, sizeof(buf), "tag: 0x%x (%s), ch: %u (mask: 0x%x), rate: %u, depth: %u",
> +                fmt->wFormatTag, debugstr_guid(&fmtex->SubFormat),
> +                fmt->nChannels, fmtex->dwChannelMask, fmt->nSamplesPerSec,
> +                fmt->wBitsPerSample);
> +    }else{
> +        snprintf(buf, sizeof(buf), "tag: 0x%x, ch: %u, rate: %u, depth: %u",
> +                fmt->wFormatTag, fmt->nChannels, fmt->nSamplesPerSec,
> +                fmt->wBitsPerSample);
> +    }
> +    return buf;
> +}
> +
> +static void static_mask_to_channels(AudioObjectType static_mask, WORD *count, DWORD *mask, UINT32 *map)
> +{
> +    UINT32 out_chan = 0, map_idx = 0;
> +    *count = 0;
> +    *mask = 0;
> +#define CONVERT_MASK(f, t) \
> +    if(static_mask & f){ \
> +        *count += 1; \
> +        *mask |= t; \
> +        map[map_idx++] = out_chan++; \
> +        TRACE("mapping 0x%x to %u\n", f, out_chan - 1); \
> +    }else{ \
> +        map[map_idx++] = ~0; \
> +    }
> +    CONVERT_MASK(AudioObjectType_FrontLeft, SPEAKER_FRONT_LEFT);
> +    CONVERT_MASK(AudioObjectType_FrontRight, SPEAKER_FRONT_RIGHT);
> +    CONVERT_MASK(AudioObjectType_FrontCenter, SPEAKER_FRONT_CENTER);
> +    CONVERT_MASK(AudioObjectType_LowFrequency, SPEAKER_LOW_FREQUENCY);
> +    CONVERT_MASK(AudioObjectType_SideLeft, SPEAKER_SIDE_LEFT);
> +    CONVERT_MASK(AudioObjectType_SideRight, SPEAKER_SIDE_RIGHT);
> +    CONVERT_MASK(AudioObjectType_BackLeft, SPEAKER_BACK_LEFT);
> +    CONVERT_MASK(AudioObjectType_BackRight, SPEAKER_BACK_RIGHT);
> +    CONVERT_MASK(AudioObjectType_TopFrontLeft, SPEAKER_TOP_FRONT_LEFT);
> +    CONVERT_MASK(AudioObjectType_TopFrontRight, SPEAKER_TOP_FRONT_RIGHT);
> +    CONVERT_MASK(AudioObjectType_TopBackLeft, SPEAKER_TOP_BACK_LEFT);
> +    CONVERT_MASK(AudioObjectType_TopBackRight, SPEAKER_TOP_BACK_RIGHT);
> +    CONVERT_MASK(AudioObjectType_BackCenter, SPEAKER_BACK_CENTER);
> +}
> +
> +static HRESULT activate_stream(SpatialAudioStreamImpl *stream)
> +{
> +    WAVEFORMATEXTENSIBLE *object_fmtex = (WAVEFORMATEXTENSIBLE *)stream->params.ObjectFormat;
> +    HRESULT hr;
> +    REFERENCE_TIME period;
> +
> +    if(!(object_fmtex->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
> +                (object_fmtex->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +                 IsEqualGUID(&object_fmtex->SubFormat, &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)))){
> +        FIXME("Only float formats are supported for now\n");
> +        return E_INVALIDARG;
> +    }
> +
> +    hr = IMMDevice_Activate(stream->sa_client->mmdev, &IID_IAudioClient,
> +            CLSCTX_INPROC_SERVER, NULL, (void**)&stream->client);
> +    if(FAILED(hr)){
> +        WARN("Activate failed: %08x\n", hr);
> +        return hr;
> +    }
> +
> +    hr = IAudioClient_GetDevicePeriod(stream->client, &period, NULL);
> +    if(FAILED(hr)){
> +        WARN("GetDevicePeriod failed: %08x\n", hr);
> +        IAudioClient_Release(stream->client);
> +        return hr;
> +    }
> +
> +    stream->stream_fmtex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
> +    static_mask_to_channels(stream->params.StaticObjectTypeMask,
> +            &stream->stream_fmtex.Format.nChannels, &stream->stream_fmtex.dwChannelMask,
> +            stream->static_object_map);
> +    stream->stream_fmtex.Format.nSamplesPerSec = stream->params.ObjectFormat->nSamplesPerSec;
> +    stream->stream_fmtex.Format.wBitsPerSample = stream->params.ObjectFormat->wBitsPerSample;
> +    stream->stream_fmtex.Format.nBlockAlign = (stream->stream_fmtex.Format.nChannels * stream->stream_fmtex.Format.wBitsPerSample) / 8;
> +    stream->stream_fmtex.Format.nAvgBytesPerSec = stream->stream_fmtex.Format.nSamplesPerSec * stream->stream_fmtex.Format.nBlockAlign;
> +    stream->stream_fmtex.Format.cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
> +    stream->stream_fmtex.Samples.wValidBitsPerSample = stream->stream_fmtex.Format.wBitsPerSample;
> +    stream->stream_fmtex.SubFormat = KSDATAFORMAT_SUBTYPE_IEEE_FLOAT;
> +
> +    hr = IAudioClient_Initialize(stream->client, AUDCLNT_SHAREMODE_SHARED,
> +            AUDCLNT_STREAMFLAGS_EVENTCALLBACK | AUDCLNT_STREAMFLAGS_NOPERSIST,
> +            period * MAX_PERIODS, 0, &stream->stream_fmtex.Format, NULL);
> +    if(FAILED(hr)){
> +        WARN("Initialize failed: %08x\n", hr);
> +        IAudioClient_Release(stream->client);
> +        return hr;
> +    }
> +
> +    hr = IAudioClient_SetEventHandle(stream->client, stream->params.EventHandle);
> +    if(FAILED(hr)){
> +        WARN("SetEventHandle failed: %08x\n", hr);
> +        IAudioClient_Release(stream->client);
> +        return hr;
> +    }
> +
> +    hr = IAudioClient_GetService(stream->client, &IID_IAudioRenderClient, (void**)&stream->render);
> +    if(FAILED(hr)){
> +        WARN("GetService(AudioRenderClient) failed: %08x\n", hr);
> +        IAudioClient_Release(stream->client);
> +        return hr;
> +    }
> +
> +    stream->period_frames = MulDiv(period, stream->stream_fmtex.Format.nSamplesPerSec, 10000000);
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAC_ActivateSpatialAudioStream(ISpatialAudioClient *iface,
> +        const PROPVARIANT *prop, REFIID riid, void **stream)
> +{
> +    SpatialAudioImpl *This = impl_from_ISpatialAudioClient(iface);
> +    SpatialAudioObjectRenderStreamActivationParams *params;
> +    HRESULT hr;
> +
> +    TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), stream);
> +
> +    if(IsEqualIID(riid, &IID_ISpatialAudioObjectRenderStream)){
> +        SpatialAudioStreamImpl *obj;
> +
> +        if(prop &&
> +                (prop->vt != VT_BLOB ||
> +                 prop->u.blob.cbSize != sizeof(SpatialAudioObjectRenderStreamActivationParams))){
> +            WARN("Got invalid params\n");
> +            *stream = NULL;
> +            return E_INVALIDARG;
> +        }
> +
> +        params = (SpatialAudioObjectRenderStreamActivationParams*) prop->u.blob.pBlobData;
> +
> +        if(params->StaticObjectTypeMask & AudioObjectType_Dynamic){
> +            *stream = NULL;
> +            return E_INVALIDARG;
> +        }
> +
> +        if(params->EventHandle == INVALID_HANDLE_VALUE ||
> +                params->EventHandle == 0){
> +            *stream = NULL;
> +            return E_INVALIDARG;
> +        }
> +
> +        if(!params->ObjectFormat ||
> +                memcmp(params->ObjectFormat, &This->object_fmtex.Format, sizeof(*params->ObjectFormat) + params->ObjectFormat->cbSize)){
> +            *stream = NULL;
> +            return AUDCLNT_E_UNSUPPORTED_FORMAT;
> +        }
> +
> +        obj = heap_alloc_zero(sizeof(SpatialAudioStreamImpl));
> +
> +        obj->ISpatialAudioObjectRenderStream_iface.lpVtbl = &ISpatialAudioObjectRenderStream_vtbl;
> +        obj->ref = 1;
> +        memcpy(&obj->params, params, sizeof(obj->params));
> +
> +        obj->update_frames = ~0;
> +
> +        InitializeCriticalSection(&obj->lock);
> +        list_init(&obj->objects);
> +
> +        obj->sa_client = This;
> +        SAC_AddRef(&This->ISpatialAudioClient_iface);
> +
> +        obj->params.ObjectFormat = clone_fmtex(obj->params.ObjectFormat);
> +
> +        DuplicateHandle(GetCurrentProcess(), obj->params.EventHandle,
> +                GetCurrentProcess(), &obj->params.EventHandle, 0, FALSE,
> +                DUPLICATE_SAME_ACCESS);
> +
> +        if(obj->params.NotifyObject)
> +            ISpatialAudioObjectRenderStreamNotify_AddRef(obj->params.NotifyObject);
> +
> +        if(TRACE_ON(mmdevapi)){
> +            TRACE("ObjectFormat: {%s}\n", debugstr_fmtex(obj->params.ObjectFormat));
> +            TRACE("StaticObjectTypeMask: 0x%x\n", obj->params.StaticObjectTypeMask);
> +            TRACE("MinDynamicObjectCount: 0x%x\n", obj->params.MinDynamicObjectCount);
> +            TRACE("MaxDynamicObjectCount: 0x%x\n", obj->params.MaxDynamicObjectCount);
> +            TRACE("Category: 0x%x\n", obj->params.Category);
> +            TRACE("EventHandle: %p\n", obj->params.EventHandle);
> +            TRACE("NotifyObject: %p\n", obj->params.NotifyObject);
> +        }
> +
> +        hr = activate_stream(obj);
> +        if(FAILED(hr)){
> +            if(obj->params.NotifyObject)
> +                ISpatialAudioObjectRenderStreamNotify_Release(obj->params.NotifyObject);
> +            DeleteCriticalSection(&obj->lock);
> +            heap_free((void*)obj->params.ObjectFormat);
> +            CloseHandle(obj->params.EventHandle);
> +            ISpatialAudioClient_Release(&obj->sa_client->ISpatialAudioClient_iface);
> +            heap_free(obj);
> +            *stream = NULL;
> +            return hr;
> +        }
> +
> +        *stream = &obj->ISpatialAudioObjectRenderStream_iface;
> +    }else{
> +        FIXME("Unsupported audio stream IID: %s\n", debugstr_guid(riid));
> +        *stream = NULL;
> +        return E_NOTIMPL;
> +    }
> +
> +    return S_OK;
> +}
> +
> +static ISpatialAudioClientVtbl ISpatialAudioClient_vtbl = {
> +    SAC_QueryInterface,
> +    SAC_AddRef,
> +    SAC_Release,
> +    SAC_GetStaticObjectPosition,
> +    SAC_GetNativeStaticObjectTypeMask,
> +    SAC_GetMaxDynamicObjectCount,
> +    SAC_GetSupportedAudioObjectFormatEnumerator,
> +    SAC_GetMaxFrameCount,
> +    SAC_IsAudioObjectFormatSupported,
> +    SAC_IsSpatialAudioStreamAvailable,
> +    SAC_ActivateSpatialAudioStream,
> +};
> +
> +static HRESULT WINAPI SAOFE_QueryInterface(IAudioFormatEnumerator *iface,
> +        REFIID riid, void **ppvObject)
> +{
> +    SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
> +    return SAC_QueryInterface(&This->ISpatialAudioClient_iface, riid, ppvObject);
> +}
> +
> +static ULONG WINAPI SAOFE_AddRef(IAudioFormatEnumerator *iface)
> +{
> +    SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
> +    return SAC_AddRef(&This->ISpatialAudioClient_iface);
> +}
> +
> +static ULONG WINAPI SAOFE_Release(IAudioFormatEnumerator *iface)
> +{
> +    SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
> +    return SAC_Release(&This->ISpatialAudioClient_iface);
> +}
> +
> +static HRESULT WINAPI SAOFE_GetCount(IAudioFormatEnumerator *iface, UINT32 *count)
> +{
> +    SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
> +
> +    TRACE("(%p)->(%p)\n", This, count);
> +
> +    *count = 1;
> +
> +    return S_OK;
> +}
> +
> +static HRESULT WINAPI SAOFE_GetFormat(IAudioFormatEnumerator *iface,
> +        UINT32 index, WAVEFORMATEX **format)
> +{
> +    SpatialAudioImpl *This = impl_from_IAudioFormatEnumerator(iface);
> +
> +    TRACE("(%p)->(%u, %p)\n", This, index, format);
> +
> +    if(index > 0)
> +        return E_INVALIDARG;
> +
> +    *format = &This->object_fmtex.Format;
> +
> +    return S_OK;
> +}
> +
> +static IAudioFormatEnumeratorVtbl IAudioFormatEnumerator_vtbl = {
> +    SAOFE_QueryInterface,
> +    SAOFE_AddRef,
> +    SAOFE_Release,
> +    SAOFE_GetCount,
> +    SAOFE_GetFormat,
> +};
> +
> +HRESULT SpatialAudioClient_Create(IMMDevice *mmdev, ISpatialAudioClient **out)
> +{
> +    SpatialAudioImpl *obj;
> +    IAudioClient *aclient;
> +    WAVEFORMATEX *closest;
> +    HRESULT hr;
> +
> +    obj = heap_alloc_zero(sizeof(*obj));
> +
> +    obj->ref = 1;
> +    obj->ISpatialAudioClient_iface.lpVtbl = &ISpatialAudioClient_vtbl;
> +    obj->IAudioFormatEnumerator_iface.lpVtbl = &IAudioFormatEnumerator_vtbl;
> +
> +    obj->object_fmtex.Format.wFormatTag = WAVE_FORMAT_IEEE_FLOAT;
> +    obj->object_fmtex.Format.nChannels = 1;
> +    obj->object_fmtex.Format.nSamplesPerSec = 48000;
> +    obj->object_fmtex.Format.wBitsPerSample = sizeof(float) * 8;
> +    obj->object_fmtex.Format.nBlockAlign = (obj->object_fmtex.Format.nChannels * obj->object_fmtex.Format.wBitsPerSample) / 8;
> +    obj->object_fmtex.Format.nAvgBytesPerSec = obj->object_fmtex.Format.nSamplesPerSec * obj->object_fmtex.Format.nBlockAlign;
> +    obj->object_fmtex.Format.cbSize = 0;
> +
> +    hr = IMMDevice_Activate(mmdev, &IID_IAudioClient,
> +            CLSCTX_INPROC_SERVER, NULL, (void**)&aclient);
> +    if(FAILED(hr)){
> +        WARN("Activate failed: %08x\n", hr);
> +        heap_free(obj);
> +        return hr;
> +    }
> +
> +    hr = IAudioClient_IsFormatSupported(aclient, AUDCLNT_SHAREMODE_SHARED, &obj->object_fmtex.Format, &closest);
> +
> +    IAudioClient_Release(aclient);
> +
> +    if(hr == S_FALSE){
> +        if(sizeof(WAVEFORMATEX) + closest->cbSize > sizeof(obj->object_fmtex)){
> +            ERR("Returned format too large: %s\n", debugstr_fmtex(closest));
> +            CoTaskMemFree(closest);
> +            heap_free(obj);
> +            return AUDCLNT_E_UNSUPPORTED_FORMAT;
> +        }else if(!((closest->wFormatTag == WAVE_FORMAT_IEEE_FLOAT ||
> +                    (closest->wFormatTag == WAVE_FORMAT_EXTENSIBLE &&
> +                     IsEqualGUID(&((WAVEFORMATEXTENSIBLE *)closest)->SubFormat,
> +                         &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT))) &&
> +                    closest->wBitsPerSample == 32)){
> +            ERR("Returned format not 32-bit float: %s\n", debugstr_fmtex(closest));
> +            CoTaskMemFree(closest);
> +            heap_free(obj);
> +            return AUDCLNT_E_UNSUPPORTED_FORMAT;
> +        }
> +        WARN("The audio stack doesn't support 48kHz 32bit float. Using the closest match. Audio may be glitchy. %s\n", debugstr_fmtex(closest));
> +        memcpy(&obj->object_fmtex,
> +               closest,
> +               sizeof(WAVEFORMATEX) + closest->cbSize);
> +        CoTaskMemFree(closest);
> +    } else if(hr != S_OK){
> +        WARN("Checking supported formats failed: %08x\n", hr);
> +        heap_free(obj);
> +        return hr;
> +    }
> +
> +    obj->mmdev = mmdev;
> +    IMMDevice_AddRef(mmdev);
> +
> +    *out = &obj->ISpatialAudioClient_iface;
> +
> +    return S_OK;
> +}
> diff --git a/include/spatialaudioclient.idl b/include/spatialaudioclient.idl
> index 16a1541fd1d..08c84965566 100644
> --- a/include/spatialaudioclient.idl
> +++ b/include/spatialaudioclient.idl
> @@ -43,6 +43,47 @@ typedef [v1_enum] enum AudioObjectType
>      AudioObjectType_BackCenter       = 0x00020000,
>  } AudioObjectType;
>  
> +cpp_quote("#define SPTLAUDCLNT_E_DESTROYED                     AUDCLNT_ERR(0x100)")
> +cpp_quote("#define SPTLAUDCLNT_E_OUT_OF_ORDER                  AUDCLNT_ERR(0x101)")
> +cpp_quote("#define SPTLAUDCLNT_E_RESOURCES_INVALIDATED         AUDCLNT_ERR(0x102)")
> +cpp_quote("#define SPTLAUDCLNT_E_NO_MORE_OBJECTS               AUDCLNT_ERR(0x103)")
> +cpp_quote("#define SPTLAUDCLNT_E_PROPERTY_NOT_SUPPORTED        AUDCLNT_ERR(0x104)")
> +cpp_quote("#define SPTLAUDCLNT_E_ERRORS_IN_OBJECT_CALLS        AUDCLNT_ERR(0x105)")
> +cpp_quote("#define SPTLAUDCLNT_E_METADATA_FORMAT_NOT_SUPPORTED AUDCLNT_ERR(0x106)")
> +cpp_quote("#define SPTLAUDCLNT_E_STREAM_NOT_AVAILABLE          AUDCLNT_ERR(0x107)")
> +cpp_quote("#define SPTLAUDCLNT_E_INVALID_LICENSE               AUDCLNT_ERR(0x108)")
> +cpp_quote("#define SPTLAUDCLNT_E_STREAM_NOT_STOPPED            AUDCLNT_ERR(0x10a)")
> +cpp_quote("#define SPTLAUDCLNT_E_STATIC_OBJECT_NOT_AVAILABLE   AUDCLNT_ERR(0x10b)")
> +cpp_quote("#define SPTLAUDCLNT_E_OBJECT_ALREADY_ACTIVE         AUDCLNT_ERR(0x10c)")
> +cpp_quote("#define SPTLAUDCLNT_E_INTERNAL                      AUDCLNT_ERR(0x10d)")
> +
> +interface ISpatialAudioObjectRenderStreamBase;
> +
> +[
> +    object,
> +    uuid(dddf83e6-68d7-4c70-883f-a1836afb4a50),
> +    pointer_default(unique),
> +    local
> +]
> +interface ISpatialAudioObjectRenderStreamNotify : IUnknown
> +{
> +    HRESULT OnAvailableDynamicObjectCountChange(
> +        [in] ISpatialAudioObjectRenderStreamBase *stream,
> +        [in] LONGLONG deadline,
> +        [in] UINT32 object_count);
> +}
> +
> +typedef struct tagSpatialAudioObjectRenderStreamActivationParams
> +{
> +    const WAVEFORMATEX *ObjectFormat;
> +    AudioObjectType StaticObjectTypeMask;
> +    UINT32 MinDynamicObjectCount;
> +    UINT32 MaxDynamicObjectCount;
> +    AUDIO_STREAM_CATEGORY Category;
> +    HANDLE EventHandle;
> +    ISpatialAudioObjectRenderStreamNotify *NotifyObject;
> +} SpatialAudioObjectRenderStreamActivationParams;
> +
>  [
>      object,
>      uuid(dcdaa858-895a-4a22-a5eb-67bda506096d),
> @@ -98,3 +139,83 @@ interface ISpatialAudioClient : IUnknown
>          [in] REFIID riid,
>          [out, iid_is(riid)] void **stream);
>  }
> +
> +[
> +    object,
> +    uuid(cce0b8f2-8d4d-4efb-a8cf-3d6ecf1c30e0),
> +    pointer_default(unique),
> +    local
> +]
> +interface ISpatialAudioObjectBase : IUnknown
> +{
> +    HRESULT GetBuffer(
> +        [out] BYTE **buffer,
> +        [out] UINT32 *bytes);
> +
> +    HRESULT SetEndOfStream(
> +        [in] UINT32 frames);
> +
> +    HRESULT IsActive(
> +        [out] BOOL *active);
> +
> +    HRESULT GetAudioObjectType(
> +        [out] AudioObjectType *type);
> +}
> +
> +[
> +    object,
> +    uuid(dde28967-521b-46e5-8f00-bd6f2bc8ab1d),
> +    pointer_default(unique),
> +    local
> +]
> +interface ISpatialAudioObject : ISpatialAudioObjectBase
> +{
> +    HRESULT SetPosition(
> +        [in] float x,
> +        [in] float y,
> +        [in] float z);
> +
> +    HRESULT SetVolume(
> +        [in] float vol);
> +}
> +
> +[
> +    object,
> +    uuid(feaaf403-c1d8-450d-aa05-e0ccee7502a8),
> +    pointer_default(unique),
> +    local
> +]
> +interface ISpatialAudioObjectRenderStreamBase : IUnknown
> +{
> +    HRESULT GetAvailableDynamicObjectCount(
> +        [out] UINT32 *count);
> +
> +    HRESULT GetService(
> +        [in] REFIID riid,
> +        [out] void **service);
> +
> +    HRESULT Start();
> +
> +    HRESULT Stop();
> +
> +    HRESULT Reset();
> +
> +    HRESULT BeginUpdatingAudioObjects(
> +        [out] UINT32 *count,
> +        [out] UINT32 *frames);
> +
> +    HRESULT EndUpdatingAudioObjects();
> +}
> +
> +[
> +    object,
> +    uuid(bab5f473-b423-477b-85f5-b5a332a04153),
> +    pointer_default(unique),
> +    local
> +]
> +interface ISpatialAudioObjectRenderStream : ISpatialAudioObjectRenderStreamBase
> +{
> +    HRESULT ActivateSpatialAudioObject(
> +        [in] AudioObjectType type,
> +        [out] ISpatialAudioObject **object);
> +}
> -- 
> 2.30.0
> 



More information about the wine-devel mailing list