[PATCH 11/12] add initial mmdevapi based audio forwarder for winmm

Maarten Lankhorst m.b.lankhorst at gmail.com
Sun Mar 14 19:38:11 CDT 2010


---
 dlls/winemmaudio.drv/Makefile.in          |   14 +
 dlls/winemmaudio.drv/coreaudio.h          |   32 ++
 dlls/winemmaudio.drv/mmaudio.c            |  120 +++++
 dlls/winemmaudio.drv/render.c             |  671 +++++++++++++++++++++++++++++
 dlls/winemmaudio.drv/winemmaudio.drv.spec |    4 +
 5 files changed, 841 insertions(+), 0 deletions(-)
 create mode 100644 dlls/winemmaudio.drv/Makefile.in
 create mode 100644 dlls/winemmaudio.drv/coreaudio.h
 create mode 100644 dlls/winemmaudio.drv/mmaudio.c
 create mode 100644 dlls/winemmaudio.drv/render.c
 create mode 100644 dlls/winemmaudio.drv/winemmaudio.drv.spec

diff --git a/dlls/winemmaudio.drv/Makefile.in b/dlls/winemmaudio.drv/Makefile.in
new file mode 100644
index 0000000..2bd455e
--- /dev/null
+++ b/dlls/winemmaudio.drv/Makefile.in
@@ -0,0 +1,14 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = winemmaudio.drv
+IMPORTS   = uuid winmm user32 kernel32 ole32 oleaut32
+
+C_SRCS = \
+	mmaudio.c \
+	render.c
+
+ at MAKE_DLL_RULES@
+
+ at DEPENDENCIES@  # everything below this line is overwritten by make depend
diff --git a/dlls/winemmaudio.drv/coreaudio.h b/dlls/winemmaudio.drv/coreaudio.h
new file mode 100644
index 0000000..9797b0a
--- /dev/null
+++ b/dlls/winemmaudio.drv/coreaudio.h
@@ -0,0 +1,32 @@
+/* Definition for CoreAudio drivers : wine multimedia system
+ *
+ * Copyright 2005-2007 Emmanuel Maillard
+ *
+ * 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
+ */
+
+#ifndef __WINE_COREAUDIO_H
+#define __WINE_COREAUDIO_H
+
+#include "mmdeviceapi.h"
+#include "audioclient.h"
+#include "endpointvolume.h"
+#include "audiopolicy.h"
+#include "devpkey.h"
+
+extern IMMDeviceEnumerator *devenum;
+extern DWORD bytes_to_mmtime(MMTIME *time, DWORD pos, WAVEFORMATEX *wfx);
+
+#endif /* __WINE_COREAUDIO_H */
diff --git a/dlls/winemmaudio.drv/mmaudio.c b/dlls/winemmaudio.drv/mmaudio.c
new file mode 100644
index 0000000..be27ad2
--- /dev/null
+++ b/dlls/winemmaudio.drv/mmaudio.c
@@ -0,0 +1,120 @@
+/*
+ * Wine Driver for Windows Core Audio
+ *
+ * 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
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "ks.h"
+#include "ksmedia.h"
+
+#include "ole2.h"
+#include "initguid.h"
+
+#include "coreaudio.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mmaudio);
+
+IMMDeviceEnumerator *devenum = NULL;
+
+DWORD bytes_to_mmtime(MMTIME *time, DWORD pos, WAVEFORMATEX *wfx)
+{
+    TRACE("Position in bytes=%u\n", pos);
+
+    switch (time->wType) {
+    case TIME_SAMPLES:
+        time->u.sample = pos / wfx->nBlockAlign;
+        TRACE("TIME_SAMPLES=%u\n", time->u.sample);
+        break;
+    case TIME_MS:
+        time->u.ms = 1000.0 * pos / wfx->nAvgBytesPerSec;
+        TRACE("TIME_MS=%u\n", time->u.ms);
+        break;
+    case TIME_SMPTE: {
+        DWORD sec, frame;
+
+        pos /= wfx->nBlockAlign;
+        time->u.smpte.fps = 30;
+        pos += (wfx->nSamplesPerSec / time->u.smpte.fps) - 1;
+        sec = pos / wfx->nSamplesPerSec;
+
+        time->u.smpte.sec = sec % 60;
+        time->u.smpte.min = (sec / 60) % 60;
+        time->u.smpte.hour = sec / 3600;
+        frame = pos * time->u.smpte.fps / wfx->nSamplesPerSec;
+        time->u.smpte.frame = frame % time->u.smpte.fps;
+        TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
+              time->u.smpte.hour, time->u.smpte.min, time->u.smpte.sec, time->u.smpte.frame);
+        break;
+    }
+    default:
+        WARN("Format %d not supported, using TIME_BYTES !\n", time->wType);
+        time->wType = TIME_BYTES;
+        /* fall through */
+    case TIME_BYTES:
+        time->u.cb = pos;
+        TRACE("TIME_BYTES=%u\n", time->u.cb);
+        break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+LRESULT CALLBACK DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg, 
+                            LPARAM dwParam1, LPARAM dwParam2)
+{
+    HRESULT hr;
+
+    TRACE("(%08lX, %p, %04x, %08lX, %08lX)\n",
+          dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+
+    switch (wMsg) {
+        case DRV_LOAD: {
+            CoInitializeEx(NULL, COINIT_MULTITHREADED);
+            hr = CoCreateInstance(&CLSID_MMDeviceEnumerator, NULL,
+                                  CLSCTX_INPROC_SERVER,
+                                  &IID_IMMDeviceEnumerator, (void**)&devenum);
+            if (hr == S_OK)
+                return 1;
+            ERR("Could not create device enumerator: %08x\n", hr);
+            return 0;
+        }
+        case DRV_FREE:
+            if (devenum)
+                IMMDeviceEnumerator_Release(devenum);
+        case DRV_OPEN:
+        case DRV_CLOSE:
+        case DRV_ENABLE:
+        case DRV_DISABLE:
+            return 1;
+    }
+
+    return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+}
+
diff --git a/dlls/winemmaudio.drv/render.c b/dlls/winemmaudio.drv/render.c
new file mode 100644
index 0000000..a885166
--- /dev/null
+++ b/dlls/winemmaudio.drv/render.c
@@ -0,0 +1,671 @@
+/*
+ * 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, *loop;
+    DWORD ofs, numloops, stopped, bytesplayed;
+
+    /* callback stuff */
+    DWORD_PTR cbflags, cbhandle, cbinstance;
+    HDRVR wavehandle;
+
+    IAudioClient *ac;
+    IAudioClock *clock;
+    IAudioRenderClient *render;
+    IAudioStreamVolume *vol;
+    struct list entry;
+    DWORD cachevol;
+} wavestreamimpl;
+
+static waveimpl *waveout_head;
+static DWORD waveout_count;
+
+static void wodNotifyClient(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 wodNotifyDoneList(wavestreamimpl *wwo, LPWAVEHDR lpWaveHdr) {
+    while (lpWaveHdr) {
+        LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+
+        lpWaveHdr->lpNext = NULL;
+        lpWaveHdr->dwFlags = (lpWaveHdr->dwFlags & ~WHDR_INQUEUE) | WHDR_DONE;
+        wodNotifyClient(wwo, WOM_DONE, (DWORD_PTR)lpWaveHdr, 0);
+
+        lpWaveHdr = lpNext;
+    }
+}
+
+static void wodNext(wavestreamimpl *wwo, WAVEHDR **beginnotify, WAVEHDR **endnotify) {
+    LPWAVEHDR hdr;
+
+    wwo->ofs = 0;
+    if (wwo->loop && wwo->queue->dwFlags & WHDR_ENDLOOP) {
+        if (wwo->numloops-- > 1) {
+            wwo->queue = wwo->loop;
+            return;
+        }
+
+        if (!*beginnotify)
+            *beginnotify = wwo->loop;
+        wwo->loop = NULL;
+    }
+
+    hdr = wwo->queue;
+    if (!wwo->loop) {
+        if (!*beginnotify)
+            *beginnotify = hdr;
+        *endnotify = hdr;
+    }
+    wwo->queue = hdr->lpNext;
+
+    if (wwo->queue && wwo->queue->dwFlags & WHDR_BEGINLOOP) {
+        if (!wwo->loop) {
+            TRACE("Starting loop (%d) with %p\n", wwo->queue->dwLoops, wwo->queue);
+
+            wwo->loop = wwo->queue;
+            wwo->numloops = wwo->queue->dwLoops;
+        } else
+            WARN("Already in a loop. Discarding loop on this header (%p)\n", wwo->queue);
+    }
+}
+
+static void CALLBACK wodTick(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) {
+            DWORD size, pad = 0, avail;
+            WAVEHDR *begin = NULL, *end = NULL;
+            DWORD block = cur->wfx->nBlockAlign;
+
+            if (cur->stopped)
+                continue;
+            IAudioClient_GetBufferSize(cur->ac, &size);
+            IAudioClient_GetCurrentPadding(cur->ac, &pad);
+            pad = (size - pad)*block;
+
+            while (pad && cur->queue) {
+                BYTE *data = NULL;
+                avail = cur->queue->dwBufferLength - cur->ofs;
+                if (avail > pad)
+                    avail = pad;
+
+                IAudioRenderClient_GetBuffer(cur->render, avail/block, &data);
+                if (!data)
+                    break;
+                memcpy(data, cur->queue->lpData + cur->ofs, avail);
+                IAudioRenderClient_ReleaseBuffer(cur->render, avail/block, 0);
+                cur->ofs += avail;
+                pad -= avail;
+                if (cur->ofs == cur->queue->dwBufferLength)
+                    wodNext(cur, &begin, &end);
+                cur->bytesplayed += avail;
+            }
+            if (begin) {
+                end->lpNext = NULL;
+                wodNotifyDoneList(cur, begin);
+            }
+        }
+        LeaveCriticalSection(&waveout_head[x].crst);
+    }
+}
+
+static DWORD wodGetDevCaps(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_VOLUME
+                     |WAVECAPS_LRVOLUME|WAVECAPS_DIRECTSOUND;
+    memcpy(lpCaps, &caps, min(dwSize, sizeof(caps)));
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodOpen(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_IAudioClock, (void**)&wwo->clock);
+    if (FAILED(hr)) {
+        WARN("Could not get audio clock: %08x\n", hr);
+        goto error;
+    }
+    hr = IAudioClient_GetService(ac, &IID_IAudioRenderClient, (void**)&wwo->render);
+    if (FAILED(hr)) {
+        WARN("Could not get render client: %08x\n", hr);
+        goto error;
+    }
+    hr = IAudioClient_GetService(ac, &IID_IAudioStreamVolume, (void**)&wwo->vol);
+    if (FAILED(hr)) {
+        WARN("Could not get stream audio volume: %08x\n", hr);
+        /* Not fatal yet, wine doesn't support IAudioStreamVolume */
+    } else
+        FIXME("Succeeded? Tell maarten to remove !wwo->vol case\n");
+    wwo->cachevol = 0xffffffff;
+    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, wodTick, 0L, TIME_PERIODIC | TIME_KILL_SYNCHRONOUS);
+    }
+    IAudioClient_Start(wwo->ac);
+    LeaveCriticalSection(&dev->crst);
+    *pInstance = wwo;
+    TRACE("opened instance %p\n", wwo);
+
+    wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
+    return MMSYSERR_NOERROR;
+
+error:
+    if (wwo->vol)
+        IAudioStreamVolume_Release(wwo->vol);
+    if (wwo->render)
+        IAudioRenderClient_Release(wwo->render);
+    if (wwo->clock)
+        IAudioClock_Release(wwo->clock);
+    IAudioClient_Release(ac);
+    CoTaskMemFree(wwo->wfx);
+    HeapFree(GetProcessHeap(), 0, wwo);
+    return ret;
+}
+
+static DWORD wodClose(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;
+    }
+
+    wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
+    list_remove(&wwo->entry);
+
+    /* Should be unconditional when !wwo->vol is no longer ignorable */
+    if (wwo->vol)
+        IAudioStreamVolume_Release(wwo->vol);
+    IAudioRenderClient_Release(wwo->render);
+    IAudioClock_Release(wwo->clock);
+    IAudioClient_Release(wwo->ac);
+    CoTaskMemFree(wwo->wfx);
+    HeapFree(GetProcessHeap(), 0, wwo);
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodWrite(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 && hdr->dwFlags & WHDR_BEGINLOOP) {
+        wwo->loop = hdr;
+        wwo->numloops = hdr->dwLoops;
+    }
+    if (!wwo->queue)
+        wwo->ofs = 0;
+
+    wh = &(wwo->queue);
+    while (*wh)
+        wh = &((*wh)->lpNext);
+    *wh = hdr;
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodPause(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 wodRestart(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 wodReset(waveimpl *dev, wavestreamimpl *wwo) {
+    LPWAVEHDR savedqueue;
+
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+
+    if (wwo->loop)
+        savedqueue = wwo->loop;
+    else
+        savedqueue = wwo->queue;
+    wwo->queue = wwo->loop = NULL;
+    wwo->ofs = 0;
+    wwo->stopped = 1;
+    IAudioClient_Stop(wwo->ac);
+    IAudioClient_Reset(wwo->ac);
+
+    LeaveCriticalSection(&dev->crst);
+
+    wodNotifyDoneList(wwo, savedqueue);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodBreakLoop(waveimpl *dev, wavestreamimpl *wwo) {
+    TRACE("(%p, %p)\n", dev, wwo);
+
+    EnterCriticalSection(&dev->crst);
+    if (wwo->loop != NULL)
+        wwo->numloops = 1;
+    LeaveCriticalSection(&dev->crst);
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodGetPosition(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 wodGetVolume(waveimpl *dev, wavestreamimpl *wwo, LPDWORD lpdwVol) {
+    TRACE("(%p, %p, %p)\n", dev, wwo, lpdwVol);
+
+    if (wwo && wwo->vol) {
+        float vol[8] = { 1.0, 1.0 };
+
+        IAudioStreamVolume_GetAllVolumes(wwo->vol, wwo->wfx->nChannels, vol);
+        if (wwo->wfx->nChannels == 1)
+            vol[1] = vol[0];
+        *lpdwVol = (DWORD)pow(65536.f, vol[0]) - 1U;
+        *lpdwVol |= ((DWORD)pow(65536.f, vol[1]) - 1U)<<16U;
+
+        FIXME("Tell maarten to remove the !wwo->vol case, since the wwo->vol case works now\n");
+    } else if (wwo && !wwo->vol) {
+        *lpdwVol = wwo->cachevol;
+        WARN("stub: Wine misses code to support volume\n");
+    } else {
+        FIXME("Touch IChannelAudioVolume\n");
+        *lpdwVol = dev->vol;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodSetVolume(waveimpl *dev, wavestreamimpl *wwo, DWORD dwParam) {
+    TRACE("(%p, %p, %08x)\n", dev, wwo, dwParam);
+
+    if (wwo && wwo->vol) {
+        float vol[8];
+        IAudioStreamVolume_GetAllVolumes(wwo->vol, wwo->wfx->nChannels, vol);
+        vol[0] = LOWORD(dwParam);
+        vol[1] = HIWORD(dwParam);
+        vol[0] = log(vol[0]+1)/log(65536.f);
+        vol[1] = log(vol[0]+1)/log(65536.f);
+        IAudioStreamVolume_SetAllVolumes(wwo->vol, wwo->wfx->nChannels, vol);
+
+        FIXME("Tell maarten to remove the !wwo->vol case, since the wwo->vol case works now\n");
+    } else if (wwo && !wwo->vol) {
+        wwo->cachevol = dwParam;
+        WARN("stub: Wine misses code to support volume\n");
+    } else {
+        FIXME("Touch IChannelAudioVolume\n");
+        dev->vol = dwParam;
+    }
+
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodGetNumDevs(void) {
+    TRACE("\n");
+
+    return waveout_count;
+}
+
+static DWORD wodDevInterfaceSize(waveimpl *dev, LPDWORD dwParam1) {
+    TRACE("(%p, %p)\n", dev, dwParam1);
+
+    *dwParam1 = (lstrlenW(dev->friendlyname)+1)*sizeof(*dev->friendlyname);
+    return MMSYSERR_NOERROR;
+}
+
+static DWORD wodDevInterface(waveimpl *dev, PWCHAR dwParam1, DWORD dwParam2) {
+    DWORD size;
+
+    TRACE("\n");
+    wodDevInterfaceSize(dev, &size);
+    if (dwParam2 >= size) {
+        lstrcpyW(dwParam1, dev->friendlyname);
+        return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/* To be removed */
+static DWORD wodDsDesc(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 wodInit(void) {
+    IMMDeviceCollection *col;
+    DWORD i = 0;
+
+    if (!devenum)
+        return 1;
+    IMMDeviceEnumerator_EnumAudioEndpoints(devenum, eRender, 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 wodExit(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) {
+            wodReset(wave, cur);
+            wodClose(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 wodMessage(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 wodInit();
+        case DRVM_EXIT:
+            return wodExit();
+        case DRVM_ENABLE:
+        case DRVM_DISABLE:
+            return TRUE;
+        case WODM_GETNUMDEVS:
+            return wodGetNumDevs();
+    }
+
+    if (wDevID >= waveout_count) {
+        ERR("Bad device id %u/%u\n", wDevID, waveout_count);
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    switch (wMsg) {
+        case WODM_OPEN:
+            return wodOpen(dev, (wavestreamimpl**)dwUser, (LPWAVEOPENDESC)dwParam1, dwParam2);
+        case WODM_CLOSE:
+            return wodClose(dev, wwo);
+        case WODM_WRITE:
+            return wodWrite(dev, wwo, (LPWAVEHDR)dwParam1, dwParam2);
+        case WODM_PAUSE:
+            return wodPause(dev, wwo);
+        case WODM_GETPOS:
+            return wodGetPosition(dev, wwo, (LPMMTIME)dwParam1, dwParam2);
+        case WODM_BREAKLOOP:
+            return wodBreakLoop(dev, wwo);
+        case WODM_GETDEVCAPS:
+            return wodGetDevCaps(dev, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
+
+        case WODM_GETVOLUME:
+            return wodGetVolume(dev, wwo, (LPDWORD)dwParam1);
+        case WODM_SETVOLUME:
+            return wodSetVolume(dev, wwo, dwParam1);
+        case WODM_RESTART:
+            return wodRestart(dev, wwo);
+        case WODM_RESET:
+            return wodReset(dev, wwo);
+
+        case DRV_QUERYDEVICEINTERFACESIZE:
+            return wodDevInterfaceSize(dev, (LPDWORD)dwParam1);
+        case DRV_QUERYDEVICEINTERFACE:
+            return wodDevInterface(dev, (PWCHAR)dwParam1, dwParam2);
+
+        case DRV_QUERYDSOUNDDESC:
+            return wodDsDesc(dev, (DSDRIVERDESC*)dwParam1);
+
+        default:
+            FIXME("unknown message %04x!\n", wMsg);
+        case WODM_PREPARE:
+        case WODM_UNPREPARE:
+        case WODM_GETPITCH:
+        case WODM_SETPITCH:
+        case WODM_GETPLAYBACKRATE:
+        case WODM_SETPLAYBACKRATE:
+        case DRV_QUERYDSOUNDIFACE:
+            return MMSYSERR_NOTSUPPORTED;
+    }
+}
diff --git a/dlls/winemmaudio.drv/winemmaudio.drv.spec b/dlls/winemmaudio.drv/winemmaudio.drv.spec
new file mode 100644
index 0000000..4f47ee6
--- /dev/null
+++ b/dlls/winemmaudio.drv/winemmaudio.drv.spec
@@ -0,0 +1,4 @@
+@ stdcall -private DriverProc(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


--------------010209050002080608050705--



More information about the wine-patches mailing list