Maarten Lankhorst : mmdevapi: Add support for IAudioRenderClient.

Alexandre Julliard julliard at winehq.org
Tue Apr 20 11:32:23 CDT 2010


Module: wine
Branch: master
Commit: cffb95fdb835140cd3b167835f3e445a8ed5974b
URL:    http://source.winehq.org/git/wine.git/?a=commit;h=cffb95fdb835140cd3b167835f3e445a8ed5974b

Author: Maarten Lankhorst <m.b.lankhorst at gmail.com>
Date:   Mon Apr 19 11:52:51 2010 +0200

mmdevapi: Add support for IAudioRenderClient.

---

 dlls/mmdevapi/audio.c |  224 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 224 insertions(+), 0 deletions(-)

diff --git a/dlls/mmdevapi/audio.c b/dlls/mmdevapi/audio.c
index fcefc5a..b2ee3f0 100644
--- a/dlls/mmdevapi/audio.c
+++ b/dlls/mmdevapi/audio.c
@@ -74,9 +74,21 @@ typedef struct ACImpl {
     HANDLE timer_id;
     ALCdevice *capdev;
     ALint format;
+
+    ACRender *render;
 } ACImpl;
 
+struct ACRender {
+    const IAudioRenderClientVtbl *lpVtbl;
+    LONG ref;
+    ACImpl *parent;
+};
+
 static const IAudioClientVtbl ACImpl_Vtbl;
