[PATCH 06/13] winemmaudio.drv: Add support for capture

Maarten Lankhorst m.b.lankhorst at gmail.com
Wed Apr 21 06:35:20 CDT 2010


---
 dlls/winemmaudio.drv/Makefile.in          |    1 +
 dlls/winemmaudio.drv/capture.c            |  546 +++++++++++++++++++++++++++++
 dlls/winemmaudio.drv/winemmaudio.drv.spec |    2 +-
 3 files changed, 548 insertions(+), 1 deletions(-)
 create mode 100644 dlls/winemmaudio.drv/capture.c

diff --git a/dlls/winemmaudio.drv/Makefile.in b/dlls/winemmaudio.drv/Makefile.in
index 2bd455e..f608cff 100644
--- a/dlls/winemmaudio.drv/Makefile.in
+++ b/dlls/winemmaudio.drv/Makefile.in
@@ -6,6 +6,7 @@ MODULE    = winemmaudio.drv
 IMPORTS   = uuid winmm user32 kernel32 ole32 oleaut32
 
 C_SRCS = \
+	capture.c \
 	mmaudio.c \
 	render.c
 
diff --git a/dlls/winemmaudio.drv/capture.c b/dlls/winemmaudio.drv/capture.c
new file mode 100644
index 0000000..0634f2a
--- /dev/null
+++ b/dlls/winemmaudio.drv/capture.c
@@ -0,0 +1,546 @@
+/*
+ * Wine Driver for Windows CoreAudio Api, based on winecoreaudio.drv
+ *
+ * Copyright 1994 Martin Ayotte
+ * Copyright 1999 Eric Pouech (async playing in waveOut/waveIn)
+ * Copyright 2000 Eric Pouech (loops in waveOut)
+ * Copyright 2002 Chris Morgan (jack version of this file)
+ * Copyright 2005, 2006 Emmanuel Maillard
+ * Copyright 2010 Maarten Lankhorst 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 CINTERFACE
+#define NONAMELESSUNION
+
+#include <stdarg.h>
+#include <math.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winnls.h"
+#include "wingdi.h"
+#include "winerror.h"
+#include "mmddk.h"
+#include "mmreg.h"
+#include "dsound.h"
+#include "dsdriver.h"
+#include "ks.h"
+#include "ksmedia.h"
+
+#include "coreaudio.h"
+
+#include "wine/unicode.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+#include "wine/list.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+static DWORD wave_timerid;
+
+typedef struct waveimpl {
+    DWORD devid;
+    IMMDevice *dev;
+    WCHAR *friendlyname;
+    DWORD vol;
+    CRITICAL_SECTION crst;
+    struct list head;
+} waveimpl;
+
+typedef struct wavestreamimpl {
+    WAVEFORMATEX *wfx;
+
+    WAVEHDR *queue;
+    DWORD ofs, stopped, bytesplayed;
+
+    /* callback stuff */
+    DWORD_PTR cbflags, cbhandle, cbinstance;
+    HDRVR wavehandle;
+
+    IAudioClient *ac;
+    IAudioCaptureClient *cap;
+    struct list entry;
+} wavestreamimpl;
+
+static waveimpl *waveout_head;
+static DWORD waveout_count;
+
+static void widNotifyClient(wavestreamimpl *wwo, DWORD msg, DWORD_PTR par1, DWORD_PTR par2) {
+    if (wwo->cbflags == DCB_NULL)
+        return;
+
+    DriverCallback(wwo->cbhandle, wwo->cbflags, wwo->wavehandle,
+                   msg, wwo->cbinstance, par1, par2);
+}
+
+static void widNotifyDoneList(wavestreamimpl *wwo, LPWAVEHDR lpWaveHdr) {
+    while (lpWaveHdr) {
+        LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+
+        lpWaveHdr->lpNext = NULL;
+        lpWaveHdr->dwFlags = (lpWaveHdr->dwFlags & ~WHDR_INQUEUE) | WHDR_DONE;
+        widNotifyClient(wwo, WIM_DATA, (DWORD_PTR)lpWaveHdr, 0);
+
+        lpWaveHdr = lpNext;
+    }
+}
+
+static void CALLBACK widTick(UINT id, UINT msg, DWORD_PTR dwUser,
+                             DWORD_PTR dw1, DWORD_PTR dw2) {
+    DWORD x;
+    wavestreamimpl *cur;
+
+    for (x = 0; x < waveout_count; ++x) {
+        EnterCriticalSection(&waveout_head[x].crst);
+        LIST_FOR_EACH_ENTRY(cur, &waveout_head[x].head, wavestreamimpl, entry) {
+            WAVEHDR *begin = NULL, *end = NULL;
+            DWORD block = cur->wfx->nBlockAlign;
+
+            if (cur->stopped)
+                continue;
+
+            while (cur->queue) {
+                BYTE *data = NULL;
+                DWORD flags = 0, ofs = 0, pad = 0;
+                IAudioCaptureClient_GetBuffer(cur->cap, &data, &pad, &flags, NULL, NULL);
+                if (!pad)
+                    break;
+                pad *= block;
+
+                while (pad && cur->queue) {
+                    LPWAVEHDR hdr = cur->queue;
+                    DWORD avail = (hdr->dwBufferLength/block)*block - cur->ofs;
+                    if (avail > pad)
+                        avail = pad;
+
+                    memcpy(hdr->lpData + cur->ofs, data + ofs, avail);
+                    cur->ofs += avail;
+                    pad -= avail;
+                    ofs += avail;
+                    if (hdr->dwBufferLength - cur->ofs < block) {
+                        hdr->dwBytesRecorded = cur->ofs;
+                        cur->ofs = 0;
+                        cur->queue = hdr->lpNext;
+                        if (!begin)
+                            begin = hdr;
+                        end = hdr;
+                    }
+                }
+                IAudioCaptureClient_ReleaseBuffer(cur->cap, (ofs + pad)/block);
+                cur->bytesplayed += ofs;
+            }
+            if (begin) {
+                end->lpNext = NULL;
+                widNotifyDoneList(cur, begin);
+            }
+        }
+        LeaveCriticalSection(&waveout_head[x].crst);
+    }
+}
+
+static DWORD widGetDevCaps(waveimpl *dev, LPWAVEOUTCAPSW lpCaps, DWORD dwSize) {
+    WAVEOUTCAPSW caps;
+    TRACE("(%p, %p, %u)\n", dev, lpCaps, dwSize);
+
+    if (!lpCaps)
+        return MMSYSERR_NOTENABLED;
+
+    caps.wMid = MM_CREATIVE;
+    caps.wPid = MM_CREATIVE_SBP16_WAVEOUT;
+    caps.vDriverVersion = 0x0100;
+    lstrcpynW(caps.szPname, dev->friendlyname, sizeof(caps.szPname)/sizeof(*caps.szPname));
+    caps.dwFormats = 0xfffff;
+    caps.wChannels = 2;
+    caps.wReserved1 = 1;
+    caps.dwSupport = WAVECAPS_SAMPLEACCURATE|WAVECAPS_LRVOLUME|WAVECAPS_DIRECTSOUND;
+    memcpy(lpCaps, &caps, min(dwSize, sizeof(caps)));
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widOpen(waveimpl *dev, wavestreamimpl **pInstance, LPWAVEOPENDESC lpDesc, DWORD dwFlags) {
+    wavestreamimpl *wwo;
+    WAVEFORMATEX *wfx, *wfxout = NULL;
+    DWORD extra, ret;
+    IAudioClient *ac;
+    HRESULT hr;
+
+    TRACE("(%p, %p, %p, %08x)\n", dev, pInstance, lpDesc, dwFlags);
+    if (!lpDesc) {
+        WARN("Invalid Parameter!\n");
+        return MMSYSERR_INVALPARAM;
+    }
+    wfx = lpDesc->lpFormat;
+
+    TRACE("Format: tag=%04X nChannels=%d nSamplesPerSec=%d wBitsPerSample=%d\n",
+          wfx->wFormatTag, wfx->nChannels, wfx->nSamplesPerSec, wfx->wBitsPerSample);
+
+    hr = IMMDevice_Activate(dev->dev, &IID_IAudioClient, CLSCTX_INPROC_SERVER, NULL, (void**)&ac);
+    if (SUCCEEDED(hr))
+        hr = IAudioClient_IsFormatSupported(ac, AUDCLNT_SHAREMODE_SHARED, wfx, &wfxout);
+    if (FAILED(hr)) {
+        WARN("Failed to get format from audioclient: %08x\n", hr);
+        if (ac)
+            IAudioClient_Release(ac);
+        CoTaskMemFree(wfxout);
+        return WAVERR_BADFORMAT;
+    }
+
+    if (dwFlags & WAVE_FORMAT_QUERY) {
+        CoTaskMemFree(wfxout);
+        return MMSYSERR_NOERROR;
+    }
+    if (wfx->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+        extra = sizeof(WAVEFORMATEXTENSIBLE)-sizeof(WAVEFORMATEX);
+    else
+        extra = 0;
+
+    wwo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*wwo));
+    if (!wwo)
+        return MMSYSERR_NOMEM;
+
+    if (!wfxout) {
+        ret = MMSYSERR_NOMEM;
+        wwo->wfx = CoTaskMemAlloc(sizeof(*wwo)+extra);
+        if (!wwo->wfx)
+            goto error;
+
+        if (wfx->wFormatTag == WAVE_FORMAT_PCM)
+            memcpy(wwo->wfx, wfx, sizeof(PCMWAVEFORMAT));
+        else
+            memcpy(wwo->wfx, wfx, sizeof(*wwo)+extra);
+        wwo->wfx->cbSize = extra;
+        wwo->wfx->nBlockAlign = wfx->nChannels*wfx->wBitsPerSample/8;
+        wwo->wfx->nAvgBytesPerSec = wfx->nSamplesPerSec*wfx->nBlockAlign;
+    } else
+        wwo->wfx = wfxout;
+
+    wwo->ac = ac;
+    ret = WAVERR_BADFORMAT;
+    hr = IAudioClient_Initialize(ac, AUDCLNT_SHAREMODE_SHARED,
+                                 AUDCLNT_STREAMFLAGS_RATEADJUST,
+                                 500000, 0, wwo->wfx, NULL);
+    if (FAILED(hr)) {
+        WARN("Could not initialize audioclient: %08x\n", hr);
+        goto error;
+    }
+    hr = IAudioClient_GetService(ac, &IID_IAudioCaptureClient, (void**)&wwo->cap);
+    if (FAILED(hr)) {
+        WARN("Could not get capture client: %08x\n", hr);
+        goto error;
+    }
+    wwo->stopped = 1;
+    wwo->cbflags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+    wwo->cbhandle = lpDesc->dwCallback;
+    wwo->cbinstance = lpDesc->dwInstance;
+    wwo->wavehandle = (HDRVR)lpDesc->hWave;
+
+    EnterCriticalSection(&dev->crst);
+    list_add_before(&dev->head, &wwo->entry);
+    if (!wave_timerid) {
+        timeBeginPeriod(1);
+        wave_timerid = timeSetEvent(10, 1, widTick, 0L, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
+    }
+    LeaveCriticalSection(&dev->crst);
+    *pInstance = wwo;
+    TRACE("opened instance %p\n", wwo);
+
+    widNotifyClient(wwo, WIM_OPEN, 0L, 0L);
+    return MMSYSERR_NOERROR;
+
+error:
+    if (wwo->cap)
+        IAudioCaptureClient_Release(wwo->cap);
+    IAudioClient_Release(ac);
+    CoTaskMemFree(wwo->wfx);
+    HeapFree(GetProcessHeap(), 0, wwo);
+    return ret;
+}
+
+static DWORD widClose(waveimpl *dev, wavestreamimpl *wwo) {
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+    if (wwo->queue) {
+        LeaveCriticalSection(&dev->crst);
+        WARN("buffers still playing !\n");
+        return WAVERR_STILLPLAYING;
+    }
+
+    widNotifyClient(wwo, WIM_CLOSE, 0L, 0L);
+    list_remove(&wwo->entry);
+
+    IAudioCaptureClient_Release(wwo->cap);
+    IAudioClient_Release(wwo->ac);
+    CoTaskMemFree(wwo->wfx);
+    HeapFree(GetProcessHeap(), 0, wwo);
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widWrite(waveimpl *dev, wavestreamimpl *wwo, LPWAVEHDR hdr, DWORD dwSize) {
+    LPWAVEHDR *wh;
+
+    TRACE("(%p, %p, %p, %u, %08X)\n", dev, wwo, hdr, hdr->dwBufferLength, dwSize);
+
+    if (!hdr->lpData || !(hdr->dwFlags & WHDR_PREPARED)) {
+        TRACE("unprepared\n");
+        return WAVERR_UNPREPARED;
+    }
+
+    if (hdr->dwFlags & WHDR_INQUEUE) {
+        TRACE("still playing\n");
+        return WAVERR_STILLPLAYING;
+    }
+
+    hdr->dwFlags = (hdr->dwFlags & ~WHDR_DONE) | WHDR_INQUEUE;
+    hdr->lpNext = NULL;
+
+    EnterCriticalSection(&dev->crst);
+    if (!wwo->queue)
+        wwo->ofs = 0;
+
+    wh = &(wwo->queue);
+    while (*wh)
+        wh = &((*wh)->lpNext);
+    *wh = hdr;
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widStart(waveimpl *dev, wavestreamimpl *wwo) {
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+    wwo->stopped = 0;
+    IAudioClient_Start(wwo->ac);
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widStop(waveimpl *dev, wavestreamimpl *wwo) {
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+    wwo->stopped = 1;
+    IAudioClient_Stop(wwo->ac);
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widReset(waveimpl *dev, wavestreamimpl *wwo) {
+    LPWAVEHDR savedqueue;
+
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+
+    savedqueue = wwo->queue;
+    wwo->queue = NULL;
+    wwo->ofs = 0;
+    wwo->stopped = 1;
+    IAudioClient_Stop(wwo->ac);
+    IAudioClient_Reset(wwo->ac);
+
+    LeaveCriticalSection(&dev->crst);
+
+    widNotifyDoneList(wwo, savedqueue);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widGetPosition(waveimpl *dev, wavestreamimpl *wwo, LPMMTIME lpTime, DWORD uSize) {
+    DWORD val;
+
+    TRACE("(%p, %p, %p, %u)\n", dev, wwo, lpTime, uSize);
+
+    if (!lpTime)
+        return MMSYSERR_INVALPARAM;
+
+    EnterCriticalSection(&dev->crst);
+    val = wwo->bytesplayed;
+    LeaveCriticalSection(&dev->crst);
+
+    return bytes_to_mmtime(lpTime, val, wwo->wfx);
+}
+
+static DWORD widGetNumDevs(void) {
+    TRACE("\n");
+
+    return waveout_count;
+}
+
+static DWORD widDevInterfaceSize(waveimpl *dev, LPDWORD dwParam1) {
+    TRACE("(%p, %p)\n", dev, dwParam1);
+
+    *dwParam1 = (lstrlenW(dev->friendlyname)+1)*sizeof(*dev->friendlyname);
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widDevInterface(waveimpl *dev, PWCHAR dwParam1, DWORD dwParam2) {
+    DWORD size;
+
+    TRACE("\n");
+    widDevInterfaceSize(dev, &size);
+    if (dwParam2 >= size) {
+        lstrcpyW(dwParam1, dev->friendlyname);
+        return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/* To be removed */
+static DWORD widDsDesc(waveimpl *dev, PDSDRIVERDESC desc)
+{
+    TRACE("(%p, %p)\n", dev, desc);
+
+    WideCharToMultiByte(CP_ACP, 0, dev->friendlyname, -1,
+                        desc->szDesc, sizeof(desc->szDesc)-1, NULL, NULL);
+    desc->szDesc[sizeof(desc->szDesc)-1] = 0;
+    strcpy(desc->szDrvname, "winemmaudio.drv");
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD widInit(void) {
+    IMMDeviceCollection *col;
+    DWORD i = 0;
+
+    if (!devenum)
+        return 1;
+    IMMDeviceEnumerator_EnumAudioEndpoints(devenum, eCapture, DEVICE_STATE_ACTIVE, &col);
+    if (!col)
+        return 1;
+    IMMDeviceCollection_GetCount(col, &waveout_count);
+    waveout_head = HeapAlloc(GetProcessHeap(), 0, sizeof(*waveout_head) * waveout_count);
+
+    while (i < waveout_count) {
+        waveimpl *wave = waveout_head + i;
+        IPropertyStore *store;
+        PROPVARIANT pv = { VT_EMPTY };
+
+        IMMDeviceCollection_Item(col, i, &wave->dev);
+        if (!wave->dev)
+            break;
+        IMMDevice_OpenPropertyStore(wave->dev, STGM_READ, &store);
+        if (!store) {
+            IUnknown_Release(wave->dev);
+            break;
+        }
+        IPropertyStore_GetValue(store, (PROPERTYKEY*)&DEVPKEY_Device_FriendlyName, &pv);
+        IPropertyStore_Release(store);
+        wave->friendlyname = pv.u.pwszVal;
+        wave->devid = i++;
+        InitializeCriticalSection(&wave->crst);
+        wave->crst.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": waveimpl.crst");
+        list_init(&wave->head);
+    }
+    IMMDeviceCollection_Release(col);
+    waveout_count = i;
+    return 1;
+}
+
+static DWORD widExit(void) {
+    DWORD i;
+
+    if (wave_timerid) {
+        timeKillEvent(wave_timerid);
+        timeEndPeriod(1);
+    }
+
+    for (i = 0; i < waveout_count; ++i) {
+        waveimpl *wave = waveout_head + i;
+        wavestreamimpl *cur, *next;
+
+        LIST_FOR_EACH_ENTRY_SAFE(cur, next, &wave->head, wavestreamimpl, entry) {
+            widReset(wave, cur);
+            widClose(wave, cur);
+        }
+        CoTaskMemFree(wave->friendlyname);
+        IMMDevice_Release(wave->dev);
+        wave->crst.DebugInfo->Spare[0] = 0;
+        DeleteCriticalSection(&wave->crst);
+    }
+    HeapFree(GetProcessHeap(), 0, waveout_head);
+    waveout_count = 0;
+    return 1;
+}
+
+DWORD WINAPI widMessage(UINT wDevID, UINT wMsg, DWORD_PTR dwUser,
+                        DWORD_PTR dwParam1, DWORD_PTR dwParam2)
+{
+    wavestreamimpl *wwo = (wavestreamimpl*)dwUser;
+    waveimpl *dev = &waveout_head[wDevID];
+
+    TRACE("(%u, %04x, %p, %p, %p)\n",
+          wDevID, wMsg, (void*)dwUser, (void*)dwParam1, (void*)dwParam2);
+
+    switch (wMsg) {
+        case DRVM_INIT:
+            return widInit();
+        case DRVM_EXIT:
+            return widExit();
+        case DRVM_ENABLE:
+        case DRVM_DISABLE:
+            return TRUE;
+        case WIDM_GETNUMDEVS:
+            return widGetNumDevs();
+    }
+
+    if (wDevID >= waveout_count) {
+        ERR("Bad device id %u/%u\n", wDevID, waveout_count);
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    switch (wMsg) {
+        case WIDM_OPEN:
+            return widOpen(dev, (wavestreamimpl**)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
+        case WIDM_CLOSE:
+            return widClose(dev, wwo);
+        case WIDM_ADDBUFFER:
+            return widWrite(dev, wwo, (LPWAVEHDR)dwParam1, dwParam2);
+        case WIDM_GETPOS:
+            return widGetPosition(dev, wwo, (LPMMTIME)dwParam1, dwParam2);
+        case WIDM_GETDEVCAPS:
+            return widGetDevCaps(dev, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
+
+        case WIDM_START:
+            return widStart(dev, wwo);
+        case WIDM_STOP:
+            return widStop(dev, wwo);
+        case WIDM_RESET:
+            return widReset(dev, wwo);
+
+        case DRV_QUERYDEVICEINTERFACESIZE:
+            return widDevInterfaceSize(dev, (LPDWORD)dwParam1);
+        case DRV_QUERYDEVICEINTERFACE:
+            return widDevInterface(dev, (PWCHAR)dwParam1, dwParam2);
+
+        case DRV_QUERYDSOUNDDESC:
+            return widDsDesc(dev, (DSDRIVERDESC*)dwParam1);
+
+        default:
+            FIXME("unknown message %04x!\n", wMsg);
+        case WIDM_PREPARE:
+        case WIDM_UNPREPARE:
+        case DRV_QUERYDSOUNDIFACE:
+            return MMSYSERR_NOTSUPPORTED;
+    }
+}
diff --git a/dlls/winemmaudio.drv/winemmaudio.drv.spec b/dlls/winemmaudio.drv/winemmaudio.drv.spec
index 4f47ee6..f781d01 100644
--- a/dlls/winemmaudio.drv/winemmaudio.drv.spec
+++ b/dlls/winemmaudio.drv/winemmaudio.drv.spec
@@ -1,4 +1,4 @@
 @ stdcall -private DriverProc(long long long long long)
-#@ stdcall -private widMessage(long long long long long)
+@ stdcall -private widMessage(long long long long long)
 @ stdcall -private wodMessage(long long long long long)
 #@ stdcall -private mxdMessage(long long long long long)
-- 
1.7.0.4


--------------000003090704080204040709--



More information about the wine-patches mailing list