[PATCH 5/5] dsound: Add support for IDirectSoundBufferNotify in capture

Maarten Lankhorst m.b.lankhorst at gmail.com
Mon Nov 30 15:56:21 CST 2009


---
 dlls/dsound/capture.c |  230 ++++++++++++++++++++++++++++++++++++++++++++++++-
 1 files changed, 227 insertions(+), 3 deletions(-)

diff --git a/dlls/dsound/capture.c b/dlls/dsound/capture.c
index c95d5c1..aee10d0 100644
--- a/dlls/dsound/capture.c
+++ b/dlls/dsound/capture.c
@@ -56,18 +56,23 @@ typedef struct DSCImpl
 
     ALCchar *device;
     DSCBuffer *buf;
+    UINT timer_id;
+    DWORD timer_res;
     CRITICAL_SECTION crst;
 } DSCImpl;
 
 struct DSCBuffer
 {
     const IDirectSoundCaptureBuffer8Vtbl *lpVtbl;
-    LONG ref;
+    const IDirectSoundNotifyVtbl *lpNotVtbl;
+    LONG ref, not_ref;
     DSCImpl *parent;
     ALCdevice *dev;
     DWORD buf_size;
     BYTE *buf;
     WAVEFORMATEX *format;
+    DSBPOSITIONNOTIFY *notify;
+    DWORD nnotify;
 
     DWORD pos;
     BOOL playing, looping;
@@ -75,6 +80,7 @@ struct DSCBuffer
 
 static const IDirectSoundCaptureVtbl DSC_Vtbl;
 static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl;
+static const IDirectSoundNotifyVtbl DSCNot_Vtbl;
 
 static void DSCImpl_Destroy(DSCImpl *This);
 
@@ -84,11 +90,118 @@ static HRESULT DSCBuffer_Create(DSCBuffer **buf)
     if (!This)
         return E_OUTOFMEMORY;
     This->lpVtbl = &DSCBuffer_Vtbl;
+    This->lpNotVtbl = &DSCNot_Vtbl;
     This->ref = 1;
     *buf = This;
     return S_OK;
 }
 