+static const IAudioRenderClientVtbl ACRender_Vtbl;
+
+static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv);
+static void AudioRenderClient_Destroy(ACRender *This);
 
 static int get_format_PCM(WAVEFORMATEX *format)
 {
@@ -251,6 +263,8 @@ static void AudioClient_Destroy(ACImpl *This)
 {
     if (This->timer_id)
         DeleteTimerQueueTimer(NULL, This->timer_id, INVALID_HANDLE_VALUE);
+    if (This->render)
+        AudioRenderClient_Destroy(This->render);
     if (This->parent->flow == eRender && This->init) {
         setALContext(This->parent->ctx);
         IAudioClient_Stop((IAudioClient*)This);
@@ -808,6 +822,14 @@ static HRESULT WINAPI AC_GetService(IAudioClient *iface, REFIID riid, void **ppv
         return E_POINTER;
     *ppv = NULL;
 
+    if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
+        if (This->parent->flow != eRender)
+            return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+        if (!This->render)
+            hr = AudioRenderClient_Create(This, &This->render);
+        *ppv = This->render;
+    }
+
     if (FAILED(hr))
         return hr;
 
@@ -839,4 +861,206 @@ static const IAudioClientVtbl ACImpl_Vtbl =
     AC_GetService
 };
 
+static HRESULT AudioRenderClient_Create(ACImpl *parent, ACRender **ppv)
+{
+    ACRender *This;
+
+    This = *ppv = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
+    if (!This)
+        return E_OUTOFMEMORY;
+    This->lpVtbl = &ACRender_Vtbl;
+    This->ref = 0;
+    This->parent = parent;
+    return S_OK;
+}
+
+static void AudioRenderClient_Destroy(ACRender *This)
+{
+    This->parent->render = NULL;
+    HeapFree(GetProcessHeap(), 0, This);
+}
+
+static HRESULT WINAPI ACR_QueryInterface(IAudioRenderClient *iface, REFIID riid, void **ppv)
+{
+    TRACE("(%p)->(%s,%p)\n", iface, debugstr_guid(riid), ppv);
+
+    if (!ppv)
+        return E_POINTER;
+    *ppv = NULL;
+    if (IsEqualIID(riid, &IID_IUnknown)
+        || IsEqualIID(riid, &IID_IAudioRenderClient))
+        *ppv = iface;
+    if (*ppv) {
+        IUnknown_AddRef((IUnknown*)*ppv);
+        return S_OK;
+    }
+    WARN("Unknown interface %s\n", debugstr_guid(riid));
+    return E_NOINTERFACE;
+}
+
+static ULONG WINAPI ACR_AddRef(IAudioRenderClient *iface)
+{
+    ACRender *This = (ACRender*)iface;
+    ULONG ref;
+    ref = InterlockedIncrement(&This->ref);
+    TRACE("Refcount now %i\n", ref);
+    return ref;
+}
+
+static ULONG WINAPI ACR_Release(IAudioRenderClient *iface)
+{
+    ACRender *This = (ACRender*)iface;
+    ULONG ref;
+    ref = InterlockedDecrement(&This->ref);
+    TRACE("Refcount now %i\n", ref);
+    if (!ref)
+        AudioRenderClient_Destroy(This);
+    return ref;
+}
+
+static HRESULT WINAPI ACR_GetBuffer(IAudioRenderClient *iface, UINT32 frames, BYTE **data)
+{
+    ACRender *This = (ACRender*)iface;
+    DWORD free, framesize;
+    TRACE("(%p)->(%u,%p)\n", This, frames, data);
+
+    if (!data)
+        return E_POINTER;
+    if (!frames)
+        return S_OK;
+    *data = NULL;
+    if (This->parent->locked) {
+        ERR("Locked\n");
+        return AUDCLNT_E_OUT_OF_ORDER;
+    }
+    AC_GetCurrentPadding((IAudioClient*)This->parent, &free);
+    if (This->parent->bufsize-free < frames) {
+        ERR("Too large: %u %u %u\n", This->parent->bufsize, free, frames);
+        return AUDCLNT_E_BUFFER_TOO_LARGE;
+    }
+    EnterCriticalSection(This->parent->crst);
+    This->parent->locked = frames;
+    framesize = This->parent->pwfx->nBlockAlign;
+
+    /* Exact offset doesn't matter, offset could be 0 forever
+     * but increasing it is easier to debug */
+    if (This->parent->ofs + frames > This->parent->bufsize)
+        This->parent->ofs = 0;
+    *data = This->parent->buffer + This->parent->ofs * framesize;
+
+    LeaveCriticalSection(This->parent->crst);
+    return S_OK;
+}
+
+static HRESULT WINAPI ACR_ReleaseBuffer(IAudioRenderClient *iface, UINT32 written, DWORD flags)
+{
+    ACRender *This = (ACRender*)iface;
+    BYTE *buf = This->parent->buffer;
+    DWORD framesize = This->parent->pwfx->nBlockAlign;
+    DWORD ofs = This->parent->ofs;
+    DWORD bufsize = This->parent->bufsize;
+    DWORD locked = This->parent->locked;
+    DWORD freq = This->parent->pwfx->nSamplesPerSec;
+    DWORD bpp = This->parent->pwfx->wBitsPerSample;
+    ALuint albuf;
+
+    TRACE("(%p)->(%u,%x)\n", This, written, flags);
+
+    if (locked < written)
+        return AUDCLNT_E_INVALID_SIZE;
+
+    if (flags & ~AUDCLNT_BUFFERFLAGS_SILENT)
+        return E_INVALIDARG;
+
+    if (!written) {
+        FIXME("Handled right?\n");
+        This->parent->locked = 0;
+        return S_OK;
+    }
+
+    if (!This->parent->locked)
+        return AUDCLNT_E_OUT_OF_ORDER;
+
+    EnterCriticalSection(This->parent->crst);
+    setALContext(This->parent->parent->ctx);
+
+    This->parent->ofs += written;
+    This->parent->ofs %= bufsize;
+    This->parent->pad += written;
+    This->parent->frameswritten += written;
+
+    ofs *= framesize;
+    written *= framesize;
+    bufsize *= framesize;
+    locked *= framesize;
+
+    if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
+        memset(buf+ofs, bpp != 8 ? 0 : 128, written);
+    TRACE("buf: %p, ofs: %x, written %u, freq %u\n", buf, ofs, written, freq);
+
+    palGenBuffers(1, &albuf);
+    palBufferData(albuf, This->parent->format, buf+ofs, written, freq);
+    palSourceQueueBuffers(This->parent->source, 1, &albuf);
+    TRACE("Queued %u\n", albuf);
+
+    if (This->parent->running) {
+        ALint state = AL_PLAYING, done = 0, padpart = 0;
+
+        palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
+        palGetSourcei(This->parent->source, AL_BYTE_OFFSET, &padpart);
+        palGetSourcei(This->parent->source, AL_SOURCE_STATE, &state);
+        padpart /= framesize;
+
+        if (state == AL_STOPPED) {
+            padpart = This->parent->pad;
+            /* Buffer might have been processed in the small window
+             * between first and third call */
+            palGetSourcei(This->parent->source, AL_BUFFERS_PROCESSED, &done);
+        }
+        if (done || This->parent->padpartial != padpart)
+            This->parent->laststamp = gettime();
+        This->parent->padpartial = padpart;
+
+        while (done--) {
+            ALint size, bits, chan;
+            ALuint which;
+
+            palSourceUnqueueBuffers(This->parent->source, 1, &which);
+            palGetBufferi(which, AL_SIZE, &size);
+            palGetBufferi(which, AL_BITS, &bits);
+            palGetBufferi(which, AL_CHANNELS, &chan);
+            size /= bits * chan / 8;
+            if (size > This->parent->pad) {
+                ERR("Overflow!\n");
+                size = This->parent->pad;
+            }
+            This->parent->pad -= size;
+            This->parent->padpartial -= size;
+            TRACE("Unqueued %u\n", which);
+            palDeleteBuffers(1, &which);
+        }
+
+        if (state != AL_PLAYING) {
+            ERR("Starting from %x\n", state);
+            palSourcePlay(This->parent->source);
+        }
+        getALError();
+    }
+    This->parent->locked = 0;
+
+    getALError();
+    popALContext();
+    LeaveCriticalSection(This->parent->crst);
+
+    return S_OK;
+}
+
+static const IAudioRenderClientVtbl ACRender_Vtbl = {
+    ACR_QueryInterface,
+    ACR_AddRef,
+    ACR_Release,
+    ACR_GetBuffer,
+    ACR_ReleaseBuffer
+};
+
 #endif




More information about the wine-cvs mailing list