[PATCH 4/5] winepulse: Add IAudioRenderClient and IAudioCaptureClient

Andrew Eikum aeikum at codeweavers.com
Fri Oct 16 11:06:49 CDT 2015


From: Maarten Lankhorst <m.b.lankhorst at gmail.com>

Signed-off-by: Andrew Eikum <aeikum at codeweavers.com>
---
 dlls/winepulse.drv/mmdevdrv.c | 301 ++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 301 insertions(+)

diff --git a/dlls/winepulse.drv/mmdevdrv.c b/dlls/winepulse.drv/mmdevdrv.c
index ca5715e..2af0dcb 100644
--- a/dlls/winepulse.drv/mmdevdrv.c
+++ b/dlls/winepulse.drv/mmdevdrv.c
@@ -126,6 +126,8 @@ typedef struct _ACPacket {
 
 struct ACImpl {
     IAudioClient IAudioClient_iface;
+    IAudioRenderClient IAudioRenderClient_iface;
+    IAudioCaptureClient IAudioCaptureClient_iface;
     IMMDevice *parent;
     struct list entry;
     float vol[PA_CHANNELS_MAX];
@@ -152,12 +154,24 @@ struct ACImpl {
 static const WCHAR defaultW[] = {'P','u','l','s','e','a','u','d','i','o',0};
 
 static const IAudioClientVtbl AudioClient_Vtbl;
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl;
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl;
 
 static inline ACImpl *impl_from_IAudioClient(IAudioClient *iface)
 {
     return CONTAINING_RECORD(iface, ACImpl, IAudioClient_iface);
 }
 
+static inline ACImpl *impl_from_IAudioRenderClient(IAudioRenderClient *iface)
+{
+    return CONTAINING_RECORD(iface, ACImpl, IAudioRenderClient_iface);
+}
+
+static inline ACImpl *impl_from_IAudioCaptureClient(IAudioCaptureClient *iface)
+{
+    return CONTAINING_RECORD(iface, ACImpl, IAudioCaptureClient_iface);
+}
+
 /* Following pulseaudio design here, mainloop has the lock taken whenever
  * it is handling something for pulse, and the lock is required whenever
  * doing any pa_* call that can affect the state in any way
@@ -709,6 +723,8 @@ HRESULT WINAPI AUDDRV_GetAudioEndpoint(GUID *guid, IMMDevice *dev, IAudioClient
         return E_OUTOFMEMORY;
 
     This->IAudioClient_iface.lpVtbl = &AudioClient_Vtbl;
+    This->IAudioRenderClient_iface.lpVtbl = &AudioRenderClient_Vtbl;
+    This->IAudioCaptureClient_iface.lpVtbl = &AudioCaptureClient_Vtbl;
     This->dataflow = dataflow;
     This->parent = dev;
     for (i = 0; i < PA_CHANNELS_MAX; ++i)
@@ -1492,6 +1508,16 @@ static HRESULT WINAPI AudioClient_GetService(IAudioClient *iface, REFIID riid,
     if (FAILED(hr))
         return hr;
 
+    if (IsEqualIID(riid, &IID_IAudioRenderClient)) {
+        if (This->dataflow != eRender)
+            return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+        *ppv = &This->IAudioRenderClient_iface;
+    } else if (IsEqualIID(riid, &IID_IAudioCaptureClient)) {
+        if (This->dataflow != eCapture)
+            return AUDCLNT_E_WRONG_ENDPOINT_TYPE;
+        *ppv = &This->IAudioCaptureClient_iface;
+    }
+
     if (*ppv) {
         IUnknown_AddRef((IUnknown*)*ppv);
         return S_OK;
@@ -1520,6 +1546,281 @@ static const IAudioClientVtbl AudioClient_Vtbl =
     AudioClient_GetService
 };
 
+static HRESULT WINAPI AudioRenderClient_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 AudioRenderClient_AddRef(IAudioRenderClient *iface)
+{
+    ACImpl *This = impl_from_IAudioRenderClient(iface);
+    return AudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioRenderClient_Release(IAudioRenderClient *iface)
+{
+    ACImpl *This = impl_from_IAudioRenderClient(iface);
+    return AudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioRenderClient_GetBuffer(IAudioRenderClient *iface,
+        UINT32 frames, BYTE **data)
+{
+    ACImpl *This = impl_from_IAudioRenderClient(iface);
+    size_t avail, req, bytes = frames * pa_frame_size(&This->ss);
+    UINT32 pad;
+    HRESULT hr = S_OK;
+    int ret = -1;
+
+    TRACE("(%p)->(%u, %p)\n", This, frames, data);
+
+    if (!data)
+        return E_POINTER;
+    *data = NULL;
+
+    pthread_mutex_lock(&pulse_lock);
+    hr = pulse_stream_valid(This);
+    if (FAILED(hr) || This->locked) {
+        pthread_mutex_unlock(&pulse_lock);
+        return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+    }
+    if (!frames) {
+        pthread_mutex_unlock(&pulse_lock);
+        return S_OK;
+    }
+
+    ACImpl_GetRenderPad(This, &pad);
+    avail = This->bufsize_frames - pad;
+    if (avail < frames || bytes > This->bufsize_bytes) {
+        pthread_mutex_unlock(&pulse_lock);
+        WARN("Wanted to write %u, but only %zu available\n", frames, avail);
+        return AUDCLNT_E_BUFFER_TOO_LARGE;
+    }
+
+    This->locked = frames;
+    req = bytes;
+    ret = pa_stream_begin_write(This->stream, &This->locked_ptr, &req);
+    if (ret < 0 || req < bytes) {
+        FIXME("%p Not using pulse locked data: %i %zu/%u %u/%u\n", This, ret, req/pa_frame_size(&This->ss), frames, pad, This->bufsize_frames);
+        if (ret >= 0)
+            pa_stream_cancel_write(This->stream);
+        *data = This->tmp_buffer;
+        This->locked_ptr = NULL;
+    } else
+        *data = This->locked_ptr;
+    pthread_mutex_unlock(&pulse_lock);
+    return hr;
+}
+
+static void pulse_free_noop(void *buf)
+{
+}
+
+static HRESULT WINAPI AudioRenderClient_ReleaseBuffer(
+        IAudioRenderClient *iface, UINT32 written_frames, DWORD flags)
+{
+    ACImpl *This = impl_from_IAudioRenderClient(iface);
+    UINT32 written_bytes = written_frames * pa_frame_size(&This->ss);
+
+    TRACE("(%p)->(%u, %x)\n", This, written_frames, flags);
+
+    pthread_mutex_lock(&pulse_lock);
+    if (!This->locked || !written_frames) {
+        if (This->locked_ptr)
+            pa_stream_cancel_write(This->stream);
+        This->locked = 0;
+        This->locked_ptr = NULL;
+        pthread_mutex_unlock(&pulse_lock);
+        return written_frames ? AUDCLNT_E_OUT_OF_ORDER : S_OK;
+    }
+
+    if (This->locked < written_frames) {
+        pthread_mutex_unlock(&pulse_lock);
+        return AUDCLNT_E_INVALID_SIZE;
+    }
+
+    if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
+        if (This->ss.format == PA_SAMPLE_U8)
+            memset(This->tmp_buffer, 128, written_bytes);
+        else
+            memset(This->tmp_buffer, 0, written_bytes);
+    }
+
+    This->locked = 0;
+    if (This->locked_ptr)
+        pa_stream_write(This->stream, This->locked_ptr, written_bytes, NULL, 0, PA_SEEK_RELATIVE);
+    else
+        pa_stream_write(This->stream, This->tmp_buffer, written_bytes, pulse_free_noop, 0, PA_SEEK_RELATIVE);
+    This->pad += written_bytes;
+    This->locked_ptr = NULL;
+    TRACE("Released %u, pad %zu\n", written_frames, This->pad / pa_frame_size(&This->ss));
+    assert(This->pad <= This->bufsize_bytes);
+
+    pthread_mutex_unlock(&pulse_lock);
+    return S_OK;
+}
+
+static const IAudioRenderClientVtbl AudioRenderClient_Vtbl = {
+    AudioRenderClient_QueryInterface,
+    AudioRenderClient_AddRef,
+    AudioRenderClient_Release,
+    AudioRenderClient_GetBuffer,
+    AudioRenderClient_ReleaseBuffer
+};
+
+static HRESULT WINAPI AudioCaptureClient_QueryInterface(
+        IAudioCaptureClient *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_IAudioCaptureClient))
+        *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 AudioCaptureClient_AddRef(IAudioCaptureClient *iface)
+{
+    ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    return IAudioClient_AddRef(&This->IAudioClient_iface);
+}
+
+static ULONG WINAPI AudioCaptureClient_Release(IAudioCaptureClient *iface)
+{
+    ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    return IAudioClient_Release(&This->IAudioClient_iface);
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetBuffer(IAudioCaptureClient *iface,
+        BYTE **data, UINT32 *frames, DWORD *flags, UINT64 *devpos,
+        UINT64 *qpcpos)
+{
+    ACImpl *This = impl_from_IAudioCaptureClient(iface);
+    HRESULT hr;
+    ACPacket *packet;
+
+    TRACE("(%p)->(%p, %p, %p, %p, %p)\n", This, data, frames, flags,
+            devpos, qpcpos);
+
+    if (!data || !frames || !flags)
+        return E_POINTER;
+
+    pthread_mutex_lock(&pulse_lock);
+    hr = pulse_stream_valid(This);
+    if (FAILED(hr) || This->locked) {
+        pthread_mutex_unlock(&pulse_lock);
+        return FAILED(hr) ? hr : AUDCLNT_E_OUT_OF_ORDER;
+    }
+
+    ACImpl_GetCapturePad(This, NULL);
+    if ((packet = This->locked_ptr)) {
+        *frames = This->capture_period / pa_frame_size(&This->ss);
+        *flags = 0;
+        if (packet->discont)
+            *flags |= AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY;
+        if (devpos) {
+            if (packet->discont)
+                *devpos = (This->clock_written + This->capture_period) / pa_frame_size(&This->ss);
+            else
+                *devpos = This->clock_written / pa_frame_size(&This->ss);
+        }
+        if (qpcpos)
+            *qpcpos = packet->qpcpos;
+        *data = packet->data;
+    }
+    else
+        *frames = 0;
+    This->locked = *frames;
+    pthread_mutex_unlock(&pulse_lock);
+    return *frames ? S_OK : AUDCLNT_S_BUFFER_EMPTY;
+}
+
+static HRESULT WINAPI AudioCaptureClient_ReleaseBuffer(
+        IAudioCaptureClient *iface, UINT32 done)
+{
+    ACImpl *This = impl_from_IAudioCaptureClient(iface);
+
+    TRACE("(%p)->(%u)\n", This, done);
+
+    pthread_mutex_lock(&pulse_lock);
+    if (!This->locked && done) {
+        pthread_mutex_unlock(&pulse_lock);
+        return AUDCLNT_E_OUT_OF_ORDER;
+    }
+    if (done && This->locked != done) {
+        pthread_mutex_unlock(&pulse_lock);
+        return AUDCLNT_E_INVALID_SIZE;
+    }
+    if (done) {
+        ACPacket *packet = This->locked_ptr;
+        This->locked_ptr = NULL;
+        This->pad -= This->capture_period;
+        if (packet->discont)
+            This->clock_written += 2 * This->capture_period;
+        else
+            This->clock_written += This->capture_period;
+        list_add_tail(&This->packet_free_head, &packet->entry);
+    }
+    This->locked = 0;
+    pthread_mutex_unlock(&pulse_lock);
+    return S_OK;
+}
+
+static HRESULT WINAPI AudioCaptureClient_GetNextPacketSize(
+        IAudioCaptureClient *iface, UINT32 *frames)
+{
+    ACImpl *This = impl_from_IAudioCaptureClient(iface);
+
+    TRACE("(%p)->(%p)\n", This, frames);
+    if (!frames)
+        return E_POINTER;
+
+    pthread_mutex_lock(&pulse_lock);
+    ACImpl_GetCapturePad(This, NULL);
+    if (This->locked_ptr)
+        *frames = This->capture_period / pa_frame_size(&This->ss);
+    else
+        *frames = 0;
+    pthread_mutex_unlock(&pulse_lock);
+    return S_OK;
+}
+
+static const IAudioCaptureClientVtbl AudioCaptureClient_Vtbl =
+{
+    AudioCaptureClient_QueryInterface,
+    AudioCaptureClient_AddRef,
+    AudioCaptureClient_Release,
+    AudioCaptureClient_GetBuffer,
+    AudioCaptureClient_ReleaseBuffer,
+    AudioCaptureClient_GetNextPacketSize
+};
+
 HRESULT WINAPI AUDDRV_GetAudioSessionManager(IMMDevice *device,
         IAudioSessionManager2 **out)
 {
-- 
2.6.1





More information about the wine-patches mailing list