+static void trigger_notifies(DSCBuffer *buf, DWORD lastpos, DWORD curpos)
+{
+    DWORD i;
+    if (lastpos == curpos)
+        return;
+    for (i = 0; i < buf->nnotify; ++i)
+    {
+        DSBPOSITIONNOTIFY *not = &buf->notify[i];
+        HANDLE event = not->hEventNotify;
+        DWORD ofs = not->dwOffset;
+
+        if (ofs == DSCBPN_OFFSET_STOP)
+            continue;
+
+        /* Wraparound case */
+        if (curpos < lastpos)
+        {
+            if (ofs < curpos
+                || ofs >= lastpos)
+                SetEvent(event);
+            continue;
+        }
+
+        /* Normal case */
+        if (ofs >= lastpos
+            && ofs < curpos)
+            SetEvent(event);
+    }
+}
+
+static void CALLBACK DSCBuffer_timer(UINT timerID, UINT msg, DWORD_PTR dwUser,
+                                     DWORD_PTR dw1, DWORD_PTR dw2)
+{
+    DSCImpl *This = (DSCImpl*)dwUser;
+    DSCBuffer *buf;
+    ALint avail = 0;
+
+    EnterCriticalSection(&This->crst);
+    buf = This->buf;
+    if (!buf || !buf->dev || !buf->playing)
+        goto out;
+
+    alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
+    if (avail)
+    {
+        avail *= buf->format->nBlockAlign;
+        if (avail + buf->pos > buf->buf_size)
+            avail = buf->buf_size - buf->pos;
+
+        alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
+        trigger_notifies(buf, buf->pos, buf->pos + avail);
+        buf->pos += avail;
+
+        if (buf->pos == buf->buf_size)
+        {
+            buf->pos = 0;
+            if (!buf->looping)
+                IDirectSoundCaptureBuffer_Stop((IDirectSoundCaptureBuffer*)buf);
+            else
+            {
+                avail = 0;
+                alcGetIntegerv(buf->dev, ALC_CAPTURE_SAMPLES, 1, &avail);
+                avail *= buf->format->nBlockAlign;
+                if (avail >= buf->buf_size)
+                {
+                    ERR("TOO MUCH AVAIL: %u/%u\n", avail, buf->buf_size);
+                    avail = buf->buf_size;
+                }
+
+                if (avail)
+                {
+                    alcCaptureSamples(buf->dev, buf->buf + buf->pos, avail/buf->format->nBlockAlign);
+                    trigger_notifies(buf, buf->pos, buf->pos + avail);
+                    buf->pos += avail;
+                }
+            }
+        }
+    }
+
+out:
+    LeaveCriticalSection(&This->crst);
+    return;
+}
+
+static void DSCBuffer_starttimer(DSCImpl *prim)
+{
+    TIMECAPS time;
+    ALint refresh = FAKE_REFRESH_COUNT;
+    DWORD triggertime, res = DS_TIME_RES;
+
+    if (prim->timer_id)
+        return;
+
+    timeGetDevCaps(&time, sizeof(TIMECAPS));
+    triggertime = 1000 / refresh;
+    if (triggertime < time.wPeriodMin)
+        triggertime = time.wPeriodMin;
+    TRACE("Calling timer every %u ms for %i refreshes per second\n", triggertime, refresh);
+    if (res < time.wPeriodMin)
+        res = time.wPeriodMin;
+    if (timeBeginPeriod(res) == TIMERR_NOCANDO)
+        WARN("Could not set minimum resolution, don't expect sound\n");
+    prim->timer_res = res;
+    prim->timer_id = timeSetEvent(triggertime, res, DSCBuffer_timer, (DWORD_PTR)prim, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
+}
+
 static void DSCBuffer_Destroy(DSCBuffer *This)
 {
     if (This->dev)
@@ -99,6 +212,7 @@ static void DSCBuffer_Destroy(DSCBuffer *This)
     }
     if (This->parent)
         This->parent->buf = NULL;
+    HeapFree(GetProcessHeap(), 0, This->notify);
     HeapFree(GetProcessHeap(), 0, This->format);
     HeapFree(GetProcessHeap(), 0, This->buf);
     HeapFree(GetProcessHeap(), 0, This);
@@ -112,7 +226,7 @@ static HRESULT WINAPI DSCBuffer_QueryInterface(IDirectSoundCaptureBuffer8 *iface
     TRACE("(%p)->(%s,%p)\n", This, debugstr_guid(riid), ppv);
     *ppv = NULL;
     if (IsEqualIID(riid, &IID_IDirectSoundNotify))
-        FIXME("IDirectSoundNotify implemented\n");
+        *ppv = &This->lpNotVtbl;
     else if (IsEqualIID(riid, &IID_IUnknown)
              || IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer)
              || IsEqualIID(riid, &IID_IDirectSoundCaptureBuffer8))
@@ -140,7 +254,7 @@ static ULONG WINAPI DSCBuffer_Release(IDirectSoundCaptureBuffer8 *iface)
     EnterCriticalSection(crst);
     ref = InterlockedDecrement(&This->ref);
     TRACE("Reference count decremented to %i\n", ref);
-    if (!ref)
+    if (!ref && !This->not_ref)
         DSCBuffer_Destroy(This);
     LeaveCriticalSection(crst);
     return ref;
@@ -389,6 +503,7 @@ static HRESULT WINAPI DSCBuffer_Start(IDirectSoundCaptureBuffer8 *iface, DWORD f
     EnterCriticalSection(&This->parent->crst);
     if (!This->playing)
     {
+        DSCBuffer_starttimer(This->parent);
         This->playing = 1;
         alcCaptureStart(This->dev);
     }
@@ -405,6 +520,14 @@ static HRESULT WINAPI DSCBuffer_Stop(IDirectSoundCaptureBuffer8 *iface)
     EnterCriticalSection(&This->parent->crst);
     if (This->playing)
     {
+        DWORD i;
+
+        for (i = 0; i < This->nnotify; ++i)
+            if (This->notify[i].dwOffset == DSCBPN_OFFSET_STOP)
+            {
+                SetEvent(This->notify[i].hEventNotify);
+                break;
+            }
         This->playing = This->looping = 0;
         alcCaptureStop(This->dev);
     }
@@ -454,6 +577,102 @@ static const IDirectSoundCaptureBuffer8Vtbl DSCBuffer_Vtbl =
     DSCBuffer_GetFXStatus
 };
 
+static DSCBuffer *get_this_from_not(IDirectSoundNotify *iface)
+{
+    return (DSCBuffer*)((char*)iface - offsetof(DSCBuffer,lpNotVtbl));
+}
+
+static HRESULT WINAPI DSCBufferNot_QueryInterface(IDirectSoundNotify *iface, REFIID riid, void **ppv)
+{
+    DSCBuffer *This = get_this_from_not(iface);
+    return IDirectSoundCaptureBuffer_QueryInterface((IDirectSoundCaptureBuffer*)This, riid, ppv);
+}
+
+static ULONG WINAPI DSCBufferNot_AddRef(IDirectSoundNotify *iface)
+{
+    DSCBuffer *This = get_this_from_not(iface);
+    LONG ret;
+
+    ret = InterlockedIncrement(&This->not_ref);
+    TRACE("new refcount %d\n", ret);
+    return ret;
+}
+
+static ULONG WINAPI DSCBufferNot_Release(IDirectSoundNotify *iface)
+{
+    DSCBuffer *This = get_this_from_not(iface);
+    CRITICAL_SECTION *crst = &This->parent->crst;
+    LONG ret;
+
+    EnterCriticalSection(crst);
+    ret = InterlockedDecrement(&This->not_ref);
+    TRACE("new refcount %d\n", ret);
+    if (!ret && !This->ref)
+        DSCBuffer_Destroy(This);
+    LeaveCriticalSection(crst);
+    return ret;
+}
+
+static HRESULT WINAPI DSCBufferNot_SetNotificationPositions(IDirectSoundNotify *iface, DWORD count, const DSBPOSITIONNOTIFY *notifications)
+{
+    DSCBuffer *This = get_this_from_not(iface);
+    DSBPOSITIONNOTIFY *nots;
+    HRESULT hr;
+    DWORD state;
+
+    EnterCriticalSection(&This->parent->crst);
+    hr = DSERR_INVALIDPARAM;
+    if (count && !notifications)
+        goto out;
+
+    hr = DSCBuffer_GetStatus((IDirectSoundCaptureBuffer8*)This, &state);
+    if (FAILED(hr))
+        goto out;
+
+    hr = DSERR_INVALIDCALL;
+    if (state & DSCBSTATUS_CAPTURING)
+        goto out;
+
+    if (!count)
+    {
+        HeapFree(GetProcessHeap(), 0, This->notify);
+        This->notify = 0;
+        This->nnotify = 0;
+    }
+    else
+    {
+        DWORD i;
+        hr = DSERR_INVALIDPARAM;
+        for (i = 0; i < count; ++i)
+        {
+            if (notifications[i].dwOffset >= This->buf_size
+                && notifications[i].dwOffset != DSCBPN_OFFSET_STOP)
+                goto out;
+        }
+        hr = E_OUTOFMEMORY;
+        nots = HeapAlloc(GetProcessHeap(), 0, count*sizeof(*nots));
+        if (!nots)
+            goto out;
+        memcpy(nots, notifications, count*sizeof(*nots));
+        HeapFree(GetProcessHeap(), 0, This->notify);
+        This->notify = nots;
+        This->nnotify = count;
+        hr = S_OK;
+    }
+
+out:
+    LeaveCriticalSection(&This->parent->crst);
+    return hr;
+}
+
+static const IDirectSoundNotifyVtbl DSCNot_Vtbl =
+{
+    DSCBufferNot_QueryInterface,
+    DSCBufferNot_AddRef,
+    DSCBufferNot_Release,
+    DSCBufferNot_SetNotificationPositions
+};
+
 HRESULT DSOUND_CaptureCreate8(REFIID riid, IDirectSoundCapture8 **cap)
 {
     DSCImpl *This = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*This));
@@ -474,6 +693,11 @@ HRESULT DSOUND_CaptureCreate8(REFIID riid, IDirectSoundCapture8 **cap)
 
 static void DSCImpl_Destroy(DSCImpl *This)
 {
+    if (This->timer_id)
+    {
+        timeKillEvent(This->timer_id);
+        timeEndPeriod(This->timer_res);
+    }
     EnterCriticalSection(&This->crst);
     if (This->buf)
         DSCBuffer_Destroy(This->buf);
-- 
1.6.5.3




More information about the wine-patches mailing list