[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