[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