Native NetBSD audio driver
Yorick Hardy
yh at metroweb.co.za
Mon Feb 23 14:29:04 CST 2004
Thanks for the input.
Here is the patch again, with the bug specified below fixed.
I definitely think it would be useful to integrate at least the
winenbsd and wineoss drivers somehow. However, I think
a separate winenbsd driver will have to do in the mean time, and
provides a starting point to compare the drivers in case someone
else is also interested.
Changelog: Add the winenbsd audio driver using the native NetBSD audio system.
On Sun, Feb 22, 2004 at 06:15:47PM +0100, Eric Pouech wrote:
> I really think we're starting to create a real mess for the audio
> drivers. It's always useful to have drivers access the HW at the lowest
> level, so we should support NetBSD.
> However, if the iface is rather close to OSS, I'd rather suggest we try
> to integrate all the drivers with the same features into a single piece
> of driver
>
> >+++ ./dlls/Makefile.in Sat Feb 21 23:43:35 2004
> >@@ -1745,6 +1749,7 @@
> > winmm/winejack/winejack.drv$(DLLEXT): winmm/winejack
> > msacm/winemp3/winemp3.acm$(DLLEXT): msacm/winemp3
> > winmm/winenas/winenas.drv$(DLLEXT): winmm/winenas
> >+winmm/winenas/winenbsd.drv$(DLLEXT): winmm/winenbsd
> ***
> this should be winenbsd
Kind regards,
--
Yorick Hardy
diff -urN /home/yorick/software/wine/wine/DEVELOPERS-HINTS ./DEVELOPERS-HINTS
--- /home/yorick/software/wine/wine/DEVELOPERS-HINTS Sat Feb 14 19:19:56 2004
+++ ./DEVELOPERS-HINTS Sat Feb 21 23:41:20 2004
@@ -135,6 +135,7 @@
winmm/wineaudioio/ - audioio audio driver
winmm/winejack/ - JACK audio server driver
winmm/winenas/ - NAS audio driver
+ winmm/winenbsd/ - NetBSD audio driver
winmm/wineoss/ - OSS audio driver
winnls/ - National Language Support
winsock/ - Sockets 2.0 (networking)
diff -urN /home/yorick/software/wine/wine/configure.ac ./configure.ac
--- /home/yorick/software/wine/wine/configure.ac Sat Feb 14 19:19:59 2004
+++ ./configure.ac Sun Feb 22 11:12:21 2004
@@ -631,6 +631,31 @@
[AUDIOIOLIBS="-laudioio"
AC_DEFINE(HAVE_LIBAUDIOIO, 1, [Define if you have libaudioIO])])])
+dnl **** Check for NetBSD audioio Sound System ****
+AC_CHECK_HEADERS(sys/audioio.h, break)
+
+AC_CACHE_CHECK([for NetBSD audioio Sound System],
+ ac_cv_c_netbsdsoundsystem,
+ AC_TRY_COMPILE([
+ #if defined(HAVE_SYS_AUDIOIO_H)
+ #include <sys/types.h>
+ #include <sys/audioio.h>
+ #endif
+ ],[
+
+/* check for one of the NetBSD audioio specific defines and typedef */
+#if !defined(AUDIO_GETINFO)
+#error No NetBSD audioio
+#endif
+audio_info_t ai;
+AUDIO_INITINFO(&ai);
+],ac_cv_c_netbsdsoundsystem="yes",ac_cv_c_netbsdsoundsystem="no"))
+
+if test "$ac_cv_c_netbsdsoundsystem" = "yes"
+then
+ AC_DEFINE(HAVE_NBSDAUDIOIO, 1, [Define to support NetBSD audioio])
+fi
+
dnl **** Check for capi4linux ****
AC_CHECK_HEADERS(capi20.h,[
@@ -1609,6 +1634,7 @@
dlls/winmm/wineaudioio/Makefile
dlls/winmm/winejack/Makefile
dlls/winmm/winenas/Makefile
+dlls/winmm/winenbsd/Makefile
dlls/winmm/wineoss/Makefile
dlls/winnls/Makefile
dlls/winsock/Makefile
diff -urN /home/yorick/software/wine/wine/dlls/Makefile.in ./dlls/Makefile.in
--- /home/yorick/software/wine/wine/dlls/Makefile.in Sat Feb 14 19:20:00 2004
+++ ./dlls/Makefile.in Sat Feb 21 23:43:35 2004
@@ -130,6 +130,7 @@
winmm/wineaudioio \
winmm/winejack \
winmm/winenas \
+ winmm/winenbsd \
winmm/wineoss \
winnls \
winsock \
@@ -764,6 +765,9 @@
winenas.drv$(DLLEXT): winmm/winenas/winenas.drv$(DLLEXT)
$(RM) $@ && $(LN_S) winmm/winenas/winenas.drv$(DLLEXT) $@
+winenbsd.drv$(DLLEXT): winmm/winenbsd/winenbsd.drv$(DLLEXT)
+ $(RM) $@ && $(LN_S) winmm/winenbsd/winenbsd.drv$(DLLEXT) $@
+
wineoss.drv$(DLLEXT): winmm/wineoss/wineoss.drv$(DLLEXT)
$(RM) $@ && $(LN_S) winmm/wineoss/wineoss.drv$(DLLEXT) $@
@@ -1745,6 +1749,7 @@
winmm/winejack/winejack.drv$(DLLEXT): winmm/winejack
msacm/winemp3/winemp3.acm$(DLLEXT): msacm/winemp3
winmm/winenas/winenas.drv$(DLLEXT): winmm/winenas
+winmm/winenbsd/winenbsd.drv$(DLLEXT): winmm/winenbsd
winmm/wineoss/wineoss.drv$(DLLEXT): winmm/wineoss
wineps/wineps.dll$(DLLEXT): wineps
wininet/wininet.dll$(DLLEXT): wininet
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/Makefile.in ./dlls/winmm/winenbsd/Makefile.in
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/Makefile.in Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/Makefile.in Sat Feb 21 23:57:46 2004
@@ -0,0 +1,16 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR = @srcdir@
+VPATH = @srcdir@
+MODULE = winenbsd.drv
+IMPORTS = winmm user32 kernel32
+EXTRALIBS = -ldxguid -luuid
+
+C_SRCS = \
+ audio.c \
+ mmaux.c \
+ nbsd.c
+
+ at MAKE_DLL_RULES@
+
+### Dependencies:
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/audio.c ./dlls/winmm/winenbsd/audio.c
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/audio.c Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/audio.c Sun Feb 22 12:23:22 2004
@@ -0,0 +1,4249 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+
+/*
+ * Wine Driver for NetBSD audioio
+ * Derived from the Wine OSS Sample Driver
+ *
+ * Copyright 1994 Martin Ayotte
+ * 1999 Eric Pouech (async playing in waveOut/waveIn)
+ * 2000 Eric Pouech (loops in waveOut)
+ * 2002 Eric Pouech (full duplex)
+ * 2004 Yorick Hardy (NetBSD audioio)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+/*
+ * FIXME:
+ * pause in waveOut does not work correctly in loop mode
+ * Direct Sound Capture driver does not work (not complete yet)
+ */
+
+/*#define EMULATE_SB16*/
+
+/* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
+#define USE_PIPE_SYNC
+
+/* an exact wodGetPosition is usually not worth the extra context switches,
+ * as we're going to have near fragment accuracy anyway */
+/* #define EXACT_WODPOSITION */
+
+#include "config.h"
+#include "wine/port.h"
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <errno.h>
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+#ifdef HAVE_SYS_MMAN_H
+# include <sys/mman.h>
+#endif
+#ifdef HAVE_SYS_POLL_H
+# include <sys/poll.h>
+#endif
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winerror.h"
+#include "wine/winuser16.h"
+#include "mmddk.h"
+#include "dsound.h"
+#include "dsdriver.h"
+#include "nbsd.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+/* Allow 1% deviation for sample rates (some ES137x cards) */
+#define NEAR_MATCH(rate1,rate2) (((100*((int)(rate1)-(int)(rate2)))/(rate1))==0)
+
+#ifdef HAVE_NBSDAUDIOIO
+
+#define MAX_WAVEDRV (6)
+
+/* state diagram for waveOut writing:
+ *
+ * +---------+-------------+---------------+---------------------------------+
+ * | state | function | event | new state |
+ * +---------+-------------+---------------+---------------------------------+
+ * | | open() | | STOPPED |
+ * | PAUSED | write() | | PAUSED |
+ * | STOPPED | write() | <thrd create> | PLAYING |
+ * | PLAYING | write() | HEADER | PLAYING |
+ * | (other) | write() | <error> | |
+ * | (any) | pause() | PAUSING | PAUSED |
+ * | PAUSED | restart() | RESTARTING | PLAYING (if no thrd => STOPPED) |
+ * | (any) | reset() | RESETTING | STOPPED |
+ * | (any) | close() | CLOSING | CLOSED |
+ * +---------+-------------+---------------+---------------------------------+
+ */
+
+/* states of the playing device */
+#define WINE_WS_PLAYING 0
+#define WINE_WS_PAUSED 1
+#define WINE_WS_STOPPED 2
+#define WINE_WS_CLOSED 3
+
+/* events to be send to device */
+enum win_wm_message {
+ WINE_WM_PAUSING = WM_USER + 1, WINE_WM_RESTARTING, WINE_WM_RESETTING, WINE_WM_HEADER,
+ WINE_WM_UPDATE, WINE_WM_BREAKLOOP, WINE_WM_CLOSING, WINE_WM_STARTING, WINE_WM_STOPPING
+};
+
+#ifdef USE_PIPE_SYNC
+#define SIGNAL_OMR(omr) do { int x = 0; write((omr)->msg_pipe[1], &x, sizeof(x)); } while (0)
+#define CLEAR_OMR(omr) do { int x = 0; read((omr)->msg_pipe[0], &x, sizeof(x)); } while (0)
+#define RESET_OMR(omr) do { } while (0)
+#define WAIT_OMR(omr, sleep) \
+ do { struct pollfd pfd; pfd.fd = (omr)->msg_pipe[0]; \
+ pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
+#else
+#define SIGNAL_OMR(omr) do { SetEvent((omr)->msg_event); } while (0)
+#define CLEAR_OMR(omr) do { } while (0)
+#define RESET_OMR(omr) do { ResetEvent((omr)->msg_event); } while (0)
+#define WAIT_OMR(omr, sleep) \
+ do { WaitForSingleObject((omr)->msg_event, sleep); } while (0)
+#endif
+
+typedef struct {
+ enum win_wm_message msg; /* message identifier */
+ DWORD param; /* parameter for this message */
+ HANDLE hEvent; /* if message is synchronous, handle of event for synchro */
+} NBSD_MSG;
+
+/* implement an in-process message ring for better performance
+ * (compared to passing thru the server)
+ * this ring will be used by the input (resp output) record (resp playback) routine
+ */
+#define NBSD_RING_BUFFER_INCREMENT 64
+typedef struct {
+ int ring_buffer_size;
+ NBSD_MSG * messages;
+ int msg_tosave;
+ int msg_toget;
+#ifdef USE_PIPE_SYNC
+ int msg_pipe[2];
+#else
+ HANDLE msg_event;
+#endif
+ CRITICAL_SECTION msg_crst;
+} NBSD_MSG_RING;
+
+typedef struct tagNBSD_DEVICE {
+ char dev_name[32];
+ char mixer_name[32];
+ unsigned open_count;
+ WAVEOUTCAPSA out_caps;
+ WAVEINCAPSA in_caps;
+ DWORD in_caps_support;
+ unsigned open_access;
+ int fd;
+ DWORD owner_tid;
+ int sample_rate;
+ int stereo;
+ int encoding;
+ int precision;
+ unsigned audio_fragment;
+ BOOL full_duplex;
+ BOOL bOutputEnabled;
+ BOOL bInputEnabled;
+ DSDRIVERDESC ds_desc;
+ DSDRIVERCAPS ds_caps;
+ DSCDRIVERCAPS dsc_caps;
+ GUID ds_guid;
+ GUID dsc_guid;
+} NBSD_DEVICE;
+
+static NBSD_DEVICE NBSD_Devices[MAX_WAVEDRV];
+
+typedef struct {
+ NBSD_DEVICE* nbsdDev;
+ volatile int state; /* one of the WINE_WS_ manifest constants */
+ WAVEOPENDESC waveDesc;
+ WORD wFlags;
+ PCMWAVEFORMAT format;
+
+ /* driver information */
+ DWORD dwFragmentSize; /* size of buffer fragment */
+ DWORD dwBufferSize; /* size of whole buffer in bytes */
+ LPWAVEHDR lpQueuePtr; /* start of queued WAVEHDRs (waiting to be notified) */
+ LPWAVEHDR lpPlayPtr; /* start of not yet fully played buffers */
+ DWORD dwPartialOffset; /* Offset of not yet written bytes in lpPlayPtr */
+
+ LPWAVEHDR lpLoopPtr; /* pointer of first buffer in loop, if any */
+ DWORD dwLoops; /* private copy of loop counter */
+
+ DWORD dwPlayedTotal; /* number of bytes actually played since opening */
+ DWORD dwWrittenTotal; /* number of bytes written to buffer since opening */
+
+ /* synchronization stuff */
+ HANDLE hStartUpEvent;
+ HANDLE hThread;
+ DWORD dwThreadID;
+ NBSD_MSG_RING msgRing;
+} WINE_WAVEOUT;
+
+typedef struct {
+ NBSD_DEVICE* nbsdDev;
+ volatile int state;
+ DWORD dwFragmentSize;
+ WAVEOPENDESC waveDesc;
+ WORD wFlags;
+ PCMWAVEFORMAT format;
+ LPWAVEHDR lpQueuePtr;
+ DWORD dwTotalRecorded;
+
+ /* synchronization stuff */
+ HANDLE hThread;
+ DWORD dwThreadID;
+ HANDLE hStartUpEvent;
+ NBSD_MSG_RING msgRing;
+} WINE_WAVEIN;
+
+static WINE_WAVEOUT WOutDev [MAX_WAVEDRV];
+static WINE_WAVEIN WInDev [MAX_WAVEDRV];
+static unsigned numOutDev;
+static unsigned numInDev;
+
+static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
+static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv);
+static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
+static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc);
+static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid);
+static DWORD widDsGuid(UINT wDevID, LPGUID pGuid);
+
+/* These strings used only for tracing */
+static const char *wodPlayerCmdString[] = {
+ "WINE_WM_PAUSING",
+ "WINE_WM_RESTARTING",
+ "WINE_WM_RESETTING",
+ "WINE_WM_HEADER",
+ "WINE_WM_UPDATE",
+ "WINE_WM_BREAKLOOP",
+ "WINE_WM_CLOSING",
+ "WINE_WM_STARTING",
+ "WINE_WM_STOPPING",
+};
+
+static DWORD wdDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
+{
+ TRACE("(%u, %p)\n", wDevID, dwParam1);
+
+ *dwParam1 = MultiByteToWideChar(CP_ACP, 0, NBSD_Devices[wDevID].dev_name, -1,
+ NULL, 0 ) * sizeof(WCHAR);
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD wdDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
+{
+ if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, NBSD_Devices[wDevID].dev_name, -1,
+ NULL, 0 ) * sizeof(WCHAR))
+ {
+ MultiByteToWideChar(CP_ACP, 0, NBSD_Devices[wDevID].dev_name, -1,
+ dwParam1, dwParam2 / sizeof(WCHAR));
+ return MMSYSERR_NOERROR;
+ }
+
+ return MMSYSERR_INVALPARAM;
+}
+
+/*======================================================================*
+ * Low level WAVE implementation *
+ *======================================================================*/
+
+/******************************************************************
+ * NBSD_RawOpenDevice
+ *
+ * Low level device opening (from values stored in nbsdDev)
+ */
+static DWORD NBSD_RawOpenDevice(NBSD_DEVICE* nbsdDev, int strict_format)
+{
+ int fd, rc;
+ audio_info_t info;
+ TRACE("(%p,%d)\n",nbsdDev,strict_format);
+
+ if ((fd = open(nbsdDev->dev_name, nbsdDev->open_access|O_NDELAY, 0)) == -1)
+ {
+ WARN("Couldn't open %s (%s)\n", nbsdDev->dev_name, strerror(errno));
+ return (errno == EBUSY) ? MMSYSERR_ALLOCATED : MMSYSERR_ERROR;
+ }
+ fcntl(fd, F_SETFD, 1); /* set close on exec flag */
+ /* turn full duplex on if it has been requested */
+ if (nbsdDev->open_access == O_RDWR && nbsdDev->full_duplex) {
+ rc = 1;
+ rc = ioctl(fd, AUDIO_SETFD, &rc);
+ if (rc != 0) {
+ ERR("ioctl(%s, AUDIO_SETFD) failed (%s)\n", nbsdDev->dev_name, strerror(errno));
+ close(fd);
+ return MMSYSERR_ERROR;
+ }
+ }
+
+ AUDIO_INITINFO(&info);
+
+ if (nbsdDev->open_access == O_RDWR && nbsdDev->full_duplex)
+ info.mode = AUMODE_PLAY | AUMODE_RECORD | AUMODE_PLAY_ALL;
+ else if (nbsdDev->open_access == O_WRONLY)
+ info.mode = AUMODE_PLAY | AUMODE_PLAY_ALL;
+ else if (nbsdDev->open_access == O_RDONLY)
+ info.mode = AUMODE_RECORD | AUMODE_PLAY_ALL;
+
+ TRACE("blocksize=%d\n", nbsdDev->audio_fragment);
+
+ if (nbsdDev->audio_fragment)
+ info.blocksize = nbsdDev->audio_fragment;
+
+ if (nbsdDev->open_access == O_WRONLY || nbsdDev->open_access == O_RDWR) {
+ if (nbsdDev->encoding>=0) info.play.encoding = nbsdDev->encoding;
+ if (nbsdDev->precision>=0) info.play.precision = nbsdDev->precision;
+ if (nbsdDev->stereo>=0) info.play.channels = (nbsdDev->stereo) ? 2 : 1;
+ if (nbsdDev->sample_rate>=0) info.play.sample_rate = nbsdDev->sample_rate;
+ }
+
+ if (nbsdDev->open_access == O_RDONLY || nbsdDev->open_access == O_RDWR) {
+ if (nbsdDev->encoding>=0) info.record.encoding = nbsdDev->encoding;
+ if (nbsdDev->precision>=0) info.record.precision = nbsdDev->precision;
+ if (nbsdDev->stereo>=0) info.record.channels = (nbsdDev->stereo) ? 2 : 1;
+ if (nbsdDev->sample_rate>=0) info.record.sample_rate = nbsdDev->sample_rate;
+ }
+
+ if (ioctl(fd, AUDIO_SETINFO, &info) == -1) {
+ close(fd);
+ if (errno == EINVAL) return WAVERR_BADFORMAT;
+ return MMSYSERR_ERROR;
+ }
+
+ if (ioctl(fd, AUDIO_GETINFO, &info) == -1) {
+ close(fd);
+ return MMSYSERR_ERROR;
+ }
+
+ nbsdDev->fd = fd;
+
+ nbsdDev->bOutputEnabled = ! info.play.pause;
+ nbsdDev->bInputEnabled = ! info.record.pause;
+
+ TRACE("NBSD_RawOpenDevice Success\n");
+ return MMSYSERR_NOERROR;
+}
+
+/******************************************************************
+ * NBSD_OpenDevice
+ */
+static DWORD NBSD_OpenDevice(NBSD_DEVICE* nbsdDev, unsigned req_access,
+ int* frag, int strict_format,
+ int sample_rate, int stereo, int enc, int precision)
+{
+ DWORD ret;
+ TRACE("(%p,%u,%p,%d,%d,%d,%d,%d)\n",nbsdDev,req_access,frag,strict_format,
+ sample_rate,stereo,enc,precision);
+
+ if (nbsdDev->full_duplex && (req_access == O_RDONLY || req_access == O_WRONLY))
+ req_access = O_RDWR;
+
+ /* FIXME: this should be protected, and it also contains a race with NBSD_CloseDevice */
+ if (nbsdDev->open_count == 0)
+ {
+ if (access(nbsdDev->dev_name, 0) != 0) return MMSYSERR_NODRIVER;
+
+ nbsdDev->audio_fragment = (frag) ? *frag : 0;
+ nbsdDev->sample_rate = sample_rate;
+ nbsdDev->stereo = stereo;
+ nbsdDev->encoding = enc;
+ nbsdDev->precision = precision;
+ nbsdDev->open_access = req_access;
+ nbsdDev->owner_tid = GetCurrentThreadId();
+
+ if ((ret = NBSD_RawOpenDevice(nbsdDev,strict_format)) != MMSYSERR_NOERROR) return ret;
+ }
+ else
+ {
+ /* check we really open with the same parameters */
+ if (nbsdDev->open_access != req_access)
+ {
+ ERR("FullDuplex: Mismatch in access. Your sound device is not full duplex capable.\n");
+ return WAVERR_BADFORMAT;
+ }
+
+ /* check if the audio parameters are the same */
+ if (nbsdDev->sample_rate != sample_rate ||
+ nbsdDev->stereo != stereo ||
+ nbsdDev->encoding != enc ||
+ nbsdDev->precision != precision)
+ {
+ /* This is not a fatal error because MSACM might do the remapping */
+ WARN("FullDuplex: mismatch in PCM parameters for input and output\n"
+ "audioio doesn't allow us different parameters\n"
+ "audio_frag(%x/%x) sample_rate(%d/%d) stereo(%d/%d)\n"
+ "encoding(%d/%d) precision(%d,%d)\n",
+ nbsdDev->audio_fragment, frag ? *frag : 0,
+ nbsdDev->sample_rate, sample_rate,
+ nbsdDev->stereo, stereo,
+ nbsdDev->encoding, enc,
+ nbsdDev->precision, precision);
+ return WAVERR_BADFORMAT;
+ }
+ /* check if the fragment sizes are the same */
+ if (nbsdDev->audio_fragment != (frag ? *frag : 0) ) {
+ ERR("FullDuplex: Playback and Capture hardware acceleration levels are different.\n"
+ "Use: \"HardwareAcceleration\" = \"Emulation\" in the [dsound] section of your config file.\n");
+ return WAVERR_BADFORMAT;
+ }
+ if (GetCurrentThreadId() != nbsdDev->owner_tid)
+ {
+ WARN("Another thread is trying to access audio...\n");
+ return MMSYSERR_ERROR;
+ }
+ }
+
+ nbsdDev->open_count++;
+
+ return MMSYSERR_NOERROR;
+}
+
+/******************************************************************
+ * NBSD_CloseDevice
+ *
+ *
+ */
+static void NBSD_CloseDevice(NBSD_DEVICE* nbsdDev)
+{
+ TRACE("(%p)\n",nbsdDev);
+ if (nbsdDev->open_count>0) {
+ nbsdDev->open_count--;
+ } else {
+ WARN("NBSD_CloseDevice called too many times\n");
+ }
+ if (nbsdDev->open_count == 0)
+ {
+ /* reset the device before we close it in case it is in a bad state */
+ ioctl(nbsdDev->fd, AUDIO_FLUSH);
+ close(nbsdDev->fd);
+ }
+}
+
+/******************************************************************
+ * NBSD_ResetDevice
+ *
+ * Resets the device.
+ * FIXME: This causes problems when doing full duplex so we really
+ * only reset when not doing full duplex. We need to do this better
+ * someday.
+ */
+static DWORD NBSD_ResetDevice(NBSD_DEVICE* nbsdDev)
+{
+ DWORD ret = MMSYSERR_NOERROR;
+ int old_fd = nbsdDev->fd;
+ TRACE("(%p)\n", nbsdDev);
+
+ if (nbsdDev->open_count == 1) {
+ if (ioctl(nbsdDev->fd, AUDIO_FLUSH) == -1)
+ {
+ perror("ioctl AUDIO_FLUSH");
+ return -1;
+ }
+ close(nbsdDev->fd);
+ ret = NBSD_RawOpenDevice(nbsdDev, 1);
+ TRACE("Changing fd from %d to %d\n", old_fd, nbsdDev->fd);
+ } else
+ WARN("Not resetting device because it is in full duplex mode!\n");
+
+ return ret;
+}
+
+const static int win_std_nbsd_fmts[2]=
+ {AUDIO_ENCODING_ULINEAR_LE,AUDIO_ENCODING_SLINEAR_LE};
+const static int win_std_rates[5]={96000,48000,44100,22050,11025};
+const static int win_std_formats[2][2][5]=
+ {{{WAVE_FORMAT_96M08, WAVE_FORMAT_48M08, WAVE_FORMAT_4M08,
+ WAVE_FORMAT_2M08, WAVE_FORMAT_1M08},
+ {WAVE_FORMAT_96S08, WAVE_FORMAT_48S08, WAVE_FORMAT_4S08,
+ WAVE_FORMAT_2S08, WAVE_FORMAT_1S08}},
+ {{WAVE_FORMAT_96M16, WAVE_FORMAT_48M16, WAVE_FORMAT_4M16,
+ WAVE_FORMAT_2M16, WAVE_FORMAT_1M16},
+ {WAVE_FORMAT_96S16, WAVE_FORMAT_48S16, WAVE_FORMAT_4S16,
+ WAVE_FORMAT_2S16, WAVE_FORMAT_1S16}},
+ };
+
+/******************************************************************
+ * NBSD_WaveOutInit
+ *
+ *
+ */
+static BOOL NBSD_WaveOutInit(NBSD_DEVICE* nbsdDev)
+{
+ int rc,arg;
+ int f,c,r;
+ audio_info_t info;
+ audio_device_t dev;
+
+ TRACE("(%p) %s\n", nbsdDev, nbsdDev->dev_name);
+
+ if (NBSD_OpenDevice(nbsdDev, O_WRONLY, NULL, 0,-1,-1,-1,-1) != 0) return FALSE;
+ ioctl(nbsdDev->fd, AUDIO_FLUSH);
+
+ if (ioctl(nbsdDev->fd, AUDIO_GETDEV, &dev) >= 0) {
+ strncpy(nbsdDev->ds_desc.szDesc, dev.name, sizeof(dev.name));
+ strcpy(nbsdDev->ds_desc.szDrvName, "winenbsd.drv");
+ strncpy(nbsdDev->out_caps.szPname, dev.config, sizeof(dev.config));
+ TRACE("%s\n", nbsdDev->ds_desc.szDesc);
+ } else {
+ ERR("%s: can't read info!\n", nbsdDev->dev_name);
+ NBSD_CloseDevice(nbsdDev);
+ return FALSE;
+ }
+
+ /* FIXME: some programs compare this string against the content of the
+ * registry for MM drivers. The names have to match in order for the
+ * program to work (e.g. MS win9x mplayer.exe)
+ */
+#ifdef EMULATE_SB16
+ nbsdDev->out_caps.wMid = 0x0002;
+ nbsdDev->out_caps.wPid = 0x0104;
+ strcpy(nbsdDev->out_caps.szPname, "SB16 Wave Out");
+#else
+ nbsdDev->out_caps.wMid = 0x00FF; /* Manufac ID */
+ nbsdDev->out_caps.wPid = 0x0001; /* Product ID */
+#endif
+ nbsdDev->out_caps.vDriverVersion = 0x0100;
+ nbsdDev->out_caps.wChannels = 1;
+ nbsdDev->out_caps.dwFormats = 0x00000000;
+ nbsdDev->out_caps.wReserved1 = 0;
+ nbsdDev->out_caps.dwSupport = WAVECAPS_VOLUME;
+
+ /* direct sound caps */
+ nbsdDev->ds_caps.dwFlags = 0;
+ nbsdDev->ds_caps.dwPrimaryBuffers = 1;
+ nbsdDev->ds_caps.dwMinSecondarySampleRate = DSBFREQUENCY_MIN;
+ nbsdDev->ds_caps.dwMaxSecondarySampleRate = DSBFREQUENCY_MAX;
+
+
+ for (f=0;f<2;f++) {
+ for (c=1;c<=2;c++) {
+ for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
+ AUDIO_INITINFO(&info);
+ info.play.sample_rate = win_std_rates[r];
+ info.play.channels = c;
+ info.play.precision = (f + 1) * 8;
+ info.play.encoding = win_std_nbsd_fmts[f];
+
+ rc = ioctl(nbsdDev->fd, AUDIO_SETINFO, &info);
+ if (rc==0) rc = ioctl(nbsdDev->fd, AUDIO_GETINFO, &info);
+ if (rc==0 && NEAR_MATCH(info.play.sample_rate,win_std_rates[r])) {
+ if (f == 0)
+ nbsdDev->ds_caps.dwFlags |= DSCAPS_PRIMARY8BIT;
+ else if (f == 1)
+ nbsdDev->ds_caps.dwFlags |= DSCAPS_PRIMARY16BIT;
+ if (c == 0) {
+ nbsdDev->ds_caps.dwFlags |= DSCAPS_PRIMARYMONO;
+ } else if (c == 1) {
+ nbsdDev->out_caps.wChannels = 2;
+ nbsdDev->out_caps.dwSupport |= WAVECAPS_LRVOLUME;
+ nbsdDev->ds_caps.dwFlags |= DSCAPS_PRIMARYSTEREO;
+ }
+ nbsdDev->out_caps.dwFormats |= win_std_formats[f][c-1][r];
+ }
+ }
+ }
+ }
+
+ nbsdDev->out_caps.dwSupport |= WAVECAPS_SAMPLEACCURATE;
+ if (ioctl(nbsdDev->fd, AUDIO_GETPROPS, &arg) == 0) {
+ TRACE("audioio dsp out caps=%08X\n", arg);
+
+ /* well, might as well use the DirectSound cap flag for something */
+ if (arg & AUDIO_PROP_MMAP) {
+ nbsdDev->out_caps.dwSupport |= WAVECAPS_DIRECTSOUND;
+ } else {
+ nbsdDev->ds_caps.dwFlags |= DSCAPS_EMULDRIVER;
+ }
+ }
+ NBSD_CloseDevice(nbsdDev);
+ TRACE("out dwFormats = %08lX, dwSupport = %08lX\n",
+ nbsdDev->out_caps.dwFormats, nbsdDev->out_caps.dwSupport);
+ return TRUE;
+}
+
+/******************************************************************
+ * NBSD_WaveInInit
+ *
+ *
+ */
+static BOOL NBSD_WaveInInit(NBSD_DEVICE* nbsdDev)
+{
+ int rc,arg;
+ int f,c,r;
+ audio_info_t info;
+ audio_device_t dev;
+
+ TRACE("(%p) %s\n", nbsdDev, nbsdDev->dev_name);
+
+ if (NBSD_OpenDevice(nbsdDev, O_RDONLY, NULL, 0,-1,-1,-1,-1) != 0) return FALSE;
+ ioctl(nbsdDev->fd, AUDIO_FLUSH);
+
+ if (ioctl(nbsdDev->fd, AUDIO_GETDEV, &dev) >= 0) {
+ strncpy(nbsdDev->in_caps.szPname, dev.name, sizeof(dev.name));
+ TRACE("%s\n", nbsdDev->ds_desc.szDesc);
+ } else {
+ ERR("%s: can't read info!\n", nbsdDev->mixer_name);
+ NBSD_CloseDevice(nbsdDev);
+ return FALSE;
+ }
+
+ /* See comment in NBSD_WaveOutInit */
+#ifdef EMULATE_SB16
+ nbsdDev->in_caps.wMid = 0x0002;
+ nbsdDev->in_caps.wPid = 0x0004;
+ strcpy(nbsdDev->in_caps.szPname, "SB16 Wave In");
+#else
+ nbsdDev->in_caps.wMid = 0x00FF; /* Manufac ID */
+ nbsdDev->in_caps.wPid = 0x0001; /* Product ID */
+#endif
+ nbsdDev->in_caps.dwFormats = 0x00000000;
+ nbsdDev->in_caps.wChannels = 1;
+ nbsdDev->in_caps.wReserved1 = 0;
+
+ /* direct sound caps */
+ nbsdDev->dsc_caps.dwSize = sizeof(nbsdDev->dsc_caps);
+ nbsdDev->dsc_caps.dwFlags = 0;
+ nbsdDev->dsc_caps.dwFormats = 0x00000000;
+ nbsdDev->dsc_caps.dwChannels = 1;
+
+ /* See the comment in NBSD_WaveOutInit */
+ for (f=0;f<2;f++) {
+ for (c=1;c<=2;c++) {
+ for (r=0;r<sizeof(win_std_rates)/sizeof(*win_std_rates);r++) {
+ AUDIO_INITINFO(&info);
+ info.record.sample_rate = win_std_rates[r];
+ info.record.channels = c;
+ info.record.precision = (f + 1) * 8;
+ info.record.encoding = win_std_nbsd_fmts[f];
+
+ rc = ioctl(nbsdDev->fd, AUDIO_SETINFO, &info);
+ if (rc==0) rc = ioctl(nbsdDev->fd, AUDIO_GETINFO, &info);
+ if (rc==0 && NEAR_MATCH(info.record.sample_rate,win_std_rates[r])) {
+ if (c == 1) {
+ nbsdDev->in_caps.wChannels = 2;
+ nbsdDev->dsc_caps.dwChannels = 2;
+ }
+ nbsdDev->in_caps.dwFormats |= win_std_formats[f][c-1][r];
+ nbsdDev->dsc_caps.dwFormats |= win_std_formats[f][c-1][r];
+ }
+ }
+ }
+ }
+
+ nbsdDev->in_caps_support |= WAVECAPS_SAMPLEACCURATE;
+ if (ioctl(nbsdDev->fd, AUDIO_GETPROPS, &arg) == 0) {
+ TRACE("audioio dsp in caps=%08X\n", arg);
+ /* FIXME: enable the next statement if you want to work on the driver */
+#if 0
+ nbsdDev->in_caps_support |= WAVECAPS_DIRECTSOUND;
+#endif
+ }
+ NBSD_CloseDevice(nbsdDev);
+ TRACE("in dwFormats = %08lX\n", nbsdDev->in_caps.dwFormats);
+ return TRUE;
+}
+
+/******************************************************************
+ * NBSD_WaveFullDuplexInit
+ *
+ *
+ */
+static void NBSD_WaveFullDuplexInit(NBSD_DEVICE* nbsdDev)
+{
+ int caps;
+ TRACE("(%p)\n",nbsdDev);
+
+ if (NBSD_OpenDevice(nbsdDev, O_RDWR, NULL, 0,-1,-1,-1,-1) != 0) return;
+ if (ioctl(nbsdDev->fd, AUDIO_GETPROPS, &caps) == 0)
+ {
+ nbsdDev->full_duplex = (caps & AUDIO_PROP_FULLDUPLEX);
+ }
+ NBSD_CloseDevice(nbsdDev);
+}
+
+#define INIT_GUID(guid, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8) \
+ guid.Data1 = l; guid.Data2 = w1; guid.Data3 = w2; \
+ guid.Data4[0] = b1; guid.Data4[1] = b2; guid.Data4[2] = b3; \
+ guid.Data4[3] = b4; guid.Data4[4] = b5; guid.Data4[5] = b6; \
+ guid.Data4[6] = b7; guid.Data4[7] = b8;
+/******************************************************************
+ * NBSD_WaveInit
+ *
+ * Initialize internal structures from audioio information
+ */
+LONG NBSD_WaveInit(void)
+{
+ int i;
+ TRACE("()\n");
+
+ for (i = 0; i < MAX_WAVEDRV; ++i)
+ {
+ if (i == 0) {
+ sprintf((char *)NBSD_Devices[i].dev_name, "/dev/audio");
+ sprintf((char *)NBSD_Devices[i].mixer_name, "/dev/mixer");
+ } else {
+ sprintf((char *)NBSD_Devices[i].dev_name, "/dev/audio%d", i);
+ sprintf((char *)NBSD_Devices[i].mixer_name, "/dev/mixer%d", i);
+ }
+
+ INIT_GUID(NBSD_Devices[i].ds_guid, 0xbd6dd71a, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i);
+ INIT_GUID(NBSD_Devices[i].dsc_guid, 0xbd6dd71b, 0x3deb, 0x11d1, 0xb1, 0x71, 0x00, 0xc0, 0x4f, 0xc2, 0x00, 0x00 + i);
+
+ }
+
+ /* start with output devices */
+ for (i = 0; i < MAX_WAVEDRV; ++i)
+ {
+ if (NBSD_WaveOutInit(&NBSD_Devices[i]))
+ {
+ WOutDev[numOutDev].state = WINE_WS_CLOSED;
+ WOutDev[numOutDev].nbsdDev = &NBSD_Devices[i];
+ numOutDev++;
+ }
+ }
+
+ /* then do input devices */
+ for (i = 0; i < MAX_WAVEDRV; ++i)
+ {
+ if (NBSD_WaveInInit(&NBSD_Devices[i]))
+ {
+ WInDev[numInDev].state = WINE_WS_CLOSED;
+ WInDev[numInDev].nbsdDev = &NBSD_Devices[i];
+ numInDev++;
+ }
+ }
+
+ /* finish with the full duplex bits */
+ for (i = 0; i < MAX_WAVEDRV; i++)
+ NBSD_WaveFullDuplexInit(&NBSD_Devices[i]);
+
+ return 0;
+}
+
+/******************************************************************
+ * NBSD_InitRingMessage
+ *
+ * Initialize the ring of messages for passing between driver's caller and playback/record
+ * thread
+ */
+static int NBSD_InitRingMessage(NBSD_MSG_RING* omr)
+{
+ omr->msg_toget = 0;
+ omr->msg_tosave = 0;
+#ifdef USE_PIPE_SYNC
+ if (pipe(omr->msg_pipe) < 0) {
+ omr->msg_pipe[0] = -1;
+ omr->msg_pipe[1] = -1;
+ ERR("could not create pipe, error=%s\n", strerror(errno));
+ }
+#else
+ omr->msg_event = CreateEventA(NULL, FALSE, FALSE, NULL);
+#endif
+ omr->ring_buffer_size = NBSD_RING_BUFFER_INCREMENT;
+ omr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,omr->ring_buffer_size * sizeof(NBSD_MSG));
+ InitializeCriticalSection(&omr->msg_crst);
+ return 0;
+}
+
+/******************************************************************
+ * NBSD_DestroyRingMessage
+ *
+ */
+static int NBSD_DestroyRingMessage(NBSD_MSG_RING* omr)
+{
+#ifdef USE_PIPE_SYNC
+ close(omr->msg_pipe[0]);
+ close(omr->msg_pipe[1]);
+#else
+ CloseHandle(omr->msg_event);
+#endif
+ HeapFree(GetProcessHeap(),0,omr->messages);
+ DeleteCriticalSection(&omr->msg_crst);
+ return 0;
+}
+
+/******************************************************************
+ * NBSD_AddRingMessage
+ *
+ * Inserts a new message into the ring (should be called from DriverProc derivated routines)
+ */
+static int NBSD_AddRingMessage(NBSD_MSG_RING* omr, enum win_wm_message msg, DWORD param, BOOL wait)
+{
+ HANDLE hEvent = INVALID_HANDLE_VALUE;
+
+ EnterCriticalSection(&omr->msg_crst);
+ if ((omr->msg_toget == ((omr->msg_tosave + 1) % omr->ring_buffer_size)))
+ {
+ int old_ring_buffer_size = omr->ring_buffer_size;
+ omr->ring_buffer_size += NBSD_RING_BUFFER_INCREMENT;
+ TRACE("omr->ring_buffer_size=%d\n",omr->ring_buffer_size);
+ omr->messages = HeapReAlloc(GetProcessHeap(),0,omr->messages, omr->ring_buffer_size * sizeof(NBSD_MSG));
+ /* Now we need to rearrange the ring buffer so that the new
+ buffers just allocated are in between omr->msg_tosave and
+ omr->msg_toget.
+ */
+ if (omr->msg_tosave < omr->msg_toget)
+ {
+ memmove(&(omr->messages[omr->msg_toget + NBSD_RING_BUFFER_INCREMENT]),
+ &(omr->messages[omr->msg_toget]),
+ sizeof(NBSD_MSG)*(old_ring_buffer_size - omr->msg_toget)
+ );
+ omr->msg_toget += NBSD_RING_BUFFER_INCREMENT;
+ }
+ }
+ if (wait)
+ {
+ hEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+ if (hEvent == INVALID_HANDLE_VALUE)
+ {
+ ERR("can't create event !?\n");
+ LeaveCriticalSection(&omr->msg_crst);
+ return 0;
+ }
+ if (omr->msg_toget != omr->msg_tosave && omr->messages[omr->msg_toget].msg != WINE_WM_HEADER)
+ FIXME("two fast messages in the queue!!!!\n");
+
+ /* fast messages have to be added at the start of the queue */
+ omr->msg_toget = (omr->msg_toget + omr->ring_buffer_size - 1) % omr->ring_buffer_size;
+
+ omr->messages[omr->msg_toget].msg = msg;
+ omr->messages[omr->msg_toget].param = param;
+ omr->messages[omr->msg_toget].hEvent = hEvent;
+ }
+ else
+ {
+ omr->messages[omr->msg_tosave].msg = msg;
+ omr->messages[omr->msg_tosave].param = param;
+ omr->messages[omr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
+ omr->msg_tosave = (omr->msg_tosave + 1) % omr->ring_buffer_size;
+ }
+ LeaveCriticalSection(&omr->msg_crst);
+ /* signal a new message */
+ SIGNAL_OMR(omr);
+ if (wait)
+ {
+ /* wait for playback/record thread to have processed the message */
+ WaitForSingleObject(hEvent, INFINITE);
+ CloseHandle(hEvent);
+ }
+ return 1;
+}
+
+/******************************************************************
+ * NBSD_RetrieveRingMessage
+ *
+ * Get a message from the ring. Should be called by the playback/record thread.
+ */
+static int NBSD_RetrieveRingMessage(NBSD_MSG_RING* omr,
+ enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
+{
+ EnterCriticalSection(&omr->msg_crst);
+
+ if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
+ {
+ LeaveCriticalSection(&omr->msg_crst);
+ return 0;
+ }
+
+ *msg = omr->messages[omr->msg_toget].msg;
+ omr->messages[omr->msg_toget].msg = 0;
+ *param = omr->messages[omr->msg_toget].param;
+ *hEvent = omr->messages[omr->msg_toget].hEvent;
+ omr->msg_toget = (omr->msg_toget + 1) % omr->ring_buffer_size;
+ CLEAR_OMR(omr);
+ LeaveCriticalSection(&omr->msg_crst);
+ return 1;
+}
+
+/******************************************************************
+ * NBSD_PeekRingMessage
+ *
+ * Peek at a message from the ring but do not remove it.
+ * Should be called by the playback/record thread.
+ */
+static int NBSD_PeekRingMessage(NBSD_MSG_RING* omr,
+ enum win_wm_message *msg,
+ DWORD *param, HANDLE *hEvent)
+{
+ EnterCriticalSection(&omr->msg_crst);
+
+ if (omr->msg_toget == omr->msg_tosave) /* buffer empty ? */
+ {
+ LeaveCriticalSection(&omr->msg_crst);
+ return 0;
+ }
+
+ *msg = omr->messages[omr->msg_toget].msg;
+ *param = omr->messages[omr->msg_toget].param;
+ *hEvent = omr->messages[omr->msg_toget].hEvent;
+ LeaveCriticalSection(&omr->msg_crst);
+ return 1;
+}
+
+/*======================================================================*
+ * Low level WAVE OUT implementation *
+ *======================================================================*/
+
+/**************************************************************************
+ * wodNotifyClient [internal]
+ */
+static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
+ wMsg == WOM_OPEN ? "WOM_OPEN" : wMsg == WOM_CLOSE ? "WOM_CLOSE" :
+ wMsg == WOM_DONE ? "WOM_DONE" : "Unknown", dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case WOM_OPEN:
+ case WOM_CLOSE:
+ case WOM_DONE:
+ if (wwo->wFlags != DCB_NULL &&
+ !DriverCallback(wwo->waveDesc.dwCallback, wwo->wFlags,
+ (HDRVR)wwo->waveDesc.hWave, wMsg,
+ wwo->waveDesc.dwInstance, dwParam1, dwParam2)) {
+ WARN("can't notify client !\n");
+ return MMSYSERR_ERROR;
+ }
+ break;
+ default:
+ FIXME("Unknown callback message %u\n", wMsg);
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodUpdatePlayedTotal [internal]
+ *
+ */
+static BOOL wodUpdatePlayedTotal(WINE_WAVEOUT* wwo, audio_info_t* info)
+{
+ audio_info_t dspspace;
+ if (!info) info = &dspspace;
+
+ if (ioctl(wwo->nbsdDev->fd, AUDIO_GETINFO, info) < 0) {
+ ERR("ioctl(%s, AUDIO_GETINFO) failed (%s)\n", wwo->nbsdDev->dev_name, strerror(errno));
+ return FALSE;
+ }
+ wwo->dwPlayedTotal = wwo->dwWrittenTotal - info->play.seek;
+ return TRUE;
+}
+
+/**************************************************************************
+ * wodPlayer_BeginWaveHdr [internal]
+ *
+ * Makes the specified lpWaveHdr the currently playing wave header.
+ * If the specified wave header is a begin loop and we're not already in
+ * a loop, setup the loop.
+ */
+static void wodPlayer_BeginWaveHdr(WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
+{
+ wwo->lpPlayPtr = lpWaveHdr;
+
+ if (!lpWaveHdr) return;
+
+ if (lpWaveHdr->dwFlags & WHDR_BEGINLOOP) {
+ if (wwo->lpLoopPtr) {
+ WARN("Already in a loop. Discarding loop on this header (%p)\n", lpWaveHdr);
+ } else {
+ TRACE("Starting loop (%ldx) with %p\n", lpWaveHdr->dwLoops, lpWaveHdr);
+ wwo->lpLoopPtr = lpWaveHdr;
+ /* Windows does not touch WAVEHDR.dwLoops,
+ * so we need to make an internal copy */
+ wwo->dwLoops = lpWaveHdr->dwLoops;
+ }
+ }
+ wwo->dwPartialOffset = 0;
+}
+
+/**************************************************************************
+ * wodPlayer_PlayPtrNext [internal]
+ *
+ * Advance the play pointer to the next waveheader, looping if required.
+ */
+static LPWAVEHDR wodPlayer_PlayPtrNext(WINE_WAVEOUT* wwo)
+{
+ LPWAVEHDR lpWaveHdr = wwo->lpPlayPtr;
+
+ wwo->dwPartialOffset = 0;
+ if ((lpWaveHdr->dwFlags & WHDR_ENDLOOP) && wwo->lpLoopPtr) {
+ /* We're at the end of a loop, loop if required */
+ if (--wwo->dwLoops > 0) {
+ wwo->lpPlayPtr = wwo->lpLoopPtr;
+ } else {
+ /* Handle overlapping loops correctly */
+ if (wwo->lpLoopPtr != lpWaveHdr && (lpWaveHdr->dwFlags & WHDR_BEGINLOOP)) {
+ FIXME("Correctly handled case ? (ending loop buffer also starts a new loop)\n");
+ /* shall we consider the END flag for the closing loop or for
+ * the opening one or for both ???
+ * code assumes for closing loop only
+ */
+ } else {
+ lpWaveHdr = lpWaveHdr->lpNext;
+ }
+ wwo->lpLoopPtr = NULL;
+ wodPlayer_BeginWaveHdr(wwo, lpWaveHdr);
+ }
+ } else {
+ /* We're not in a loop. Advance to the next wave header */
+ wodPlayer_BeginWaveHdr(wwo, lpWaveHdr = lpWaveHdr->lpNext);
+ }
+
+ return lpWaveHdr;
+}
+
+/**************************************************************************
+ * wodPlayer_DSPWait [internal]
+ * Returns the number of milliseconds to wait for the DSP buffer to write
+ * one fragment.
+ */
+static DWORD wodPlayer_DSPWait(const WINE_WAVEOUT *wwo)
+{
+ /* time for one fragment to be played */
+ return wwo->dwFragmentSize * 1000 / wwo->format.wf.nAvgBytesPerSec;
+}
+
+/**************************************************************************
+ * wodPlayer_NotifyWait [internal]
+ * Returns the number of milliseconds to wait before attempting to notify
+ * completion of the specified wavehdr.
+ * This is based on the number of bytes remaining to be written in the
+ * wave.
+ */
+static DWORD wodPlayer_NotifyWait(const WINE_WAVEOUT* wwo, LPWAVEHDR lpWaveHdr)
+{
+ DWORD dwMillis;
+
+ if (lpWaveHdr->reserved < wwo->dwPlayedTotal) {
+ dwMillis = 1;
+ } else {
+ dwMillis = (lpWaveHdr->reserved - wwo->dwPlayedTotal) * 1000 / wwo->format.wf.nAvgBytesPerSec;
+ if (!dwMillis) dwMillis = 1;
+ }
+
+ return dwMillis;
+}
+
+
+/**************************************************************************
+ * wodPlayer_WriteMaxFrags [internal]
+ * Writes the maximum number of bytes possible to the DSP and returns
+ * TRUE iff the current playPtr has been fully played
+ */
+static BOOL wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
+{
+ DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
+ DWORD toWrite = min(dwLength, *bytes);
+ int written;
+ BOOL ret = FALSE;
+
+ TRACE("Writing wavehdr %p.%lu[%lu]/%lu\n",
+ wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength, toWrite);
+
+ if (toWrite > 0)
+ {
+ written = write(wwo->nbsdDev->fd, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
+ if (written <= 0) return FALSE;
+ }
+ else
+ written = 0;
+
+ if (written >= dwLength) {
+ /* If we wrote all current wavehdr, skip to the next one */
+ wodPlayer_PlayPtrNext(wwo);
+ ret = TRUE;
+ } else {
+ /* Remove the amount written */
+ wwo->dwPartialOffset += written;
+ }
+ *bytes -= written;
+ wwo->dwWrittenTotal += written;
+ TRACE("dwWrittenTotal=%lu\n", wwo->dwWrittenTotal);
+ return ret;
+}
+
+
+/**************************************************************************
+ * wodPlayer_NotifyCompletions [internal]
+ *
+ * Notifies and remove from queue all wavehdrs which have been played to
+ * the speaker (ie. they have cleared the buffer). If force is true,
+ * we notify all wavehdrs and remove them all from the queue even if they
+ * are unplayed or part of a loop.
+ */
+static DWORD wodPlayer_NotifyCompletions(WINE_WAVEOUT* wwo, BOOL force)
+{
+ LPWAVEHDR lpWaveHdr;
+
+ /* Start from lpQueuePtr and keep notifying until:
+ * - we hit an unwritten wavehdr
+ * - we hit the beginning of a running loop
+ * - we hit a wavehdr which hasn't finished playing
+ */
+ while ((lpWaveHdr = wwo->lpQueuePtr) &&
+ (force ||
+ (lpWaveHdr != wwo->lpPlayPtr &&
+ lpWaveHdr != wwo->lpLoopPtr &&
+ lpWaveHdr->reserved <= wwo->dwPlayedTotal))) {
+
+ wwo->lpQueuePtr = lpWaveHdr->lpNext;
+
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ wodNotifyClient(wwo, WOM_DONE, (DWORD)lpWaveHdr, 0);
+ }
+ return (lpWaveHdr && lpWaveHdr != wwo->lpPlayPtr && lpWaveHdr != wwo->lpLoopPtr) ?
+ wodPlayer_NotifyWait(wwo, lpWaveHdr) : INFINITE;
+}
+
+/**************************************************************************
+ * wodPlayer_Reset [internal]
+ *
+ * wodPlayer helper. Resets current output stream.
+ */
+static void wodPlayer_Reset(WINE_WAVEOUT* wwo, BOOL reset)
+{
+ wodUpdatePlayedTotal(wwo, NULL);
+ /* updates current notify list */
+ wodPlayer_NotifyCompletions(wwo, FALSE);
+
+ /* flush all possible output */
+ if (NBSD_ResetDevice(wwo->nbsdDev) != MMSYSERR_NOERROR)
+ {
+ wwo->hThread = 0;
+ wwo->state = WINE_WS_STOPPED;
+ ExitThread(-1);
+ }
+
+ if (reset) {
+ enum win_wm_message msg;
+ DWORD param;
+ HANDLE ev;
+
+ /* remove any buffer */
+ wodPlayer_NotifyCompletions(wwo, TRUE);
+
+ wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
+ wwo->state = WINE_WS_STOPPED;
+ wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
+ /* Clear partial wavehdr */
+ wwo->dwPartialOffset = 0;
+
+ /* remove any existing message in the ring */
+ EnterCriticalSection(&wwo->msgRing.msg_crst);
+ /* return all pending headers in queue */
+ while (NBSD_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev))
+ {
+ if (msg != WINE_WM_HEADER)
+ {
+ FIXME("shouldn't have headers left\n");
+ SetEvent(ev);
+ continue;
+ }
+ ((LPWAVEHDR)param)->dwFlags &= ~WHDR_INQUEUE;
+ ((LPWAVEHDR)param)->dwFlags |= WHDR_DONE;
+
+ wodNotifyClient(wwo, WOM_DONE, param, 0);
+ }
+ RESET_OMR(&wwo->msgRing);
+ LeaveCriticalSection(&wwo->msgRing.msg_crst);
+ } else {
+ if (wwo->lpLoopPtr) {
+ /* complicated case, not handled yet (could imply modifying the loop counter */
+ FIXME("Pausing while in loop isn't correctly handled yet, except strange results\n");
+ wwo->lpPlayPtr = wwo->lpLoopPtr;
+ wwo->dwPartialOffset = 0;
+ wwo->dwWrittenTotal = wwo->dwPlayedTotal; /* this is wrong !!! */
+ } else {
+ LPWAVEHDR ptr;
+ DWORD sz = wwo->dwPartialOffset;
+
+ /* reset all the data as if we had written only up to lpPlayedTotal bytes */
+ /* compute the max size playable from lpQueuePtr */
+ for (ptr = wwo->lpQueuePtr; ptr != wwo->lpPlayPtr; ptr = ptr->lpNext) {
+ sz += ptr->dwBufferLength;
+ }
+ /* because the reset lpPlayPtr will be lpQueuePtr */
+ if (wwo->dwWrittenTotal > wwo->dwPlayedTotal + sz) ERR("grin\n");
+ wwo->dwPartialOffset = sz - (wwo->dwWrittenTotal - wwo->dwPlayedTotal);
+ wwo->dwWrittenTotal = wwo->dwPlayedTotal;
+ wwo->lpPlayPtr = wwo->lpQueuePtr;
+ }
+ wwo->state = WINE_WS_PAUSED;
+ }
+}
+
+/**************************************************************************
+ * wodPlayer_ProcessMessages [internal]
+ */
+static void wodPlayer_ProcessMessages(WINE_WAVEOUT* wwo)
+{
+ LPWAVEHDR lpWaveHdr;
+ enum win_wm_message msg;
+ DWORD param;
+ HANDLE ev;
+
+ while (NBSD_RetrieveRingMessage(&wwo->msgRing, &msg, ¶m, &ev)) {
+ TRACE("Received %s %lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
+ switch (msg) {
+ case WINE_WM_PAUSING:
+ wodPlayer_Reset(wwo, FALSE);
+ SetEvent(ev);
+ break;
+ case WINE_WM_RESTARTING:
+ if (wwo->state == WINE_WS_PAUSED)
+ {
+ wwo->state = WINE_WS_PLAYING;
+ }
+ SetEvent(ev);
+ break;
+ case WINE_WM_HEADER:
+ lpWaveHdr = (LPWAVEHDR)param;
+
+ /* insert buffer at the end of queue */
+ {
+ LPWAVEHDR* wh;
+ for (wh = &(wwo->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
+ *wh = lpWaveHdr;
+ }
+ if (!wwo->lpPlayPtr)
+ wodPlayer_BeginWaveHdr(wwo,lpWaveHdr);
+ if (wwo->state == WINE_WS_STOPPED)
+ wwo->state = WINE_WS_PLAYING;
+ break;
+ case WINE_WM_RESETTING:
+ wodPlayer_Reset(wwo, TRUE);
+ SetEvent(ev);
+ break;
+ case WINE_WM_UPDATE:
+ wodUpdatePlayedTotal(wwo, NULL);
+ SetEvent(ev);
+ break;
+ case WINE_WM_BREAKLOOP:
+ if (wwo->state == WINE_WS_PLAYING && wwo->lpLoopPtr != NULL) {
+ /* ensure exit at end of current loop */
+ wwo->dwLoops = 1;
+ }
+ SetEvent(ev);
+ break;
+ case WINE_WM_CLOSING:
+ /* sanity check: this should not happen since the device must have been reset before */
+ if (wwo->lpQueuePtr || wwo->lpPlayPtr) ERR("out of sync\n");
+ wwo->hThread = 0;
+ wwo->state = WINE_WS_CLOSED;
+ SetEvent(ev);
+ ExitThread(0);
+ /* shouldn't go here */
+ default:
+ FIXME("unknown message %d\n", msg);
+ break;
+ }
+ }
+}
+
+/**************************************************************************
+ * wodPlayer_FeedDSP [internal]
+ * Feed as much sound data as we can into the DSP and return the number of
+ * milliseconds before it will be necessary to feed the DSP again.
+ */
+static DWORD wodPlayer_FeedDSP(WINE_WAVEOUT* wwo)
+{
+ audio_info_t dspspace;
+ DWORD availInQ;
+ int fragments;
+
+ wodUpdatePlayedTotal(wwo, &dspspace);
+ availInQ = dspspace.hiwat * dspspace.blocksize - dspspace.play.seek;
+ if (availInQ < 0) availInQ = 0;
+ fragments = availInQ / dspspace.blocksize;
+ TRACE("fragments=%d/%d, fragsize=%d, bytes=%d\n",
+ fragments, dspspace.hiwat, dspspace.blocksize, (int) availInQ);
+
+ /* input queue empty and output buffer with less than one fragment to play
+ * actually some cards do not play the fragment before the last if this one is partially feed
+ * so we need to test for full the availability of 2 fragments
+ */
+ if (!wwo->lpPlayPtr && wwo->dwBufferSize < availInQ + 2 * wwo->dwFragmentSize) {
+ TRACE("Run out of wavehdr:s...\n");
+ return INFINITE;
+ }
+
+ /* no more room... no need to try to feed */
+ if (fragments != 0) {
+ /* Feed from partial wavehdr */
+ if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0) {
+ wodPlayer_WriteMaxFrags(wwo, &availInQ);
+ }
+
+ /* Feed wavehdrs until we run out of wavehdrs or DSP space */
+ if (wwo->dwPartialOffset == 0 && wwo->lpPlayPtr) {
+ do {
+ TRACE("Setting time to elapse for %p to %lu\n",
+ wwo->lpPlayPtr, wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength);
+ /* note the value that dwPlayedTotal will return when this wave finishes playing */
+ wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
+ } while (wodPlayer_WriteMaxFrags(wwo, &availInQ) && wwo->lpPlayPtr && availInQ > 0);
+ }
+ }
+
+ /* FIXME("%d bytes available in audio buffer\n", (int)availInQ); */
+ return wodPlayer_DSPWait(wwo);
+}
+
+
+/**************************************************************************
+ * wodPlayer [internal]
+ */
+static DWORD CALLBACK wodPlayer(LPVOID pmt)
+{
+ WORD uDevID = (DWORD)pmt;
+ WINE_WAVEOUT* wwo = (WINE_WAVEOUT*)&WOutDev[uDevID];
+ DWORD dwNextFeedTime = INFINITE; /* Time before DSP needs feeding */
+ DWORD dwNextNotifyTime = INFINITE; /* Time before next wave completion */
+ DWORD dwSleepTime;
+
+ wwo->state = WINE_WS_STOPPED;
+ SetEvent(wwo->hStartUpEvent);
+
+ for (;;) {
+ /** Wait for the shortest time before an action is required. If there
+ * are no pending actions, wait forever for a command.
+ */
+ dwSleepTime = min(dwNextFeedTime, dwNextNotifyTime);
+ TRACE("waiting %lums (%lu,%lu)\n", dwSleepTime, dwNextFeedTime, dwNextNotifyTime);
+ WAIT_OMR(&wwo->msgRing, dwSleepTime);
+ wodPlayer_ProcessMessages(wwo);
+ if (wwo->state == WINE_WS_PLAYING) {
+ dwNextFeedTime = wodPlayer_FeedDSP(wwo);
+ dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
+ if (dwNextFeedTime == INFINITE) {
+ /* FeedDSP ran out of data, but before flushing, */
+ /* check that a notification didn't give us more */
+ wodPlayer_ProcessMessages(wwo);
+ if (!wwo->lpPlayPtr) {
+ TRACE("flushing\n");
+ ioctl(wwo->nbsdDev->fd, AUDIO_DRAIN);
+ wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+ dwNextNotifyTime = wodPlayer_NotifyCompletions(wwo, FALSE);
+ } else {
+ TRACE("recovering\n");
+ dwNextFeedTime = wodPlayer_FeedDSP(wwo);
+ }
+ }
+ } else {
+ dwNextFeedTime = dwNextNotifyTime = INFINITE;
+ }
+ }
+}
+
+/**************************************************************************
+ * wodGetDevCaps [internal]
+ */
+static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSA lpCaps, DWORD dwSize)
+{
+ TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
+
+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+ if (wDevID >= numOutDev) {
+ TRACE("numOutDev reached !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ memcpy(lpCaps, &WOutDev[wDevID].nbsdDev->out_caps, min(dwSize, sizeof(*lpCaps)));
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodOpen [internal]
+ */
+static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+ int audio_fragment;
+ WINE_WAVEOUT* wwo;
+ audio_info_t info;
+ DWORD ret;
+
+ TRACE("(%u, %p[cb=%08lx], %08lX);\n", wDevID, lpDesc, lpDesc->dwCallback, dwFlags);
+ if (lpDesc == NULL) {
+ WARN("Invalid Parameter !\n");
+ return MMSYSERR_INVALPARAM;
+ }
+ if (wDevID >= numOutDev) {
+ TRACE("MAX_WAVOUTDRV reached !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ /* only PCM format is supported so far... */
+ if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
+ lpDesc->lpFormat->nChannels == 0 ||
+ lpDesc->lpFormat->nSamplesPerSec == 0) {
+ WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return WAVERR_BADFORMAT;
+ }
+
+ if (dwFlags & WAVE_FORMAT_QUERY) {
+ TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return MMSYSERR_NOERROR;
+ }
+
+ wwo = &WOutDev[wDevID];
+
+ if ((dwFlags & WAVE_DIRECTSOUND) &&
+ !(wwo->nbsdDev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND))
+ /* not supported, ignore it */
+ dwFlags &= ~WAVE_DIRECTSOUND;
+
+ if (dwFlags & WAVE_DIRECTSOUND) {
+ if (wwo->nbsdDev->out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
+ /* we have realtime DirectSound, however we do not know the
+ * resolution of pointer updates - so choose a small blocksize */
+ audio_fragment = 256;
+ } else {
+ /* shockwave player uses only 4 1k-fragments at a rate of 22050 bytes/sec
+ * thus leading to 46ms per fragment, and a turnaround time of 185ms
+ */
+ /* 1024 bytes per fragment */
+ audio_fragment = 1024;
+ }
+ if (wwo->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
+ /* we want to be able to mmap() the device, which means it must be opened readable,
+ * otherwise mmap() will fail */
+ ret = NBSD_OpenDevice(wwo->nbsdDev,
+ (dwFlags & WAVE_DIRECTSOUND) ? O_RDWR : O_WRONLY,
+ &audio_fragment,
+ (dwFlags & WAVE_DIRECTSOUND) ? 0 : 1,
+ lpDesc->lpFormat->nSamplesPerSec,
+ (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
+ (lpDesc->lpFormat->wBitsPerSample == 16)
+ ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_ULINEAR_LE,
+ lpDesc->lpFormat->wBitsPerSample);
+ if ((ret==MMSYSERR_NOERROR) && (dwFlags & WAVE_DIRECTSOUND)) {
+ lpDesc->lpFormat->nSamplesPerSec=wwo->nbsdDev->sample_rate;
+ lpDesc->lpFormat->nChannels=(wwo->nbsdDev->stereo ? 2 : 1);
+ lpDesc->lpFormat->wBitsPerSample=wwo->nbsdDev->precision;
+ lpDesc->lpFormat->nBlockAlign=lpDesc->lpFormat->nChannels*lpDesc->lpFormat->wBitsPerSample/8;
+ lpDesc->lpFormat->nAvgBytesPerSec=lpDesc->lpFormat->nSamplesPerSec*lpDesc->lpFormat->nBlockAlign;
+ TRACE("NBSD_OpenDevice returned this format: %ldx%dx%d\n",
+ lpDesc->lpFormat->nSamplesPerSec,
+ lpDesc->lpFormat->wBitsPerSample,
+ lpDesc->lpFormat->nChannels);
+ }
+ if (ret != 0) return ret;
+ wwo->state = WINE_WS_STOPPED;
+
+ wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+ memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+ memcpy(&wwo->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
+
+ if (wwo->format.wBitsPerSample == 0) {
+ WARN("Resetting zeroed wBitsPerSample\n");
+ wwo->format.wBitsPerSample = 8 *
+ (wwo->format.wf.nAvgBytesPerSec /
+ wwo->format.wf.nSamplesPerSec) /
+ wwo->format.wf.nChannels;
+ }
+ /* Read output space info for future reference */
+ if (ioctl(wwo->nbsdDev->fd, AUDIO_GETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_GETINFO) failed (%s)\n", wwo->nbsdDev->dev_name, strerror(errno));
+ NBSD_CloseDevice(wwo->nbsdDev);
+ wwo->state = WINE_WS_CLOSED;
+ return MMSYSERR_NOTENABLED;
+ }
+
+ /* Check that fragsize is correct per our settings above */
+#if 0
+ if ((info.blocksize > 1024) && (audio_fragment < 1024)) {
+#else
+ if (info.blocksize > audio_fragment) {
+#endif
+ /* we've tried to set 1K fragments or less, but it didn't work */
+ ERR("fragment size set failed, size is now %d\n", info.blocksize);
+ MESSAGE("Your AudioIO driver did not let us configure small enough sound fragments.\n");
+ MESSAGE("This may cause delays and other problems in audio playback with certain applications.\n");
+ }
+
+ /* Remember fragsize and total buffer size for future use */
+ wwo->dwFragmentSize = info.blocksize;
+ wwo->dwBufferSize = info.play.buffer_size;
+ wwo->dwPlayedTotal = 0;
+ wwo->dwWrittenTotal = 0;
+
+ NBSD_InitRingMessage(&wwo->msgRing);
+
+ wwo->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+ wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
+ WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
+ CloseHandle(wwo->hStartUpEvent);
+ wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
+
+ TRACE("fd=%d fragmentSize=%ld\n",
+ wwo->nbsdDev->fd, wwo->dwFragmentSize);
+ if (wwo->dwFragmentSize % wwo->format.wf.nBlockAlign)
+ ERR("Fragment doesn't contain an integral number of data blocks\n");
+
+ TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
+ wwo->format.wBitsPerSample, wwo->format.wf.nAvgBytesPerSec,
+ wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
+ wwo->format.wf.nBlockAlign);
+
+ return wodNotifyClient(wwo, WOM_OPEN, 0L, 0L);
+}
+
+/**************************************************************************
+ * wodClose [internal]
+ */
+static DWORD wodClose(WORD wDevID)
+{
+ DWORD ret = MMSYSERR_NOERROR;
+ WINE_WAVEOUT* wwo;
+
+ TRACE("(%u);\n", wDevID);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ wwo = &WOutDev[wDevID];
+ if (wwo->lpQueuePtr) {
+ WARN("buffers still playing !\n");
+ ret = WAVERR_STILLPLAYING;
+ } else {
+ if (wwo->hThread != INVALID_HANDLE_VALUE) {
+ NBSD_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
+ }
+
+ NBSD_DestroyRingMessage(&wwo->msgRing);
+
+ NBSD_CloseDevice(wwo->nbsdDev);
+ wwo->state = WINE_WS_CLOSED;
+ wwo->dwFragmentSize = 0;
+ ret = wodNotifyClient(wwo, WOM_CLOSE, 0L, 0L);
+ }
+ return ret;
+}
+
+/**************************************************************************
+ * wodWrite [internal]
+ *
+ */
+static DWORD wodWrite(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ /* first, do the sanity checks... */
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad dev ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
+ return WAVERR_UNPREPARED;
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+ lpWaveHdr->lpNext = 0;
+
+ if ((lpWaveHdr->dwBufferLength & (WOutDev[wDevID].format.wf.nBlockAlign - 1)) != 0)
+ {
+ WARN("WaveHdr length isn't a multiple of the PCM block size: %ld %% %d\n",lpWaveHdr->dwBufferLength,WOutDev[wDevID].format.wf.nBlockAlign);
+ lpWaveHdr->dwBufferLength &= ~(WOutDev[wDevID].format.wf.nBlockAlign - 1);
+ }
+
+ NBSD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodPrepare [internal]
+ */
+static DWORD wodPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= numOutDev) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags |= WHDR_PREPARED;
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodUnprepare [internal]
+ */
+static DWORD wodUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= numOutDev) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodPause [internal]
+ */
+static DWORD wodPause(WORD wDevID)
+{
+ TRACE("(%u);!\n", wDevID);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ NBSD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_PAUSING, 0, TRUE);
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodRestart [internal]
+ */
+static DWORD wodRestart(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ NBSD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESTARTING, 0, TRUE);
+
+ /* FIXME: is NotifyClient with WOM_DONE right ? (Comet Busters 1.3.3 needs this notification) */
+ /* FIXME: Myst crashes with this ... hmm -MM
+ return wodNotifyClient(wwo, WOM_DONE, 0L, 0L);
+ */
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodReset [internal]
+ */
+static DWORD wodReset(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ NBSD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodGetPosition [internal]
+ */
+static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
+{
+ int time;
+ DWORD val;
+ WINE_WAVEOUT* wwo;
+
+ TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ if (lpTime == NULL) return MMSYSERR_INVALPARAM;
+
+ wwo = &WOutDev[wDevID];
+#ifdef EXACT_WODPOSITION
+ NBSD_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
+#endif
+ val = wwo->dwPlayedTotal;
+
+ TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
+ lpTime->wType, wwo->format.wBitsPerSample,
+ wwo->format.wf.nSamplesPerSec, wwo->format.wf.nChannels,
+ wwo->format.wf.nAvgBytesPerSec);
+ TRACE("dwPlayedTotal=%lu\n", val);
+
+ switch (lpTime->wType) {
+ case TIME_BYTES:
+ lpTime->u.cb = val;
+ TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
+ break;
+ case TIME_SAMPLES:
+ lpTime->u.sample = val * 8 / wwo->format.wBitsPerSample /wwo->format.wf.nChannels;
+ TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
+ break;
+ case TIME_SMPTE:
+ time = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
+ lpTime->u.smpte.hour = time / (60 * 60 * 1000);
+ time -= lpTime->u.smpte.hour * (60 * 60 * 1000);
+ lpTime->u.smpte.min = time / (60 * 1000);
+ time -= lpTime->u.smpte.min * (60 * 1000);
+ lpTime->u.smpte.sec = time / 1000;
+ time -= lpTime->u.smpte.sec * 1000;
+ lpTime->u.smpte.frame = time * 30 / 1000;
+ lpTime->u.smpte.fps = 30;
+ TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
+ lpTime->u.smpte.hour, lpTime->u.smpte.min,
+ lpTime->u.smpte.sec, lpTime->u.smpte.frame);
+ break;
+ default:
+ FIXME("Format %d not supported ! use TIME_MS !\n", lpTime->wType);
+ lpTime->wType = TIME_MS;
+ case TIME_MS:
+ lpTime->u.ms = val / (wwo->format.wf.nAvgBytesPerSec / 1000);
+ TRACE("TIME_MS=%lu\n", lpTime->u.ms);
+ break;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodBreakLoop [internal]
+ */
+static DWORD wodBreakLoop(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+
+ if (wDevID >= numOutDev || WOutDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("bad device ID !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+ NBSD_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodGetVolume [internal]
+ */
+static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
+{
+ int volume;
+ DWORD left, right;
+ audio_info_t info;
+
+ TRACE("(%u, %p);\n", wDevID, lpdwVol);
+
+ if (lpdwVol == NULL)
+ return MMSYSERR_NOTENABLED;
+ if (wDevID >= numOutDev)
+ return MMSYSERR_INVALPARAM;
+
+ if (ioctl(WOutDev[wDevID].nbsdDev->fd, AUDIO_GETINFO, &info) == -1) {
+ WARN("ioctl(%s, AUDIO_GETINFO) failed (%s)n", WOutDev[wDevID].nbsdDev->dev_name, strerror(errno));
+ return MMSYSERR_NOTENABLED;
+ }
+
+ volume = (info.play.gain - AUDIO_MIN_GAIN);
+ left = volume * (AUDIO_RIGHT_BALANCE - info.play.balance) /
+ (AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE);
+ right = volume * (info.play.balance - AUDIO_LEFT_BALANCE) /
+ (AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE);
+ TRACE("left=%ld right=%ld !\n", left, right);
+ *lpdwVol = ((left * 0xFFFFl) / (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN))
+ + (((right * 0xFFFFl) / (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) << 16);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodSetVolume [internal]
+ */
+static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
+{
+ DWORD left, right;
+ audio_info_t info;
+
+ TRACE("(%u, %08lX);\n", wDevID, dwParam);
+
+ AUDIO_INITINFO(&info);
+
+ left = (LOWORD(dwParam) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) / 0xFFFFl;
+ right = (HIWORD(dwParam) * (AUDIO_MAX_GAIN - AUDIO_MIN_GAIN)) / 0xFFFFl;
+ if (left + right == 0)
+ info.play.balance = AUDIO_MID_BALANCE;
+ else
+ info.play.balance = (left * AUDIO_LEFT_BALANCE
+ + right * AUDIO_RIGHT_BALANCE)
+ / (left + right) + AUDIO_LEFT_BALANCE;
+ if (info.play.balance == AUDIO_RIGHT_BALANCE)
+ info.play.gain = AUDIO_MAX_GAIN;
+ else
+ info.play.gain = left * (AUDIO_RIGHT_BALANCE - AUDIO_LEFT_BALANCE)
+ / (AUDIO_RIGHT_BALANCE - info.play.balance);
+
+ if (wDevID >= numOutDev) return MMSYSERR_INVALPARAM;
+
+ if (ioctl(WOutDev[wDevID].nbsdDev->fd, AUDIO_SETINFO, &info) == -1) {
+ WARN("ioctl(%s, AUDIO_SETINFO) failed (%s)\n", WOutDev[wDevID].nbsdDev->dev_name, strerror(errno));
+ return MMSYSERR_NOTENABLED;
+ } else {
+ TRACE("gain=%04x balance=%04x\n", info.play.gain, info.play.balance);
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * wodMessage (WINENBSD.4)
+ */
+DWORD WINAPI NBSD_wodMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
+ wDevID, wMsg, dwUser, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case DRVM_INIT:
+ case DRVM_EXIT:
+ case DRVM_ENABLE:
+ case DRVM_DISABLE:
+ /* FIXME: Pretend this is supported */
+ return 0;
+ case WODM_OPEN: return wodOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
+ case WODM_CLOSE: return wodClose (wDevID);
+ case WODM_WRITE: return wodWrite (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WODM_PAUSE: return wodPause (wDevID);
+ case WODM_GETPOS: return wodGetPosition (wDevID, (LPMMTIME)dwParam1, dwParam2);
+ case WODM_BREAKLOOP: return wodBreakLoop (wDevID);
+ case WODM_PREPARE: return wodPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WODM_UNPREPARE: return wodUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WODM_GETDEVCAPS: return wodGetDevCaps (wDevID, (LPWAVEOUTCAPSA)dwParam1, dwParam2);
+ case WODM_GETNUMDEVS: return numOutDev;
+ case WODM_GETPITCH: return MMSYSERR_NOTSUPPORTED;
+ case WODM_SETPITCH: return MMSYSERR_NOTSUPPORTED;
+ case WODM_GETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
+ case WODM_SETPLAYBACKRATE: return MMSYSERR_NOTSUPPORTED;
+ case WODM_GETVOLUME: return wodGetVolume (wDevID, (LPDWORD)dwParam1);
+ case WODM_SETVOLUME: return wodSetVolume (wDevID, dwParam1);
+ case WODM_RESTART: return wodRestart (wDevID);
+ case WODM_RESET: return wodReset (wDevID);
+
+ case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
+ case DRV_QUERYDEVICEINTERFACE: return wdDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
+ case DRV_QUERYDSOUNDIFACE: return wodDsCreate (wDevID, (PIDSDRIVER*)dwParam1);
+ case DRV_QUERYDSOUNDDESC: return wodDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
+ case DRV_QUERYDSOUNDGUID: return wodDsGuid (wDevID, (LPGUID)dwParam1);
+ default:
+ FIXME("unknown message %d!\n", wMsg);
+ }
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+/*======================================================================*
+ * Low level DSOUND definitions *
+ *======================================================================*/
+
+typedef struct IDsDriverPropertySetImpl IDsDriverPropertySetImpl;
+typedef struct IDsDriverNotifyImpl IDsDriverNotifyImpl;
+typedef struct IDsDriverImpl IDsDriverImpl;
+typedef struct IDsDriverBufferImpl IDsDriverBufferImpl;
+
+struct IDsDriverPropertySetImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriverPropertySet);
+ DWORD ref;
+
+ IDsDriverBufferImpl* buffer;
+};
+
+struct IDsDriverNotifyImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriverNotify);
+ DWORD ref;
+
+ /* IDsDriverNotifyImpl fields */
+ LPDSBPOSITIONNOTIFY notifies;
+ int nrofnotifies;
+
+ IDsDriverBufferImpl* buffer;
+};
+
+struct IDsDriverImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriver);
+ DWORD ref;
+
+ /* IDsDriverImpl fields */
+ UINT wDevID;
+ IDsDriverBufferImpl* primary;
+};
+
+struct IDsDriverBufferImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriverBuffer);
+ DWORD ref;
+
+ /* IDsDriverBufferImpl fields */
+ IDsDriverImpl* drv;
+ DWORD buflen;
+ WAVEFORMATEX wfx;
+ LPBYTE mapping;
+ DWORD maplen;
+ int fd;
+ DWORD dwFlags;
+
+ /* IDsDriverNotifyImpl fields */
+ IDsDriverNotifyImpl* notify;
+ int notify_index;
+
+ /* IDsDriverPropertySetImpl fields */
+ IDsDriverPropertySetImpl* property_set;
+};
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_Create(
+ IDsDriverBufferImpl * dsdb,
+ IDsDriverPropertySetImpl **pdsdps);
+
+static HRESULT WINAPI IDsDriverNotifyImpl_Create(
+ IDsDriverBufferImpl * dsdb,
+ IDsDriverNotifyImpl **pdsdn);
+
+/*======================================================================*
+ * Low level DSOUND property set implementation *
+ *======================================================================*/
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_QueryInterface(
+ PIDSDRIVERPROPERTYSET iface,
+ REFIID riid,
+ LPVOID *ppobj)
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
+ IDsDriverPropertySet_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface)
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedIncrement(&(This->ref));
+ return ref;
+}
+
+static ULONG WINAPI IDsDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface)
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedDecrement(&(This->ref));
+ if (ref == 0) {
+ IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("(%p) released\n",This);
+ }
+ return ref;
+}
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_Get(
+ PIDSDRIVERPROPERTYSET iface,
+ PDSPROPERTY pDsProperty,
+ LPVOID pPropertyParams,
+ ULONG cbPropertyParams,
+ LPVOID pPropertyData,
+ ULONG cbPropertyData,
+ PULONG pcbReturnedData )
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_Set(
+ PIDSDRIVERPROPERTYSET iface,
+ PDSPROPERTY pDsProperty,
+ LPVOID pPropertyParams,
+ ULONG cbPropertyParams,
+ LPVOID pPropertyData,
+ ULONG cbPropertyData )
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_QuerySupport(
+ PIDSDRIVERPROPERTYSET iface,
+ REFGUID PropertySetId,
+ ULONG PropertyId,
+ PULONG pSupport )
+{
+ ICOM_THIS(IDsDriverPropertySetImpl,iface);
+ FIXME("(%p,%s,%lx,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport);
+ return DSERR_UNSUPPORTED;
+}
+
+ICOM_VTABLE(IDsDriverPropertySet) dsdpsvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsDriverPropertySetImpl_QueryInterface,
+ IDsDriverPropertySetImpl_AddRef,
+ IDsDriverPropertySetImpl_Release,
+ IDsDriverPropertySetImpl_Get,
+ IDsDriverPropertySetImpl_Set,
+ IDsDriverPropertySetImpl_QuerySupport,
+};
+
+/*======================================================================*
+ * Low level DSOUND notify implementation *
+ *======================================================================*/
+
+static HRESULT WINAPI IDsDriverNotifyImpl_QueryInterface(
+ PIDSDRIVERNOTIFY iface,
+ REFIID riid,
+ LPVOID *ppobj)
+{
+ ICOM_THIS(IDsDriverNotifyImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
+ IDsDriverNotify_AddRef(iface);
+ *ppobj = This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface)
+{
+ ICOM_THIS(IDsDriverNotifyImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedIncrement(&(This->ref));
+ return ref;
+}
+
+static ULONG WINAPI IDsDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface)
+{
+ ICOM_THIS(IDsDriverNotifyImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedDecrement(&(This->ref));
+ if (ref == 0) {
+ IDsDriverBuffer_Release((PIDSDRIVERBUFFER)This->buffer);
+ if (This->notifies != NULL)
+ HeapFree(GetProcessHeap(), 0, This->notifies);
+
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("(%p) released\n",This);
+ }
+
+ return ref;
+}
+
+static HRESULT WINAPI IDsDriverNotifyImpl_SetNotificationPositions(
+ PIDSDRIVERNOTIFY iface,
+ DWORD howmuch,
+ LPCDSBPOSITIONNOTIFY notify)
+{
+ ICOM_THIS(IDsDriverNotifyImpl,iface);
+ TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
+
+ if (!notify) {
+ WARN("invalid parameter\n");
+ return DSERR_INVALIDPARAM;
+ }
+
+ if (TRACE_ON(wave)) {
+ int i;
+ for (i=0;i<howmuch;i++)
+ TRACE("notify at %ld to 0x%08lx\n",
+ notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
+ }
+
+ /* Make an internal copy of the caller-supplied array.
+ * Replace the existing copy if one is already present. */
+ if (This->notifies)
+ This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
+ else
+ This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ howmuch * sizeof(DSBPOSITIONNOTIFY));
+
+ memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
+ This->nrofnotifies = howmuch;
+
+ return S_OK;
+}
+
+ICOM_VTABLE(IDsDriverNotify) dsdnvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsDriverNotifyImpl_QueryInterface,
+ IDsDriverNotifyImpl_AddRef,
+ IDsDriverNotifyImpl_Release,
+ IDsDriverNotifyImpl_SetNotificationPositions,
+};
+
+/*======================================================================*
+ * Low level DSOUND implementation *
+ *======================================================================*/
+
+static HRESULT DSDB_MapBuffer(IDsDriverBufferImpl *dsdb)
+{
+ TRACE("(%p)\n",dsdb);
+ if (!dsdb->mapping) {
+ dsdb->mapping = mmap(NULL, dsdb->maplen, PROT_WRITE, MAP_SHARED,
+ dsdb->fd, 0);
+ if (dsdb->mapping == (LPBYTE)-1) {
+ TRACE("(%p): Could not map sound device for direct access (%s)\n", dsdb, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dsdb, dsdb->mapping, dsdb->maplen);
+
+ /* for some reason, es1371 and sblive! sometimes have junk in here.
+ * clear it, or we get junk noise */
+ /* some libc implementations are buggy: their memset reads from the buffer...
+ * to work around it, we have to zero the block by hand. We don't do the expected:
+ * memset(dsdb->mapping,0, dsdb->maplen);
+ */
+ {
+ unsigned char* p1 = dsdb->mapping;
+ unsigned len = dsdb->maplen;
+ unsigned char silence = (dsdb->wfx.wBitsPerSample == 8) ? 128 : 0;
+ unsigned long ulsilence = (dsdb->wfx.wBitsPerSample == 8) ? 0x80808080 : 0;
+
+ if (len >= 16) /* so we can have at least a 4 long area to store... */
+ {
+ /* the mmap:ed value is (at least) dword aligned
+ * so, start filling the complete unsigned long:s
+ */
+ int b = len >> 2;
+ unsigned long* p4 = (unsigned long*)p1;
+
+ while (b--) *p4++ = ulsilence;
+ /* prepare for filling the rest */
+ len &= 3;
+ p1 = (unsigned char*)p4;
+ }
+ /* in all cases, fill the remaining bytes */
+ while (len-- != 0) *p1++ = silence;
+ }
+ }
+ return DS_OK;
+}
+
+static HRESULT DSDB_UnmapBuffer(IDsDriverBufferImpl *dsdb)
+{
+ TRACE("(%p)\n",dsdb);
+ if (dsdb->mapping) {
+ if (munmap(dsdb->mapping, dsdb->maplen) < 0) {
+ ERR("(%p): Could not unmap sound device (%s)\n", dsdb, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ dsdb->mapping = NULL;
+ TRACE("(%p): sound device unmapped\n", dsdb);
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_QueryInterface(PIDSDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ TRACE("(%p,%s,%p)\n",iface,debugstr_guid(riid),*ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriverBuffer) ) {
+ IDsDriverBuffer_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
+ if (!This->notify)
+ IDsDriverNotifyImpl_Create(This, &(This->notify));
+ if (This->notify) {
+ IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
+ *ppobj = (LPVOID)This->notify;
+ return DS_OK;
+ }
+ *ppobj = 0;
+ return E_FAIL;
+ }
+
+ if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
+ if (!This->property_set)
+ IDsDriverPropertySetImpl_Create(This, &(This->property_set));
+ if (This->property_set) {
+ IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
+ *ppobj = (LPVOID)This->property_set;
+ return DS_OK;
+ }
+ *ppobj = 0;
+ return E_FAIL;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsDriverBufferImpl_AddRef(PIDSDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ TRACE("(%p)\n",This);
+ This->ref++;
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+}
+
+static ULONG WINAPI IDsDriverBufferImpl_Release(PIDSDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ TRACE("(%p)\n",This);
+ if (--This->ref) {
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+ }
+ if (This == This->drv->primary)
+ This->drv->primary = NULL;
+ DSDB_UnmapBuffer(This);
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("ref=0\n");
+ return 0;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Lock(PIDSDRIVERBUFFER iface,
+ LPVOID*ppvAudio1,LPDWORD pdwLen1,
+ LPVOID*ppvAudio2,LPDWORD pdwLen2,
+ DWORD dwWritePosition,DWORD dwWriteLen,
+ DWORD dwFlags)
+{
+ /* ICOM_THIS(IDsDriverBufferImpl,iface); */
+ /* since we (GetDriverDesc flags) have specified DSDDESC_DONTNEEDPRIMARYLOCK,
+ * and that we don't support secondary buffers, this method will never be called */
+ TRACE("(%p): stub\n",iface);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Unlock(PIDSDRIVERBUFFER iface,
+ LPVOID pvAudio1,DWORD dwLen1,
+ LPVOID pvAudio2,DWORD dwLen2)
+{
+ /* ICOM_THIS(IDsDriverBufferImpl,iface); */
+ TRACE("(%p): stub\n",iface);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetFormat(PIDSDRIVERBUFFER iface,
+ LPWAVEFORMATEX pwfx)
+{
+ /* ICOM_THIS(IDsDriverBufferImpl,iface); */
+
+ TRACE("(%p,%p)\n",iface,pwfx);
+ /* On our request (GetDriverDesc flags), DirectSound has by now used
+ * waveOutClose/waveOutOpen to set the format...
+ * unfortunately, this means our mmap() is now gone...
+ * so we need to somehow signal to our DirectSound implementation
+ * that it should completely recreate this HW buffer...
+ * this unexpected error code should do the trick... */
+ return DSERR_BUFFERLOST;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetFrequency(PIDSDRIVERBUFFER iface, DWORD dwFreq)
+{
+ /* ICOM_THIS(IDsDriverBufferImpl,iface); */
+ TRACE("(%p,%ld): stub\n",iface,dwFreq);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetVolumePan(PIDSDRIVERBUFFER iface, PDSVOLUMEPAN pVolPan)
+{
+ DWORD vol;
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ TRACE("(%p,%p)\n",This,pVolPan);
+
+ vol = pVolPan->dwTotalLeftAmpFactor | (pVolPan->dwTotalRightAmpFactor << 16);
+
+ if (wodSetVolume(This->drv->wDevID, vol) != MMSYSERR_NOERROR) {
+ WARN("wodSetVolume failed\n");
+ return DSERR_INVALIDPARAM;
+ }
+
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_SetPosition(PIDSDRIVERBUFFER iface, DWORD dwNewPos)
+{
+ /* ICOM_THIS(IDsDriverImpl,iface); */
+ TRACE("(%p,%ld): stub\n",iface,dwNewPos);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_GetPosition(PIDSDRIVERBUFFER iface,
+ LPDWORD lpdwPlay, LPDWORD lpdwWrite)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ audio_offset_t offs;
+ DWORD ptr;
+
+ TRACE("(%p)\n",iface);
+ if (WOutDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
+ ERR("device not open, but accessing?\n");
+ return DSERR_UNINITIALIZED;
+ }
+ if (ioctl(This->fd, AUDIO_GETOOFFS, &offs) < 0) {
+ ERR("ioctl(%s, AUDIO_GETOOFFS) failed (%s)\n",
+ WOutDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ ptr = offs.offset & ~3; /* align the pointer, just in case */
+ if (lpdwPlay) *lpdwPlay = ptr;
+ if (lpdwWrite) {
+ /* add some safety margin (not strictly necessary, but...) */
+ if (WOutDev[This->drv->wDevID].nbsdDev->out_caps.dwSupport & WAVECAPS_SAMPLEACCURATE)
+ *lpdwWrite = ptr + 32;
+ else
+ *lpdwWrite = ptr + WOutDev[This->drv->wDevID].dwFragmentSize;
+ while (*lpdwWrite > This->buflen)
+ *lpdwWrite -= This->buflen;
+ }
+ TRACE("playpos=%ld, writepos=%ld\n", lpdwPlay?*lpdwPlay:0, lpdwWrite?*lpdwWrite:0);
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Play(PIDSDRIVERBUFFER iface, DWORD dwRes1, DWORD dwRes2, DWORD dwFlags)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ audio_info_t info;
+
+ TRACE("(%p,%lx,%lx,%lx)\n",iface,dwRes1,dwRes2,dwFlags);
+ WOutDev[This->drv->wDevID].nbsdDev->bOutputEnabled = TRUE;
+ AUDIO_INITINFO(&info);
+ info.play.pause = 0;
+ if (ioctl(This->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause = 0 ) failed (%s)\n",WOutDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ WOutDev[This->drv->wDevID].nbsdDev->bOutputEnabled = FALSE;
+ return DSERR_GENERIC;
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverBufferImpl_Stop(PIDSDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsDriverBufferImpl,iface);
+ audio_info_t info;
+
+ TRACE("(%p)\n",iface);
+ /* no more playing */
+ WOutDev[This->drv->wDevID].nbsdDev->bOutputEnabled = FALSE;
+ AUDIO_INITINFO(&info);
+ info.play.pause = 1;
+ if (ioctl(This->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause = 1) failed (%s)\n", WOutDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+#if 0
+ /* the play position must be reset to the beginning of the buffer */
+ if (ioctl(This->fd, AUDIO_FLUSH) < 0) {
+ ERR("ioctl(%s, AUDIO_FLUSH) failed (%s)\n", WOutDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+#endif
+ /* Most drivers just can't stop the playback without closing the device...
+ * so we need to somehow signal to our DirectSound implementation
+ * that it should completely recreate this HW buffer...
+ * this unexpected error code should do the trick... */
+ /* FIXME: ...unless we are doing full duplex, then its not nice to close the device */
+ if (WOutDev[This->drv->wDevID].nbsdDev->open_count == 1)
+ return DSERR_BUFFERLOST;
+
+ return DS_OK;
+}
+
+static ICOM_VTABLE(IDsDriverBuffer) dsdbvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsDriverBufferImpl_QueryInterface,
+ IDsDriverBufferImpl_AddRef,
+ IDsDriverBufferImpl_Release,
+ IDsDriverBufferImpl_Lock,
+ IDsDriverBufferImpl_Unlock,
+ IDsDriverBufferImpl_SetFormat,
+ IDsDriverBufferImpl_SetFrequency,
+ IDsDriverBufferImpl_SetVolumePan,
+ IDsDriverBufferImpl_SetPosition,
+ IDsDriverBufferImpl_GetPosition,
+ IDsDriverBufferImpl_Play,
+ IDsDriverBufferImpl_Stop
+};
+
+static HRESULT WINAPI IDsDriverImpl_QueryInterface(PIDSDRIVER iface, REFIID riid, LPVOID *ppobj)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriver) ) {
+ IDsDriver_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsDriverImpl_AddRef(PIDSDRIVER iface)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ This->ref++;
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+}
+
+static ULONG WINAPI IDsDriverImpl_Release(PIDSDRIVER iface)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ if (--This->ref) {
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+ }
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("ref=0\n");
+ return 0;
+}
+
+static HRESULT WINAPI IDsDriverImpl_GetDriverDesc(PIDSDRIVER iface, PDSDRIVERDESC pDesc)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p,%p)\n",iface,pDesc);
+
+ /* copy version from driver */
+ memcpy(pDesc, &(WOutDev[This->wDevID].nbsdDev->ds_desc), sizeof(DSDRIVERDESC));
+
+ pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
+ DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK;
+ pDesc->dnDevNode = WOutDev[This->wDevID].waveDesc.dnDevNode;
+ pDesc->wVxdId = 0;
+ pDesc->wReserved = 0;
+ pDesc->ulDeviceNum = This->wDevID;
+ pDesc->dwHeapType = DSDHEAP_NOHEAP;
+ pDesc->pvDirectDrawHeap = NULL;
+ pDesc->dwMemStartAddress = 0;
+ pDesc->dwMemEndAddress = 0;
+ pDesc->dwMemAllocExtra = 0;
+ pDesc->pvReserved1 = NULL;
+ pDesc->pvReserved2 = NULL;
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_Open(PIDSDRIVER iface)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ audio_info_t info;
+
+ TRACE("(%p)\n",iface);
+
+ AUDIO_INITINFO(&info);
+ info.play.pause = 1;
+
+ /* make sure the card doesn't start playing before we want it to */
+ WOutDev[This->wDevID].nbsdDev->bOutputEnabled = FALSE;
+ if (ioctl(WOutDev[This->wDevID].nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause=1) failed (%s)\n",WOutDev[This->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_Close(PIDSDRIVER iface)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p)\n",iface);
+ if (This->primary) {
+ ERR("problem with DirectSound: primary not released\n");
+ return DSERR_GENERIC;
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverImpl_GetCaps(PIDSDRIVER iface, PDSDRIVERCAPS pCaps)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ TRACE("(%p,%p)\n",iface,pCaps);
+ memcpy(pCaps, &(WOutDev[This->wDevID].nbsdDev->ds_caps), sizeof(DSDRIVERCAPS));
+ return DS_OK;
+}
+
+static HRESULT WINAPI DSD_CreatePrimaryBuffer(PIDSDRIVER iface,
+ LPWAVEFORMATEX pwfx,
+ DWORD dwFlags,
+ DWORD dwCardAddress,
+ LPDWORD pdwcbBufferSize,
+ LPBYTE *ppbBuffer,
+ LPVOID *ppvObj)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
+ HRESULT err;
+ audio_info_t info;
+ TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+
+ if (This->primary)
+ return DSERR_ALLOCATED;
+ if (dwFlags & (DSBCAPS_CTRLFREQUENCY | DSBCAPS_CTRLPAN))
+ return DSERR_CONTROLUNAVAIL;
+
+ *ippdsdb = (IDsDriverBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverBufferImpl));
+ if (*ippdsdb == NULL)
+ return DSERR_OUTOFMEMORY;
+ (*ippdsdb)->lpVtbl = &dsdbvt;
+ (*ippdsdb)->ref = 1;
+ (*ippdsdb)->drv = This;
+ (*ippdsdb)->wfx = *pwfx;
+ (*ippdsdb)->fd = WOutDev[This->wDevID].nbsdDev->fd;
+ (*ippdsdb)->dwFlags = dwFlags;
+
+
+ /* check how big the DMA buffer is now */
+ if (ioctl((*ippdsdb)->fd, AUDIO_GETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_GETINFO) failed (%s)\n",
+ WOutDev[This->wDevID].nbsdDev->dev_name, strerror(errno));
+ HeapFree(GetProcessHeap(),0,*ippdsdb);
+ *ippdsdb = NULL;
+ return DSERR_GENERIC;
+ }
+ (*ippdsdb)->maplen = (*ippdsdb)->buflen = info.play.buffer_size;
+
+ /* map the DMA buffer */
+ err = DSDB_MapBuffer(*ippdsdb);
+ if (err != DS_OK) {
+ HeapFree(GetProcessHeap(),0,*ippdsdb);
+ *ippdsdb = NULL;
+ return err;
+ }
+
+ /* primary buffer is ready to go */
+ *pdwcbBufferSize = (*ippdsdb)->maplen;
+ *ppbBuffer = (*ippdsdb)->mapping;
+
+ AUDIO_INITINFO(&info);
+
+ /* some drivers need some extra nudging after mapping */
+ WOutDev[This->wDevID].nbsdDev->bOutputEnabled = FALSE;
+ info.play.pause = 1;
+ if (ioctl((*ippdsdb)->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause=1) failed (%s)\n",
+ WOutDev[This->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+
+ This->primary = *ippdsdb;
+
+ return DS_OK;
+}
+
+static HRESULT WINAPI DSD_CreateSecondaryBuffer(PIDSDRIVER iface,
+ LPWAVEFORMATEX pwfx,
+ DWORD dwFlags,
+ DWORD dwCardAddress,
+ LPDWORD pdwcbBufferSize,
+ LPBYTE *ppbBuffer,
+ LPVOID *ppvObj)
+{
+ ICOM_THIS(IDsDriverImpl,iface);
+ IDsDriverBufferImpl** ippdsdb = (IDsDriverBufferImpl**)ppvObj;
+ FIXME("(%p,%p,%lx,%lx,%p,%p,%p): stub\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+
+ *ippdsdb = 0;
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsDriverImpl_CreateSoundBuffer(PIDSDRIVER iface,
+ LPWAVEFORMATEX pwfx,
+ DWORD dwFlags,
+ DWORD dwCardAddress,
+ LPDWORD pdwcbBufferSize,
+ LPBYTE *ppbBuffer,
+ LPVOID *ppvObj)
+{
+ TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+
+ if (dwFlags & DSBCAPS_PRIMARYBUFFER)
+ return DSD_CreatePrimaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+
+ return DSD_CreateSecondaryBuffer(iface,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+}
+
+static HRESULT WINAPI IDsDriverImpl_DuplicateSoundBuffer(PIDSDRIVER iface,
+ PIDSDRIVERBUFFER pBuffer,
+ LPVOID *ppvObj)
+{
+ /* ICOM_THIS(IDsDriverImpl,iface); */
+ TRACE("(%p,%p): stub\n",iface,pBuffer);
+ return DSERR_INVALIDCALL;
+}
+
+static ICOM_VTABLE(IDsDriver) dsdvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsDriverImpl_QueryInterface,
+ IDsDriverImpl_AddRef,
+ IDsDriverImpl_Release,
+ IDsDriverImpl_GetDriverDesc,
+ IDsDriverImpl_Open,
+ IDsDriverImpl_Close,
+ IDsDriverImpl_GetCaps,
+ IDsDriverImpl_CreateSoundBuffer,
+ IDsDriverImpl_DuplicateSoundBuffer
+};
+
+static HRESULT WINAPI IDsDriverPropertySetImpl_Create(
+ IDsDriverBufferImpl * dsdb,
+ IDsDriverPropertySetImpl **pdsdps)
+{
+ IDsDriverPropertySetImpl * dsdps;
+ TRACE("(%p,%p)\n",dsdb,pdsdps);
+
+ dsdps = (IDsDriverPropertySetImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdps));
+ if (dsdps == NULL) {
+ WARN("out of memory\n");
+ return DSERR_OUTOFMEMORY;
+ }
+
+ dsdps->ref = 0;
+ dsdps->lpVtbl = &dsdpsvt;
+ dsdps->buffer = dsdb;
+ dsdb->property_set = dsdps;
+ IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
+
+ *pdsdps = dsdps;
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsDriverNotifyImpl_Create(
+ IDsDriverBufferImpl * dsdb,
+ IDsDriverNotifyImpl **pdsdn)
+{
+ IDsDriverNotifyImpl * dsdn;
+ TRACE("(%p,%p)\n",dsdb,pdsdn);
+
+ dsdn = (IDsDriverNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dsdn));
+
+ if (dsdn == NULL) {
+ WARN("out of memory\n");
+ return DSERR_OUTOFMEMORY;
+ }
+
+ dsdn->ref = 0;
+ dsdn->lpVtbl = &dsdnvt;
+ dsdn->buffer = dsdb;
+ dsdb->notify = dsdn;
+ IDsDriverBuffer_AddRef((PIDSDRIVER)dsdb);
+
+ *pdsdn = dsdn;
+ return DS_OK;
+};
+
+static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
+{
+ IDsDriverImpl** idrv = (IDsDriverImpl**)drv;
+ TRACE("(%d,%p)\n",wDevID,drv);
+
+ /* the HAL isn't much better than the HEL if we can't do mmap() */
+ if (!(WOutDev[wDevID].nbsdDev->out_caps.dwSupport & WAVECAPS_DIRECTSOUND)) {
+ ERR("DirectSound flag not set\n");
+ MESSAGE("This sound card's driver does not support direct access\n");
+ MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
+ return MMSYSERR_NOTSUPPORTED;
+ }
+
+ *idrv = (IDsDriverImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsDriverImpl));
+ if (!*idrv)
+ return MMSYSERR_NOMEM;
+ (*idrv)->lpVtbl = &dsdvt;
+ (*idrv)->ref = 1;
+
+ (*idrv)->wDevID = wDevID;
+ (*idrv)->primary = NULL;
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
+{
+ TRACE("(%d,%p)\n",wDevID,desc);
+ memcpy(desc, &(WOutDev[wDevID].nbsdDev->ds_desc), sizeof(DSDRIVERDESC));
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD wodDsGuid(UINT wDevID, LPGUID pGuid)
+{
+ TRACE("(%d,%p)\n",wDevID,pGuid);
+ memcpy(pGuid, &(WOutDev[wDevID].nbsdDev->ds_guid), sizeof(GUID));
+ return MMSYSERR_NOERROR;
+}
+
+/*======================================================================*
+ * Low level WAVE IN implementation *
+ *======================================================================*/
+
+/**************************************************************************
+ * widNotifyClient [internal]
+ */
+static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("wMsg = 0x%04x (%s) dwParm1 = %04lX dwParam2 = %04lX\n", wMsg,
+ wMsg == WIM_OPEN ? "WIM_OPEN" : wMsg == WIM_CLOSE ? "WIM_CLOSE" :
+ wMsg == WIM_DATA ? "WIM_DATA" : "Unknown", dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case WIM_OPEN:
+ case WIM_CLOSE:
+ case WIM_DATA:
+ if (wwi->wFlags != DCB_NULL &&
+ !DriverCallback(wwi->waveDesc.dwCallback, wwi->wFlags,
+ (HDRVR)wwi->waveDesc.hWave, wMsg,
+ wwi->waveDesc.dwInstance, dwParam1, dwParam2)) {
+ WARN("can't notify client !\n");
+ return MMSYSERR_ERROR;
+ }
+ break;
+ default:
+ FIXME("Unknown callback message %u\n", wMsg);
+ return MMSYSERR_INVALPARAM;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widGetDevCaps [internal]
+ */
+static DWORD widGetDevCaps(WORD wDevID, LPWAVEINCAPSA lpCaps, DWORD dwSize)
+{
+ TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
+
+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+ if (wDevID >= numInDev) {
+ TRACE("numOutDev reached !\n");
+ return MMSYSERR_BADDEVICEID;
+ }
+
+ memcpy(lpCaps, &WInDev[wDevID].nbsdDev->in_caps, min(dwSize, sizeof(*lpCaps)));
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widRecorder_ReadHeaders [internal]
+ */
+static void widRecorder_ReadHeaders(WINE_WAVEIN * wwi)
+{
+ enum win_wm_message tmp_msg;
+ DWORD tmp_param;
+ HANDLE tmp_ev;
+ WAVEHDR* lpWaveHdr;
+
+ while (NBSD_RetrieveRingMessage(&wwi->msgRing, &tmp_msg, &tmp_param, &tmp_ev)) {
+ if (tmp_msg == WINE_WM_HEADER) {
+ LPWAVEHDR* wh;
+ lpWaveHdr = (LPWAVEHDR)tmp_param;
+ lpWaveHdr->lpNext = 0;
+
+ if (wwi->lpQueuePtr == 0)
+ wwi->lpQueuePtr = lpWaveHdr;
+ else {
+ for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
+ *wh = lpWaveHdr;
+ }
+ } else {
+ ERR("should only have headers left\n");
+ }
+ }
+}
+
+/**************************************************************************
+ * widRecorder [internal]
+ */
+static DWORD CALLBACK widRecorder(LPVOID pmt)
+{
+ WORD uDevID = (DWORD)pmt;
+ WINE_WAVEIN* wwi = (WINE_WAVEIN*)&WInDev[uDevID];
+ WAVEHDR* lpWaveHdr;
+ DWORD dwSleepTime;
+ DWORD bytesRead;
+ LPVOID buffer = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, wwi->dwFragmentSize);
+ char *pOffset = buffer;
+ enum win_wm_message msg;
+ DWORD param;
+ HANDLE ev;
+ audio_info_t info;
+ audio_offset_t offs;
+ int xs;
+
+ wwi->state = WINE_WS_STOPPED;
+ wwi->dwTotalRecorded = 0;
+ wwi->lpQueuePtr = NULL;
+
+ SetEvent(wwi->hStartUpEvent);
+
+ AUDIO_INITINFO(&info);
+
+ /* disable input so capture will begin when triggered */
+ wwi->nbsdDev->bInputEnabled = FALSE;
+ info.record.pause = 1;
+ if (ioctl(wwi->nbsdDev->fd, AUDIO_SETINFO, &info) < 0)
+ ERR("ioctl(%s, AUDIO_SETINFO pause=1) failed (%s)\n", wwi->nbsdDev->dev_name, strerror(errno));
+
+ /* the soundblaster live needs a micro wake to get its recording started
+ * (or GETISPACE will have 0 frags all the time)
+ */
+ read(wwi->nbsdDev->fd, &xs, 4);
+
+ /* make sleep time to be # of ms to output a fragment */
+ dwSleepTime = (wwi->dwFragmentSize * 1000) / wwi->format.wf.nAvgBytesPerSec;
+ TRACE("sleeptime=%ld ms\n", dwSleepTime);
+
+ for (;;) {
+ /* wait for dwSleepTime or an event in thread's queue */
+ /* FIXME: could improve wait time depending on queue state,
+ * ie, number of queued fragments
+ */
+
+ if (wwi->lpQueuePtr != NULL && wwi->state == WINE_WS_PLAYING)
+ {
+ lpWaveHdr = wwi->lpQueuePtr;
+
+ ioctl(wwi->nbsdDev->fd, AUDIO_GETINFO, &info);
+ ioctl(wwi->nbsdDev->fd, AUDIO_GETIOFFS, &offs);
+ TRACE("info={frag=%d fsize=%d ftotal=%d bytes=%d}\n", offs.deltablks, info.blocksize, info.hiwat, info.record.buffer_size);
+
+ /* read all the fragments accumulated so far */
+ while ((offs.deltablks > 0) && (wwi->lpQueuePtr))
+ {
+ offs.deltablks --;
+
+ if (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded >= wwi->dwFragmentSize)
+ {
+ /* directly read fragment in wavehdr */
+ bytesRead = read(wwi->nbsdDev->fd,
+ lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
+ wwi->dwFragmentSize);
+
+ TRACE("bytesRead=%ld (direct)\n", bytesRead);
+ if (bytesRead != (DWORD) -1)
+ {
+ /* update number of bytes recorded in current buffer and by this device */
+ lpWaveHdr->dwBytesRecorded += bytesRead;
+ wwi->dwTotalRecorded += bytesRead;
+
+ /* buffer is full. notify client */
+ if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
+ {
+ /* must copy the value of next waveHdr, because we have no idea of what
+ * will be done with the content of lpWaveHdr in callback
+ */
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ wwi->lpQueuePtr = lpNext;
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ lpWaveHdr = lpNext;
+ }
+ }
+ }
+ else
+ {
+ /* read the fragment in a local buffer */
+ bytesRead = read(wwi->nbsdDev->fd, buffer, wwi->dwFragmentSize);
+ pOffset = buffer;
+
+ TRACE("bytesRead=%ld (local)\n", bytesRead);
+
+ /* copy data in client buffers */
+ while (bytesRead != (DWORD) -1 && bytesRead > 0)
+ {
+ DWORD dwToCopy = min (bytesRead, lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+
+ memcpy(lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded,
+ pOffset,
+ dwToCopy);
+
+ /* update number of bytes recorded in current buffer and by this device */
+ lpWaveHdr->dwBytesRecorded += dwToCopy;
+ wwi->dwTotalRecorded += dwToCopy;
+ bytesRead -= dwToCopy;
+ pOffset += dwToCopy;
+
+ /* client buffer is full. notify client */
+ if (lpWaveHdr->dwBytesRecorded == lpWaveHdr->dwBufferLength)
+ {
+ /* must copy the value of next waveHdr, because we have no idea of what
+ * will be done with the content of lpWaveHdr in callback
+ */
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+ TRACE("lpNext=%p\n", lpNext);
+
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ wwi->lpQueuePtr = lpNext;
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+
+ lpWaveHdr = lpNext;
+ if (!lpNext && bytesRead) {
+ /* before we give up, check for more header messages */
+ while (NBSD_PeekRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
+ {
+ if (msg == WINE_WM_HEADER) {
+ LPWAVEHDR hdr;
+ NBSD_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev);
+ hdr = ((LPWAVEHDR)param);
+ TRACE("msg = %s, hdr = %p, ev = %p\n", wodPlayerCmdString[msg - WM_USER - 1], hdr, ev);
+ hdr->lpNext = 0;
+ if (lpWaveHdr == 0) {
+ /* new head of queue */
+ wwi->lpQueuePtr = lpWaveHdr = hdr;
+ } else {
+ /* insert buffer at the end of queue */
+ LPWAVEHDR* wh;
+ for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
+ *wh = hdr;
+ }
+ } else
+ break;
+ }
+
+ if (lpWaveHdr == 0) {
+ /* no more buffer to copy data to, but we did read more.
+ * what hasn't been copied will be dropped
+ */
+ WARN("buffer under run! %lu bytes dropped.\n", bytesRead);
+ wwi->lpQueuePtr = NULL;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ WAIT_OMR(&wwi->msgRing, dwSleepTime);
+
+ while (NBSD_RetrieveRingMessage(&wwi->msgRing, &msg, ¶m, &ev))
+ {
+ TRACE("msg=%s param=0x%lx\n", wodPlayerCmdString[msg - WM_USER - 1], param);
+ switch (msg) {
+ case WINE_WM_PAUSING:
+ wwi->state = WINE_WS_PAUSED;
+ /*FIXME("Device should stop recording\n");*/
+ SetEvent(ev);
+ break;
+ case WINE_WM_STARTING:
+ wwi->state = WINE_WS_PLAYING;
+
+ /* start the recording */
+ wwi->nbsdDev->bInputEnabled = TRUE;
+ info.record.pause = 0;
+ if (ioctl(wwi->nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ wwi->nbsdDev->bInputEnabled = FALSE;
+ ERR("ioctl(%s, AUDIO_SETINFO pause=0) failed (%s)\n", wwi->nbsdDev->dev_name, strerror(errno));
+ }
+
+ SetEvent(ev);
+ break;
+ case WINE_WM_HEADER:
+ lpWaveHdr = (LPWAVEHDR)param;
+ lpWaveHdr->lpNext = 0;
+
+ /* insert buffer at the end of queue */
+ {
+ LPWAVEHDR* wh;
+ for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext));
+ *wh = lpWaveHdr;
+ }
+ break;
+ case WINE_WM_STOPPING:
+ if (wwi->state != WINE_WS_STOPPED)
+ {
+ /* stop the recording */
+ wwi->nbsdDev->bInputEnabled = FALSE;
+ AUDIO_INITINFO(&info);
+ info.record.pause = 1;
+ if (ioctl(wwi->nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO) failed (%s)\n", wwi->nbsdDev->dev_name, strerror(errno));
+ }
+
+ /* read any headers in queue */
+ widRecorder_ReadHeaders(wwi);
+
+ /* return current buffer to app */
+ lpWaveHdr = wwi->lpQueuePtr;
+ if (lpWaveHdr)
+ {
+ LPWAVEHDR lpNext = lpWaveHdr->lpNext;
+ TRACE("stop %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+ wwi->lpQueuePtr = lpNext;
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ }
+ }
+ wwi->state = WINE_WS_STOPPED;
+ SetEvent(ev);
+ break;
+ case WINE_WM_RESETTING:
+ if (wwi->state != WINE_WS_STOPPED)
+ {
+ /* stop the recording */
+ wwi->nbsdDev->bInputEnabled = FALSE;
+ AUDIO_INITINFO(&info);
+ info.record.pause = 1;
+ if (ioctl(wwi->nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO) failed (%s)\n", wwi->nbsdDev->dev_name, strerror(errno));
+ }
+ }
+ wwi->state = WINE_WS_STOPPED;
+ wwi->dwTotalRecorded = 0;
+
+ /* read any headers in queue */
+ widRecorder_ReadHeaders(wwi);
+
+ /* return all buffers to the app */
+ for (lpWaveHdr = wwi->lpQueuePtr; lpWaveHdr; lpWaveHdr = lpWaveHdr->lpNext) {
+ TRACE("reset %p %p\n", lpWaveHdr, lpWaveHdr->lpNext);
+ lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+ wwi->lpQueuePtr = lpWaveHdr->lpNext;
+ widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+ }
+
+ wwi->lpQueuePtr = NULL;
+ SetEvent(ev);
+ break;
+ case WINE_WM_CLOSING:
+ wwi->hThread = 0;
+ wwi->state = WINE_WS_CLOSED;
+ SetEvent(ev);
+ HeapFree(GetProcessHeap(), 0, buffer);
+ ExitThread(0);
+ /* shouldn't go here */
+ default:
+ FIXME("unknown message %d\n", msg);
+ break;
+ }
+ }
+ }
+ ExitThread(0);
+ /* just for not generating compilation warnings... should never be executed */
+ return 0;
+}
+
+
+/**************************************************************************
+ * widOpen [internal]
+ */
+static DWORD widOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+ WINE_WAVEIN* wwi;
+ DWORD ret;
+ audio_info_t info;
+ int audio_fragment;
+
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
+
+ AUDIO_INITINFO(&info);
+
+ if (lpDesc == NULL) {
+ WARN("Invalid Parameter !\n");
+ return MMSYSERR_INVALPARAM;
+ }
+ if (wDevID >= numInDev) return MMSYSERR_BADDEVICEID;
+
+ /* only PCM format is supported so far... */
+ if (lpDesc->lpFormat->wFormatTag != WAVE_FORMAT_PCM ||
+ lpDesc->lpFormat->nChannels == 0 ||
+ lpDesc->lpFormat->nSamplesPerSec == 0) {
+ WARN("Bad format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return WAVERR_BADFORMAT;
+ }
+
+ if (dwFlags & WAVE_FORMAT_QUERY) {
+ TRACE("Query format: tag=%04X nChannels=%d nSamplesPerSec=%ld !\n",
+ lpDesc->lpFormat->wFormatTag, lpDesc->lpFormat->nChannels,
+ lpDesc->lpFormat->nSamplesPerSec);
+ return MMSYSERR_NOERROR;
+ }
+
+ wwi = &WInDev[wDevID];
+
+ if (wwi->state != WINE_WS_CLOSED) return MMSYSERR_ALLOCATED;
+
+ if ((dwFlags & WAVE_DIRECTSOUND) &&
+ !(wwi->nbsdDev->in_caps_support & WAVECAPS_DIRECTSOUND))
+ /* not supported, ignore it */
+ dwFlags &= ~WAVE_DIRECTSOUND;
+
+ if (dwFlags & WAVE_DIRECTSOUND) {
+ TRACE("has DirectSoundCapture driver\n");
+ if (wwi->nbsdDev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
+ /* we have realtime DirectSound, however we do not know the
+ * resolution of pointer updates - so choose a small blocksize */
+ audio_fragment = 256;
+ } else {
+ audio_fragment = 1024;
+ }
+
+ TRACE("using %d byte fragments\n", audio_fragment);
+
+ ret = NBSD_OpenDevice(wwi->nbsdDev, O_RDONLY, &audio_fragment,
+ 1,
+ lpDesc->lpFormat->nSamplesPerSec,
+ (lpDesc->lpFormat->nChannels > 1) ? 1 : 0,
+ (lpDesc->lpFormat->wBitsPerSample == 16)
+ ? AUDIO_ENCODING_SLINEAR_LE : AUDIO_ENCODING_ULINEAR_LE,
+ lpDesc->lpFormat->wBitsPerSample);
+ if (ret != 0) return ret;
+ wwi->state = WINE_WS_STOPPED;
+
+ if (wwi->lpQueuePtr) {
+ WARN("Should have an empty queue (%p)\n", wwi->lpQueuePtr);
+ wwi->lpQueuePtr = NULL;
+ }
+ wwi->dwTotalRecorded = 0;
+ wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+ memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+ memcpy(&wwi->format, lpDesc->lpFormat, sizeof(PCMWAVEFORMAT));
+
+ if (wwi->format.wBitsPerSample == 0) {
+ WARN("Resetting zeroed wBitsPerSample\n");
+ wwi->format.wBitsPerSample = 8 *
+ (wwi->format.wf.nAvgBytesPerSec /
+ wwi->format.wf.nSamplesPerSec) /
+ wwi->format.wf.nChannels;
+ }
+
+ if (ioctl(wwi->nbsdDev->fd, AUDIO_GETINFO, &info) == -1) {
+ WARN("ioctl(%s, AUDIO_GETINFO) failed (%s)\n", wwi->nbsdDev->dev_name, strerror(errno));
+ NBSD_CloseDevice(wwi->nbsdDev);
+ wwi->state = WINE_WS_CLOSED;
+ return MMSYSERR_NOTENABLED;
+ }
+ wwi->dwFragmentSize = info.blocksize;
+
+ TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
+ wwi->format.wBitsPerSample, wwi->format.wf.nAvgBytesPerSec,
+ wwi->format.wf.nSamplesPerSec, wwi->format.wf.nChannels,
+ wwi->format.wf.nBlockAlign);
+
+ NBSD_InitRingMessage(&wwi->msgRing);
+
+ wwi->hStartUpEvent = CreateEventA(NULL, FALSE, FALSE, NULL);
+ wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
+ WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
+ CloseHandle(wwi->hStartUpEvent);
+ wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
+
+ return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
+}
+
+/**************************************************************************
+ * widClose [internal]
+ */
+static DWORD widClose(WORD wDevID)
+{
+ WINE_WAVEIN* wwi;
+
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't close !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ wwi = &WInDev[wDevID];
+
+ if (wwi->lpQueuePtr != NULL) {
+ WARN("still buffers open !\n");
+ return WAVERR_STILLPLAYING;
+ }
+
+ NBSD_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
+ NBSD_CloseDevice(wwi->nbsdDev);
+ wwi->state = WINE_WS_CLOSED;
+ wwi->dwFragmentSize = 0;
+ NBSD_DestroyRingMessage(&wwi->msgRing);
+ return widNotifyClient(wwi, WIM_CLOSE, 0L, 0L);
+}
+
+/**************************************************************************
+ * widAddBuffer [internal]
+ */
+static DWORD widAddBuffer(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't do it !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+ if (!(lpWaveHdr->dwFlags & WHDR_PREPARED)) {
+ TRACE("never been prepared !\n");
+ return WAVERR_UNPREPARED;
+ }
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE) {
+ TRACE("header already in use !\n");
+ return WAVERR_STILLPLAYING;
+ }
+
+ lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveHdr->dwBytesRecorded = 0;
+ lpWaveHdr->lpNext = NULL;
+
+ NBSD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widPrepare [internal]
+ */
+static DWORD widPrepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+
+ if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags |= WHDR_PREPARED;
+ lpWaveHdr->dwFlags &= ~WHDR_DONE;
+ lpWaveHdr->dwBytesRecorded = 0;
+ TRACE("header prepared !\n");
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widUnprepare [internal]
+ */
+static DWORD widUnprepare(WORD wDevID, LPWAVEHDR lpWaveHdr, DWORD dwSize)
+{
+ TRACE("(%u, %p, %08lX);\n", wDevID, lpWaveHdr, dwSize);
+ if (wDevID >= numInDev) return MMSYSERR_INVALHANDLE;
+
+ if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+ return WAVERR_STILLPLAYING;
+
+ lpWaveHdr->dwFlags &= ~WHDR_PREPARED;
+ lpWaveHdr->dwFlags |= WHDR_DONE;
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widStart [internal]
+ */
+static DWORD widStart(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't start recording !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ NBSD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STARTING, 0, TRUE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widStop [internal]
+ */
+static DWORD widStop(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't stop !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+
+ NBSD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_STOPPING, 0, TRUE);
+
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widReset [internal]
+ */
+static DWORD widReset(WORD wDevID)
+{
+ TRACE("(%u);\n", wDevID);
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't reset !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+ NBSD_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widGetPosition [internal]
+ */
+static DWORD widGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
+{
+ int time;
+ WINE_WAVEIN* wwi;
+
+ TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
+
+ if (wDevID >= numInDev || WInDev[wDevID].state == WINE_WS_CLOSED) {
+ WARN("can't get pos !\n");
+ return MMSYSERR_INVALHANDLE;
+ }
+ if (lpTime == NULL) return MMSYSERR_INVALPARAM;
+
+ wwi = &WInDev[wDevID];
+
+ TRACE("wType=%04X !\n", lpTime->wType);
+ TRACE("wBitsPerSample=%u\n", wwi->format.wBitsPerSample);
+ TRACE("nSamplesPerSec=%lu\n", wwi->format.wf.nSamplesPerSec);
+ TRACE("nChannels=%u\n", wwi->format.wf.nChannels);
+ TRACE("nAvgBytesPerSec=%lu\n", wwi->format.wf.nAvgBytesPerSec);
+ TRACE("dwTotalRecorded=%lu\n",wwi->dwTotalRecorded);
+ switch (lpTime->wType) {
+ case TIME_BYTES:
+ lpTime->u.cb = wwi->dwTotalRecorded;
+ TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
+ break;
+ case TIME_SAMPLES:
+ lpTime->u.sample = wwi->dwTotalRecorded * 8 /
+ wwi->format.wBitsPerSample / wwi->format.wf.nChannels;
+ TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
+ break;
+ case TIME_SMPTE:
+ time = wwi->dwTotalRecorded /
+ (wwi->format.wf.nAvgBytesPerSec / 1000);
+ lpTime->u.smpte.hour = time / (60 * 60 * 1000);
+ time -= lpTime->u.smpte.hour * (60 * 60 * 1000);
+ lpTime->u.smpte.min = time / (60 * 1000);
+ time -= lpTime->u.smpte.min * (60 * 1000);
+ lpTime->u.smpte.sec = time / 1000;
+ time -= lpTime->u.smpte.sec * 1000;
+ lpTime->u.smpte.frame = time * 30 / 1000;
+ lpTime->u.smpte.fps = 30;
+ TRACE("TIME_SMPTE=%02u:%02u:%02u:%02u\n",
+ lpTime->u.smpte.hour, lpTime->u.smpte.min,
+ lpTime->u.smpte.sec, lpTime->u.smpte.frame);
+ break;
+ default:
+ FIXME("format not supported (%u) ! use TIME_MS !\n", lpTime->wType);
+ lpTime->wType = TIME_MS;
+ case TIME_MS:
+ lpTime->u.ms = wwi->dwTotalRecorded /
+ (wwi->format.wf.nAvgBytesPerSec / 1000);
+ TRACE("TIME_MS=%lu\n", lpTime->u.ms);
+ break;
+ }
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * widMessage (WINENBSD.3)
+ */
+DWORD WINAPI NBSD_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("(%u, %04X, %08lX, %08lX, %08lX);\n",
+ wDevID, wMsg, dwUser, dwParam1, dwParam2);
+
+ switch (wMsg) {
+ case DRVM_INIT:
+ case DRVM_EXIT:
+ case DRVM_ENABLE:
+ case DRVM_DISABLE:
+ /* FIXME: Pretend this is supported */
+ return 0;
+ case WIDM_OPEN: return widOpen (wDevID, (LPWAVEOPENDESC)dwParam1, dwParam2);
+ case WIDM_CLOSE: return widClose (wDevID);
+ case WIDM_ADDBUFFER: return widAddBuffer (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_PREPARE: return widPrepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_UNPREPARE: return widUnprepare (wDevID, (LPWAVEHDR)dwParam1, dwParam2);
+ case WIDM_GETDEVCAPS: return widGetDevCaps (wDevID, (LPWAVEINCAPSA)dwParam1, dwParam2);
+ case WIDM_GETNUMDEVS: return numInDev;
+ case WIDM_GETPOS: return widGetPosition(wDevID, (LPMMTIME)dwParam1, dwParam2);
+ case WIDM_RESET: return widReset (wDevID);
+ case WIDM_START: return widStart (wDevID);
+ case WIDM_STOP: return widStop (wDevID);
+ case DRV_QUERYDEVICEINTERFACESIZE: return wdDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
+ case DRV_QUERYDEVICEINTERFACE: return wdDevInterface (wDevID, (PWCHAR)dwParam1, dwParam2);
+ case DRV_QUERYDSOUNDIFACE: return widDsCreate (wDevID, (PIDSCDRIVER*)dwParam1);
+ case DRV_QUERYDSOUNDDESC: return widDsDesc (wDevID, (PDSDRIVERDESC)dwParam1);
+ case DRV_QUERYDSOUNDGUID: return widDsGuid (wDevID, (LPGUID)dwParam1);
+ default:
+ FIXME("unknown message %u!\n", wMsg);
+ }
+ return MMSYSERR_NOTSUPPORTED;
+}
+
+/*======================================================================*
+ * Low level DSOUND capture definitions *
+ *======================================================================*/
+
+typedef struct IDsCaptureDriverPropertySetImpl IDsCaptureDriverPropertySetImpl;
+typedef struct IDsCaptureDriverNotifyImpl IDsCaptureDriverNotifyImpl;
+typedef struct IDsCaptureDriverImpl IDsCaptureDriverImpl;
+typedef struct IDsCaptureDriverBufferImpl IDsCaptureDriverBufferImpl;
+
+struct IDsCaptureDriverPropertySetImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriverPropertySet);
+ DWORD ref;
+
+ IDsCaptureDriverBufferImpl* capture_buffer;
+};
+
+struct IDsCaptureDriverNotifyImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsDriverNotify);
+ DWORD ref;
+
+ /* IDsDriverNotifyImpl fields */
+ LPDSBPOSITIONNOTIFY notifies;
+ int nrofnotifies;
+
+ IDsCaptureDriverBufferImpl* capture_buffer;
+};
+
+struct IDsCaptureDriverImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsCaptureDriver);
+ DWORD ref;
+
+ /* IDsCaptureDriverImpl fields */
+ UINT wDevID;
+ IDsCaptureDriverBufferImpl* capture_buffer;
+};
+
+struct IDsCaptureDriverBufferImpl
+{
+ /* IUnknown fields */
+ ICOM_VFIELD(IDsCaptureDriverBuffer);
+ DWORD ref;
+
+ /* IDsCaptureDriverBufferImpl fields */
+ IDsCaptureDriverImpl* drv;
+ DWORD buflen;
+ LPBYTE buffer;
+ DWORD writeptr;
+ LPBYTE mapping;
+ DWORD maplen;
+
+ /* IDsDriverNotifyImpl fields */
+ IDsCaptureDriverNotifyImpl* notify;
+ int notify_index;
+
+ /* IDsDriverPropertySetImpl fields */
+ IDsCaptureDriverPropertySetImpl* property_set;
+};
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
+ IDsCaptureDriverBufferImpl * dscdb,
+ IDsCaptureDriverPropertySetImpl **pdscdps);
+
+static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
+ IDsCaptureDriverBufferImpl * dsdcb,
+ IDsCaptureDriverNotifyImpl **pdscdn);
+
+/*======================================================================*
+ * Low level DSOUND capture property set implementation *
+ *======================================================================*/
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QueryInterface(
+ PIDSDRIVERPROPERTYSET iface,
+ REFIID riid,
+ LPVOID *ppobj)
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriverPropertySet) ) {
+ IDsDriverPropertySet_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsCaptureDriverPropertySetImpl_AddRef(PIDSDRIVERPROPERTYSET iface)
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedIncrement(&(This->ref));
+ return ref;
+}
+
+static ULONG WINAPI IDsCaptureDriverPropertySetImpl_Release(PIDSDRIVERPROPERTYSET iface)
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedDecrement(&(This->ref));
+ if (ref == 0) {
+ IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("(%p) released\n",This);
+ }
+ return ref;
+}
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Get(
+ PIDSDRIVERPROPERTYSET iface,
+ PDSPROPERTY pDsProperty,
+ LPVOID pPropertyParams,
+ ULONG cbPropertyParams,
+ LPVOID pPropertyData,
+ ULONG cbPropertyData,
+ PULONG pcbReturnedData )
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ FIXME("(%p,%p,%p,%lx,%p,%lx,%p)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData,pcbReturnedData);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Set(
+ PIDSDRIVERPROPERTYSET iface,
+ PDSPROPERTY pDsProperty,
+ LPVOID pPropertyParams,
+ ULONG cbPropertyParams,
+ LPVOID pPropertyData,
+ ULONG cbPropertyData )
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ FIXME("(%p,%p,%p,%lx,%p,%lx)\n",This,pDsProperty,pPropertyParams,cbPropertyParams,pPropertyData,cbPropertyData);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_QuerySupport(
+ PIDSDRIVERPROPERTYSET iface,
+ REFGUID PropertySetId,
+ ULONG PropertyId,
+ PULONG pSupport )
+{
+ ICOM_THIS(IDsCaptureDriverPropertySetImpl,iface);
+ FIXME("(%p,%s,%lx,%p)\n",This,debugstr_guid(PropertySetId),PropertyId,pSupport);
+ return DSERR_UNSUPPORTED;
+}
+
+ICOM_VTABLE(IDsDriverPropertySet) dscdpsvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsCaptureDriverPropertySetImpl_QueryInterface,
+ IDsCaptureDriverPropertySetImpl_AddRef,
+ IDsCaptureDriverPropertySetImpl_Release,
+ IDsCaptureDriverPropertySetImpl_Get,
+ IDsCaptureDriverPropertySetImpl_Set,
+ IDsCaptureDriverPropertySetImpl_QuerySupport,
+};
+
+/*======================================================================*
+ * Low level DSOUND capture notify implementation *
+ *======================================================================*/
+
+static HRESULT WINAPI IDsCaptureDriverNotifyImpl_QueryInterface(
+ PIDSDRIVERNOTIFY iface,
+ REFIID riid,
+ LPVOID *ppobj)
+{
+ ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsDriverNotify) ) {
+ IDsDriverNotify_AddRef(iface);
+ *ppobj = This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsCaptureDriverNotifyImpl_AddRef(PIDSDRIVERNOTIFY iface)
+{
+ ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedIncrement(&(This->ref));
+ return ref;
+}
+
+static ULONG WINAPI IDsCaptureDriverNotifyImpl_Release(PIDSDRIVERNOTIFY iface)
+{
+ ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
+ DWORD ref;
+ TRACE("(%p) ref was %ld\n", This, This->ref);
+
+ ref = InterlockedDecrement(&(This->ref));
+ if (ref == 0) {
+ IDsCaptureDriverBuffer_Release((PIDSCDRIVERBUFFER)This->capture_buffer);
+ if (This->notifies != NULL)
+ HeapFree(GetProcessHeap(), 0, This->notifies);
+
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("(%p) released\n",This);
+ }
+
+ return ref;
+}
+
+static HRESULT WINAPI IDsCaptureDriverNotifyImpl_SetNotificationPositions(
+ PIDSDRIVERNOTIFY iface,
+ DWORD howmuch,
+ LPCDSBPOSITIONNOTIFY notify)
+{
+ ICOM_THIS(IDsCaptureDriverNotifyImpl,iface);
+ TRACE("(%p,0x%08lx,%p)\n",This,howmuch,notify);
+
+ if (!notify) {
+ WARN("invalid parameter\n");
+ return DSERR_INVALIDPARAM;
+ }
+
+ if (TRACE_ON(wave)) {
+ int i;
+ for (i=0;i<howmuch;i++)
+ TRACE("notify at %ld to 0x%08lx\n",
+ notify[i].dwOffset,(DWORD)notify[i].hEventNotify);
+ }
+
+ /* Make an internal copy of the caller-supplied array.
+ * Replace the existing copy if one is already present. */
+ if (This->notifies)
+ This->notifies = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ This->notifies, howmuch * sizeof(DSBPOSITIONNOTIFY));
+ else
+ This->notifies = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,
+ howmuch * sizeof(DSBPOSITIONNOTIFY));
+
+ memcpy(This->notifies, notify, howmuch * sizeof(DSBPOSITIONNOTIFY));
+ This->nrofnotifies = howmuch;
+
+ return S_OK;
+}
+
+ICOM_VTABLE(IDsDriverNotify) dscdnvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsCaptureDriverNotifyImpl_QueryInterface,
+ IDsCaptureDriverNotifyImpl_AddRef,
+ IDsCaptureDriverNotifyImpl_Release,
+ IDsCaptureDriverNotifyImpl_SetNotificationPositions,
+};
+
+/*======================================================================*
+ * Low level DSOUND capture implementation *
+ *======================================================================*/
+
+static HRESULT DSCDB_MapBuffer(IDsCaptureDriverBufferImpl *dscdb)
+{
+ if (!dscdb->mapping) {
+ dscdb->mapping = mmap(NULL, dscdb->maplen, PROT_READ, MAP_SHARED,
+ WInDev[dscdb->drv->wDevID].nbsdDev->fd, 0);
+ if (dscdb->mapping == (LPBYTE)-1) {
+ TRACE("(%p): Could not map sound device for direct access (%s)\n", dscdb, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ TRACE("(%p): sound device has been mapped for direct access at %p, size=%ld\n", dscdb, dscdb->mapping, dscdb->maplen);
+ }
+ return DS_OK;
+}
+
+static HRESULT DSCDB_UnmapBuffer(IDsCaptureDriverBufferImpl *dscdb)
+{
+ if (dscdb->mapping) {
+ if (munmap(dscdb->mapping, dscdb->maplen) < 0) {
+ ERR("(%p): Could not unmap sound device (%s)\n", dscdb, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ dscdb->mapping = NULL;
+ TRACE("(%p): sound device unmapped\n", dscdb);
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_QueryInterface(PIDSCDRIVERBUFFER iface, REFIID riid, LPVOID *ppobj)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsCaptureDriverBuffer) ) {
+ IDsCaptureDriverBuffer_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ if ( IsEqualGUID( &IID_IDsDriverNotify, riid ) ) {
+ if (!This->notify)
+ IDsCaptureDriverNotifyImpl_Create(This, &(This->notify));
+ if (This->notify) {
+ IDsDriverNotify_AddRef((PIDSDRIVERNOTIFY)This->notify);
+ *ppobj = (LPVOID)This->notify;
+ return DS_OK;
+ }
+ *ppobj = 0;
+ return E_FAIL;
+ }
+
+ if ( IsEqualGUID( &IID_IDsDriverPropertySet, riid ) ) {
+ if (!This->property_set)
+ IDsCaptureDriverPropertySetImpl_Create(This, &(This->property_set));
+ if (This->property_set) {
+ IDsDriverPropertySet_AddRef((PIDSDRIVERPROPERTYSET)This->property_set);
+ *ppobj = (LPVOID)This->property_set;
+ return DS_OK;
+ }
+ *ppobj = 0;
+ return E_FAIL;
+ }
+
+ FIXME("(%p,%s,%p) unsupported GUID\n", This, debugstr_guid(riid), ppobj);
+
+ *ppobj = 0;
+
+ return DSERR_UNSUPPORTED;
+}
+
+static ULONG WINAPI IDsCaptureDriverBufferImpl_AddRef(PIDSCDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ This->ref++;
+ return This->ref;
+}
+
+static ULONG WINAPI IDsCaptureDriverBufferImpl_Release(PIDSCDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ if (--This->ref)
+ return This->ref;
+ DSCDB_UnmapBuffer(This);
+ if (This->notify)
+ IDsDriverNotify_Release((PIDSDRIVERNOTIFY)This->notify);
+ if (This->property_set)
+ IDsDriverPropertySet_Release((PIDSDRIVERPROPERTYSET)This->property_set);
+ HeapFree(GetProcessHeap(),0,This);
+ return 0;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_Lock(PIDSCDRIVERBUFFER iface,
+ LPVOID*ppvAudio1,LPDWORD pdwLen1,
+ LPVOID*ppvAudio2,LPDWORD pdwLen2,
+ DWORD dwWritePosition,DWORD dwWriteLen,
+ DWORD dwFlags)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ FIXME("(%p,%p,%p,%p,%p,%ld,%ld,0x%08lx): stub!\n",This,ppvAudio1,pdwLen1,ppvAudio2,pdwLen2,
+ dwWritePosition,dwWriteLen,dwFlags);
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_Unlock(PIDSCDRIVERBUFFER iface,
+ LPVOID pvAudio1,DWORD dwLen1,
+ LPVOID pvAudio2,DWORD dwLen2)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ FIXME("(%p,%p,%ld,%p,%ld): stub!\n",This,pvAudio1,dwLen1,pvAudio2,dwLen2);
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetPosition(PIDSCDRIVERBUFFER iface,
+ LPDWORD lpdwCapture,
+ LPDWORD lpdwRead)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ audio_offset_t info;
+ DWORD ptr;
+ TRACE("(%p,%p,%p)\n",This,lpdwCapture,lpdwRead);
+
+ if (WInDev[This->drv->wDevID].state == WINE_WS_CLOSED) {
+ ERR("device not open, but accessing?\n");
+ return DSERR_UNINITIALIZED;
+ }
+ if (ioctl(WInDev[This->drv->wDevID].nbsdDev->fd, AUDIO_GETIOFFS, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_GETIOFFS) failed (%s)\n", WInDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+ ptr = info.offset & ~3; /* align the pointer, just in case */
+ if (lpdwCapture) *lpdwCapture = ptr;
+ if (lpdwRead) {
+ /* add some safety margin (not strictly necessary, but...) */
+ if (WInDev[This->drv->wDevID].nbsdDev->in_caps_support & WAVECAPS_SAMPLEACCURATE)
+ *lpdwRead = ptr + 32;
+ else
+ *lpdwRead = ptr + WInDev[This->drv->wDevID].dwFragmentSize;
+ while (*lpdwRead > This->buflen)
+ *lpdwRead -= This->buflen;
+ }
+ TRACE("capturepos=%ld, readpos=%ld\n", lpdwCapture?*lpdwCapture:0, lpdwRead?*lpdwRead:0);
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_GetStatus(PIDSCDRIVERBUFFER iface, LPDWORD lpdwStatus)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ FIXME("(%p,%p): stub!\n",This,lpdwStatus);
+ return DSERR_UNSUPPORTED;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_Start(PIDSCDRIVERBUFFER iface, DWORD dwFlags)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ audio_info_t info;
+
+ TRACE("(%p,%lx)\n",This,dwFlags);
+ AUDIO_INITINFO(&info);
+ WInDev[This->drv->wDevID].nbsdDev->bInputEnabled = TRUE;
+ info.record.pause = 0;
+ if (ioctl(WInDev[This->drv->wDevID].nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause=0) failed (%s)\n", WInDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ WInDev[This->drv->wDevID].nbsdDev->bInputEnabled = FALSE;
+ return DSERR_GENERIC;
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_Stop(PIDSCDRIVERBUFFER iface)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ audio_info_t info;
+
+ TRACE("(%p)\n",This);
+ AUDIO_INITINFO(&info);
+ /* no more captureing */
+ WInDev[This->drv->wDevID].nbsdDev->bInputEnabled = FALSE;
+ info.record.pause = 1;
+ if (ioctl(WInDev[This->drv->wDevID].nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause=1) failed (%s)\n", WInDev[This->drv->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+
+ /* Most drivers just can't stop capturing without closing the device...
+ * so we need to somehow signal to our DirectSound implementation
+ * that it should completely recreate this HW buffer...
+ * this unexpected error code should do the trick... */
+ return DSERR_BUFFERLOST;
+}
+
+static HRESULT WINAPI IDsCaptureDriverBufferImpl_SetFormat(PIDSCDRIVERBUFFER iface, LPWAVEFORMATEX pwfx)
+{
+ ICOM_THIS(IDsCaptureDriverBufferImpl,iface);
+ FIXME("(%p): stub!\n",This);
+ return DSERR_UNSUPPORTED;
+}
+
+static ICOM_VTABLE(IDsCaptureDriverBuffer) dscdbvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsCaptureDriverBufferImpl_QueryInterface,
+ IDsCaptureDriverBufferImpl_AddRef,
+ IDsCaptureDriverBufferImpl_Release,
+ IDsCaptureDriverBufferImpl_Lock,
+ IDsCaptureDriverBufferImpl_Unlock,
+ IDsCaptureDriverBufferImpl_SetFormat,
+ IDsCaptureDriverBufferImpl_GetPosition,
+ IDsCaptureDriverBufferImpl_GetStatus,
+ IDsCaptureDriverBufferImpl_Start,
+ IDsCaptureDriverBufferImpl_Stop
+};
+
+static HRESULT WINAPI IDsCaptureDriverImpl_QueryInterface(PIDSCDRIVER iface, REFIID riid, LPVOID *ppobj)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p,%s,%p)\n",This,debugstr_guid(riid),ppobj);
+
+ if ( IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDsCaptureDriver) ) {
+ IDsCaptureDriver_AddRef(iface);
+ *ppobj = (LPVOID)This;
+ return DS_OK;
+ }
+
+ FIXME( "Unknown IID %s\n", debugstr_guid( riid ) );
+
+ *ppobj = 0;
+
+ return E_NOINTERFACE;
+}
+
+static ULONG WINAPI IDsCaptureDriverImpl_AddRef(PIDSCDRIVER iface)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ This->ref++;
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+}
+
+static ULONG WINAPI IDsCaptureDriverImpl_Release(PIDSCDRIVER iface)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ if (--This->ref) {
+ TRACE("ref=%ld\n",This->ref);
+ return This->ref;
+ }
+ HeapFree(GetProcessHeap(),0,This);
+ TRACE("ref=0\n");
+ return 0;
+}
+
+static HRESULT WINAPI IDsCaptureDriverImpl_GetDriverDesc(PIDSCDRIVER iface, PDSDRIVERDESC pDesc)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p,%p)\n",This,pDesc);
+
+ if (!pDesc) {
+ TRACE("invalid parameter\n");
+ return DSERR_INVALIDPARAM;
+ }
+
+ /* copy version from driver */
+ memcpy(pDesc, &(WInDev[This->wDevID].nbsdDev->ds_desc), sizeof(DSDRIVERDESC));
+
+ pDesc->dwFlags |= DSDDESC_DOMMSYSTEMOPEN | DSDDESC_DOMMSYSTEMSETFORMAT |
+ DSDDESC_USESYSTEMMEMORY | DSDDESC_DONTNEEDPRIMARYLOCK |
+ DSDDESC_DONTNEEDSECONDARYLOCK;
+ pDesc->dnDevNode = WInDev[This->wDevID].waveDesc.dnDevNode;
+ pDesc->wVxdId = 0;
+ pDesc->wReserved = 0;
+ pDesc->ulDeviceNum = This->wDevID;
+ pDesc->dwHeapType = DSDHEAP_NOHEAP;
+ pDesc->pvDirectDrawHeap = NULL;
+ pDesc->dwMemStartAddress = 0;
+ pDesc->dwMemEndAddress = 0;
+ pDesc->dwMemAllocExtra = 0;
+ pDesc->pvReserved1 = NULL;
+ pDesc->pvReserved2 = NULL;
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverImpl_Open(PIDSCDRIVER iface)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverImpl_Close(PIDSCDRIVER iface)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p)\n",This);
+ if (This->capture_buffer) {
+ ERR("problem with DirectSound: capture buffer not released\n");
+ return DSERR_GENERIC;
+ }
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverImpl_GetCaps(PIDSCDRIVER iface, PDSCDRIVERCAPS pCaps)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ TRACE("(%p,%p)\n",This,pCaps);
+ memcpy(pCaps, &(WInDev[This->wDevID].nbsdDev->dsc_caps), sizeof(DSCDRIVERCAPS));
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverImpl_CreateCaptureBuffer(PIDSCDRIVER iface,
+ LPWAVEFORMATEX pwfx,
+ DWORD dwFlags,
+ DWORD dwCardAddress,
+ LPDWORD pdwcbBufferSize,
+ LPBYTE *ppbBuffer,
+ LPVOID *ppvObj)
+{
+ ICOM_THIS(IDsCaptureDriverImpl,iface);
+ IDsCaptureDriverBufferImpl** ippdscdb = (IDsCaptureDriverBufferImpl**)ppvObj;
+ HRESULT err;
+ audio_info_t info;
+
+ TRACE("(%p,%p,%lx,%lx,%p,%p,%p)\n",This,pwfx,dwFlags,dwCardAddress,pdwcbBufferSize,ppbBuffer,ppvObj);
+
+ if (This->capture_buffer) {
+ TRACE("already allocated\n");
+ return DSERR_ALLOCATED;
+ }
+
+ *ippdscdb = (IDsCaptureDriverBufferImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverBufferImpl));
+ if (*ippdscdb == NULL) {
+ TRACE("out of memory\n");
+ return DSERR_OUTOFMEMORY;
+ }
+
+ (*ippdscdb)->lpVtbl = &dscdbvt;
+ (*ippdscdb)->ref = 1;
+ (*ippdscdb)->drv = This;
+ (*ippdscdb)->notify = 0;
+ (*ippdscdb)->notify_index = 0;
+ (*ippdscdb)->property_set = 0;
+
+ if (WInDev[This->wDevID].state == WINE_WS_CLOSED) {
+ WAVEOPENDESC desc;
+ desc.hWave = 0;
+ desc.lpFormat = pwfx;
+ desc.dwCallback = 0;
+ desc.dwInstance = 0;
+ desc.uMappedDeviceID = 0;
+ desc.dnDevNode = 0;
+ err = widOpen(This->wDevID, &desc, dwFlags | WAVE_DIRECTSOUND);
+ if (err != MMSYSERR_NOERROR) {
+ TRACE("widOpen failed\n");
+ return err;
+ }
+ }
+
+ /* check how big the DMA buffer is now */
+ if (ioctl(WInDev[This->wDevID].nbsdDev->fd, AUDIO_GETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_GETINFO) failed (%s)\n", WInDev[This->wDevID].nbsdDev->dev_name, strerror(errno));
+ HeapFree(GetProcessHeap(),0,*ippdscdb);
+ *ippdscdb = NULL;
+ return DSERR_GENERIC;
+ }
+ (*ippdscdb)->maplen = (*ippdscdb)->buflen = info.record.buffer_size;
+
+ /* map the DMA buffer */
+ err = DSCDB_MapBuffer(*ippdscdb);
+ if (err != DS_OK) {
+ HeapFree(GetProcessHeap(),0,*ippdscdb);
+ *ippdscdb = NULL;
+ return err;
+ }
+
+ /* capture buffer is ready to go */
+ *pdwcbBufferSize = (*ippdscdb)->maplen;
+ *ppbBuffer = (*ippdscdb)->mapping;
+
+ /* some drivers need some extra nudging after mapping */
+ WInDev[This->wDevID].nbsdDev->bInputEnabled = FALSE;
+ AUDIO_INITINFO(&info);
+ info.record.pause = 1;
+ if (ioctl(WInDev[This->wDevID].nbsdDev->fd, AUDIO_SETINFO, &info) < 0) {
+ ERR("ioctl(%s, AUDIO_SETINFO pause=1) failed (%s)\n", WInDev[This->wDevID].nbsdDev->dev_name, strerror(errno));
+ return DSERR_GENERIC;
+ }
+
+ This->capture_buffer = *ippdscdb;
+
+ return DS_OK;
+}
+
+static ICOM_VTABLE(IDsCaptureDriver) dscdvt =
+{
+ ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
+ IDsCaptureDriverImpl_QueryInterface,
+ IDsCaptureDriverImpl_AddRef,
+ IDsCaptureDriverImpl_Release,
+ IDsCaptureDriverImpl_GetDriverDesc,
+ IDsCaptureDriverImpl_Open,
+ IDsCaptureDriverImpl_Close,
+ IDsCaptureDriverImpl_GetCaps,
+ IDsCaptureDriverImpl_CreateCaptureBuffer
+};
+
+static HRESULT WINAPI IDsCaptureDriverPropertySetImpl_Create(
+ IDsCaptureDriverBufferImpl * dscdb,
+ IDsCaptureDriverPropertySetImpl **pdscdps)
+{
+ IDsCaptureDriverPropertySetImpl * dscdps;
+ TRACE("(%p,%p)\n",dscdb,pdscdps);
+
+ dscdps = (IDsCaptureDriverPropertySetImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdps));
+ if (dscdps == NULL) {
+ WARN("out of memory\n");
+ return DSERR_OUTOFMEMORY;
+ }
+
+ dscdps->ref = 0;
+ dscdps->lpVtbl = &dscdpsvt;
+ dscdps->capture_buffer = dscdb;
+ dscdb->property_set = dscdps;
+ IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
+
+ *pdscdps = dscdps;
+ return DS_OK;
+}
+
+static HRESULT WINAPI IDsCaptureDriverNotifyImpl_Create(
+ IDsCaptureDriverBufferImpl * dscdb,
+ IDsCaptureDriverNotifyImpl **pdscdn)
+{
+ IDsCaptureDriverNotifyImpl * dscdn;
+ TRACE("(%p,%p)\n",dscdb,pdscdn);
+
+ dscdn = (IDsCaptureDriverNotifyImpl*)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(dscdn));
+ if (dscdn == NULL) {
+ WARN("out of memory\n");
+ return DSERR_OUTOFMEMORY;
+ }
+
+ dscdn->ref = 0;
+ dscdn->lpVtbl = &dscdnvt;
+ dscdn->capture_buffer = dscdb;
+ dscdb->notify = dscdn;
+ IDsCaptureDriverBuffer_AddRef((PIDSCDRIVER)dscdb);
+
+ *pdscdn = dscdn;
+ return DS_OK;
+};
+
+static DWORD widDsCreate(UINT wDevID, PIDSCDRIVER* drv)
+{
+ IDsCaptureDriverImpl** idrv = (IDsCaptureDriverImpl**)drv;
+ TRACE("(%d,%p)\n",wDevID,drv);
+
+ /* the HAL isn't much better than the HEL if we can't do mmap() */
+ if (!(WInDev[wDevID].nbsdDev->in_caps_support & WAVECAPS_DIRECTSOUND)) {
+ ERR("DirectSoundCapture flag not set\n");
+ MESSAGE("This sound card's driver does not support direct access\n");
+ MESSAGE("The (slower) DirectSound HEL mode will be used instead.\n");
+ return MMSYSERR_NOTSUPPORTED;
+ }
+
+ *idrv = (IDsCaptureDriverImpl*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,sizeof(IDsCaptureDriverImpl));
+ if (!*idrv)
+ return MMSYSERR_NOMEM;
+ (*idrv)->lpVtbl = &dscdvt;
+ (*idrv)->ref = 1;
+
+ (*idrv)->wDevID = wDevID;
+ (*idrv)->capture_buffer = NULL;
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD widDsDesc(UINT wDevID, PDSDRIVERDESC desc)
+{
+ memcpy(desc, &(WInDev[wDevID].nbsdDev->ds_desc), sizeof(DSDRIVERDESC));
+ return MMSYSERR_NOERROR;
+}
+
+static DWORD widDsGuid(UINT wDevID, LPGUID pGuid)
+{
+ TRACE("(%d,%p)\n",wDevID,pGuid);
+
+ memcpy(pGuid, &(WInDev[wDevID].nbsdDev->dsc_guid), sizeof(GUID));
+
+ return MMSYSERR_NOERROR;
+}
+
+#else /* !HAVE_NBSDAUDIOIO */
+
+/**************************************************************************
+ * wodMessage (WINENBSD.4)
+ */
+DWORD WINAPI NBSD_wodMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+ return MMSYSERR_NOTENABLED;
+}
+
+/**************************************************************************
+ * widMessage (WINENBSD.3)
+ */
+DWORD WINAPI NBSD_widMessage(WORD wDevID, WORD wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+ return MMSYSERR_NOTENABLED;
+}
+
+#endif /* HAVE_NBSDAUDIOIO */
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/mmaux.c ./dlls/winmm/winenbsd/mmaux.c
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/mmaux.c Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/mmaux.c Sun Feb 22 00:28:45 2004
@@ -0,0 +1,341 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+/*
+ * Wine Driver for NetBSD audioio
+ * Derived from the Wine OSS Sample Driver
+ *
+ * Copyright 1994 Martin Ayotte
+ * 2004 Yorick Hardy (NetBSD audioio)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#define EMULATE_SB16
+
+#include "config.h"
+
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_SYS_IOCTL_H
+# include <sys/ioctl.h>
+#endif
+
+#include "windef.h"
+#include "winbase.h"
+#include "mmddk.h"
+#include "nbsd.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(mmaux);
+
+#ifdef HAVE_NBSDAUDIOIO
+
+#define MIXER_DEV "/dev/mixer"
+
+static int NumDev = 6;
+
+/*-----------------------------------------------------------------------*/
+
+static int AUXDRV_Init(void)
+{
+ int mixer;
+
+ if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ WARN("mixer device not available !\n");
+ NumDev = 0;
+ } else {
+ close(mixer);
+ NumDev = 6;
+ }
+ return NumDev;
+}
+
+/**************************************************************************
+ * AUX_GetDevCaps [internal]
+ */
+static DWORD AUX_GetDevCaps(WORD wDevID, LPAUXCAPSA lpCaps, DWORD dwSize)
+{
+ int mixer;
+ audio_device_t dev;
+
+ TRACE("(%04X, %p, %lu);\n", wDevID, lpCaps, dwSize);
+ if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+ if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ WARN("mixer device not available !\n");
+ return MMSYSERR_NOTENABLED;
+ }
+ if (ioctl(mixer, AUDIO_GETDEV, &dev) == -1) {
+ close(mixer);
+ WARN("unable to read mixer !\n");
+ return MMSYSERR_NOTENABLED;
+ }
+ close(mixer);
+#ifdef EMULATE_SB16
+ lpCaps->wMid = 0x0002;
+ lpCaps->vDriverVersion = 0x0200;
+ lpCaps->dwSupport = AUXCAPS_VOLUME | AUXCAPS_LRVOLUME;
+ switch (wDevID) {
+ case 0:
+ lpCaps->wPid = 0x0196;
+ strcpy(lpCaps->szPname, "SB16 Aux: Wave");
+ lpCaps->wTechnology = AUXCAPS_AUXIN;
+ break;
+ case 1:
+ lpCaps->wPid = 0x0197;
+ strcpy(lpCaps->szPname, "SB16 Aux: Midi Synth");
+ lpCaps->wTechnology = AUXCAPS_AUXIN;
+ break;
+ case 2:
+ lpCaps->wPid = 0x0191;
+ strcpy(lpCaps->szPname, "SB16 Aux: CD");
+ lpCaps->wTechnology = AUXCAPS_CDAUDIO;
+ break;
+ case 3:
+ lpCaps->wPid = 0x0192;
+ strcpy(lpCaps->szPname, "SB16 Aux: Line-In");
+ lpCaps->wTechnology = AUXCAPS_AUXIN;
+ break;
+ case 4:
+ lpCaps->wPid = 0x0193;
+ strcpy(lpCaps->szPname, "SB16 Aux: Mic");
+ lpCaps->wTechnology = AUXCAPS_AUXIN;
+ break;
+ case 5:
+ lpCaps->wPid = 0x0194;
+ strcpy(lpCaps->szPname, "SB16 Aux: Master");
+ lpCaps->wTechnology = AUXCAPS_AUXIN;
+ break;
+ }
+#else
+ lpCaps->wMid = 0xAA;
+ lpCaps->wPid = 0x55;
+ lpCaps->vDriverVersion = 0x0100;
+ strcpy(lpCaps->szPname, "Generic Wine Auxiliary Driver");
+ lpCaps->wTechnology = AUXCAPS_CDAUDIO;
+ lpCaps->dwSupport = AUXCAPS_VOLUME | AUXCAPS_LRVOLUME;
+#endif
+ return MMSYSERR_NOERROR;
+}
+
+
+/**************************************************************************
+ * AUX_GetVolume [internal]
+ */
+static DWORD AUX_GetVolume(WORD wDevID, LPDWORD lpdwVol)
+{
+ int i, mixer, left, right;
+ char *name = NULL;
+ mixer_devinfo_t devinfo;
+ mixer_ctrl_t ctrl;
+
+ TRACE("(%04X, %p);\n", wDevID, lpdwVol);
+ if (lpdwVol == NULL) return MMSYSERR_NOTENABLED;
+ if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ WARN("mixer device not available !\n");
+ return MMSYSERR_NOTENABLED;
+ }
+ switch(wDevID) {
+ case 0:
+ TRACE("AudioNwave !\n");
+ name = AudioNwave;
+ break;
+ case 1:
+ TRACE("AudioNmidi !\n");
+ name = AudioNmidi;
+ break;
+ case 2:
+ TRACE("AudioNcd !\n");
+ name = AudioNcd;
+ break;
+ case 3:
+ TRACE("AudioNline !\n");
+ name = AudioNline;
+ break;
+ case 4:
+ TRACE("AudioNmicrophone !\n");
+ name = AudioNmicrophone;
+ break;
+ case 5:
+ TRACE("AudioNvolume !\n");
+ name = AudioNvolume;
+ break;
+ default:
+ WARN("invalid device id=%04X !\n", wDevID);
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+
+ if (name == NULL) {
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+
+ for (i = devinfo.next = 0; devinfo.next != AUDIO_MIXER_LAST; i++) {
+ if (ioctl(mixer, AUDIO_MIXER_DEVINFO, &devinfo) == -1) {
+ WARN("unable to read mixer !\n");
+ }
+ if ( ! strcmp(devinfo.label.name, name) ) break;
+ }
+ if (devinfo.next == AUDIO_MIXER_LAST) {
+ WARN("unable to read mixer !\n");
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ } else {
+ ctrl.dev = i;
+ if (ioctl(mixer, AUDIO_MIXER_READ, &ctrl) == -1
+ || ctrl.type != AUDIO_MIXER_VALUE) {
+ WARN("unable to read mixer !\n");
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+ }
+
+ close(mixer);
+ left = ctrl.un.value.level[0];
+ right = (ctrl.un.value.num_channels > 1) ?
+ ctrl.un.value.level[2] : left;
+ TRACE("left=%d right=%d !\n", left, right);
+ *lpdwVol = MAKELONG((left * 0xFFFFL) / AUDIO_MAX_GAIN,
+ (right * 0xFFFFL) / AUDIO_MAX_GAIN);
+ return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ * AUX_SetVolume [internal]
+ */
+static DWORD AUX_SetVolume(WORD wDevID, DWORD dwParam)
+{
+ int mixer;
+ int i, left, right;
+ char *name = NULL;
+ mixer_devinfo_t devinfo;
+ mixer_ctrl_t ctrl;
+
+ TRACE("(%04X, %08lX);\n", wDevID, dwParam);
+
+ if ((mixer = open(MIXER_DEV, O_RDWR)) < 0) {
+ WARN("mixer device not available !\n");
+ return MMSYSERR_NOTENABLED;
+ }
+
+ switch(wDevID) {
+ case 0:
+ TRACE("AudioNwave !\n");
+ name = AudioNwave;
+ break;
+ case 1:
+ TRACE("AudioNmidi !\n");
+ name = AudioNmidi;
+ break;
+ case 2:
+ TRACE("AudioNcd !\n");
+ name = AudioNcd;
+ break;
+ case 3:
+ TRACE("AudioNcd !\n");
+ name = AudioNcd;
+ break;
+ case 4:
+ TRACE("AudioNmicrophone !\n");
+ name = AudioNmicrophone;
+ break;
+ case 5:
+ TRACE("AudioNvolume !\n");
+ name = AudioNvolume;
+ break;
+ default:
+ WARN("invalid device id=%04X !\n", wDevID);
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+
+ if (name == NULL) {
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+
+ for (i = devinfo.next = 0; devinfo.next != AUDIO_MIXER_LAST; i++) {
+ if (ioctl(mixer, AUDIO_MIXER_DEVINFO, &devinfo) == -1) {
+ WARN("unable to write mixer !\n");
+ }
+ if ( ! strcmp(devinfo.label.name, name) ) break;
+ }
+ if (devinfo.next == AUDIO_MIXER_LAST) {
+ WARN("unable to write mixer !\n");
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ } else {
+ ctrl.dev = i;
+ ctrl.type = AUDIO_MIXER_VALUE;
+ left = (LOWORD(dwParam) * 255) >> 16;
+ right = (HIWORD(dwParam) * 255) >> 16;
+ ctrl.un.value.level[0] = (u_char)left;
+ if (ctrl.un.value.num_channels > 1)
+ ctrl.un.value.level[1] = (u_char)right;
+
+ if (ioctl(mixer, AUDIO_MIXER_WRITE, &ctrl) == -1
+ || ctrl.type != AUDIO_MIXER_VALUE) {
+ WARN("unable to write mixer !\n");
+ close(mixer);
+ return MMSYSERR_NOTENABLED;
+ }
+ }
+
+ close(mixer);
+ return MMSYSERR_NOERROR;
+}
+
+#endif
+
+/**************************************************************************
+ * auxMessage (WINENBSD.2)
+ */
+DWORD WINAPI NBSD_auxMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+ DWORD dwParam1, DWORD dwParam2)
+{
+ TRACE("(%04X, %04X, %08lX, %08lX, %08lX);\n",
+ wDevID, wMsg, dwUser, dwParam1, dwParam2);
+
+#ifdef HAVE_NBSDAUDIOIO
+ switch (wMsg) {
+ case DRVM_INIT:
+ AUXDRV_Init();
+ /* fall through */
+ case DRVM_EXIT:
+ case DRVM_ENABLE:
+ case DRVM_DISABLE:
+ /* FIXME: Pretend this is supported */
+ return 0;
+ case AUXDM_GETDEVCAPS:
+ return AUX_GetDevCaps(wDevID, (LPAUXCAPSA)dwParam1,dwParam2);
+ case AUXDM_GETNUMDEVS:
+ TRACE("return %d;\n", NumDev);
+ return NumDev;
+ case AUXDM_GETVOLUME:
+ return AUX_GetVolume(wDevID, (LPDWORD)dwParam1);
+ case AUXDM_SETVOLUME:
+ return AUX_SetVolume(wDevID, dwParam1);
+ default:
+ WARN("unknown message !\n");
+ }
+ return MMSYSERR_NOTSUPPORTED;
+#else
+ return MMSYSERR_NOTENABLED;
+#endif
+}
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/nbsd.c ./dlls/winmm/winenbsd/nbsd.c
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/nbsd.c Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/nbsd.c Sun Feb 22 00:28:54 2004
@@ -0,0 +1,95 @@
+/* -*- tab-width: 8; c-basic-offset: 4 -*- */
+/*
+ * Wine Driver for NetBSD audioio
+ * Derived from the Wine OSS Sample Driver
+ *
+ * Copyright 1999 Eric Pouech
+ * 2004 Yorick Hardy (NetBSD audioio)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "nbsd.h"
+
+#ifdef HAVE_NBSDAUDIOIO
+
+static struct WINE_NBSD* nbsd = NULL;
+
+/**************************************************************************
+ * NBSD_drvOpen [internal]
+ */
+static DWORD NBSD_drvOpen(LPSTR str)
+{
+ if (nbsd)
+ return 0;
+
+ /* I know, this is ugly, but who cares... */
+ nbsd = (struct WINE_NBSD*)1;
+ return 1;
+}
+
+/**************************************************************************
+ * NBSD_drvClose [internal]
+ */
+static DWORD NBSD_drvClose(DWORD dwDevID)
+{
+ if (nbsd) {
+ nbsd = NULL;
+ return 1;
+ }
+ return 0;
+}
+
+#endif
+
+
+/**************************************************************************
+ * DriverProc (WINENBSD.1)
+ */
+LONG CALLBACK NBSD_DriverProc(DWORD dwDevID, HDRVR hDriv, DWORD wMsg,
+ DWORD dwParam1, DWORD dwParam2)
+{
+/* EPP TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n", */
+/* EPP dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
+
+ switch(wMsg) {
+#ifdef HAVE_NBSDAUDIOIO
+ case DRV_LOAD: NBSD_WaveInit();
+ return 1;
+ case DRV_FREE: return 1;
+ case DRV_OPEN: return NBSD_drvOpen((LPSTR)dwParam1);
+ case DRV_CLOSE: return NBSD_drvClose(dwDevID);
+ case DRV_ENABLE: return 1;
+ case DRV_DISABLE: return 1;
+ case DRV_QUERYCONFIGURE: return 1;
+ case DRV_CONFIGURE: MessageBoxA(0, "NetBSD AudioIO MultiMedia Driver !", "NetBSD AudioIO Driver", MB_OK); return 1;
+ case DRV_INSTALL: return DRVCNF_RESTART;
+ case DRV_REMOVE: return DRVCNF_RESTART;
+#endif
+ default:
+ return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+ }
+}
+
+
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/nbsd.h ./dlls/winmm/winenbsd/nbsd.h
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/nbsd.h Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/nbsd.h Sun Feb 22 00:28:17 2004
@@ -0,0 +1,35 @@
+/* Definition for NetBSD audioio drivers : wine multimedia system
+ *
+ * Copyright 1999 Eric Pouech
+ * 2004 Yorick Hardy (NetBSD audioio)
+ *
+ * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#ifndef __WINE_CONFIG_H
+# error You must include config.h to use this header
+#endif
+
+#ifdef HAVE_SYS_AUDIOIO_H
+# include <sys/types.h>
+# include <sys/audioio.h>
+#endif
+
+#ifdef HAVE_SYS_ERRNO_H
+# include <sys/errno.h>
+#endif
+
+extern LONG NBSD_WaveInit(void);
+extern BOOL NBSD_MidiInit(void);
diff -urN /home/yorick/software/wine/wine/dlls/winmm/winenbsd/winenbsd.drv.spec ./dlls/winmm/winenbsd/winenbsd.drv.spec
--- /home/yorick/software/wine/wine/dlls/winmm/winenbsd/winenbsd.drv.spec Thu Jan 1 02:00:00 1970
+++ ./dlls/winmm/winenbsd/winenbsd.drv.spec Sat Feb 21 23:57:46 2004
@@ -0,0 +1,4 @@
+1 stdcall DriverProc(long long long long long) NBSD_DriverProc
+2 stdcall auxMessage(long long long long long) NBSD_auxMessage
+3 stdcall widMessage(long long long long long) NBSD_widMessage
+4 stdcall wodMessage(long long long long long) NBSD_wodMessage
diff -urN /home/yorick/software/wine/wine/documentation/samples/config ./documentation/samples/config
--- /home/yorick/software/wine/wine/documentation/samples/config Sat Jan 10 16:47:35 2004
+++ ./documentation/samples/config Sat Feb 21 23:45:04 2004
@@ -253,6 +253,7 @@
;"Drivers" = "winealsa.drv" ; for ALSA users
;"Drivers" = "winejack.drv" ; for Jack sound server
;"Drivers" = "winenas.drv" ; for NAS sound system
+;"Drivers" = "winenbsd.drv" ; for NetBSD audioio sound system
;"Drivers" = "wineaudioio.drv" ; for Solaris machines
;"Drivers" = "" ; to disable sound
"WaveMapper" = "msacm.drv"
diff -urN /home/yorick/software/wine/wine/include/config.h.in ./include/config.h.in
--- /home/yorick/software/wine/wine/include/config.h.in Sun Feb 8 13:09:34 2004
+++ ./include/config.h.in Sun Feb 22 12:05:15 2004
@@ -32,6 +32,9 @@
/* Define if you have ARTS sound server */
#undef HAVE_ARTS
+/* Define to 1 if you have the <sys/audioio.h> header file. */
+#undef HAVE_SYS_AUDIOIO_H
+
/* Define to 1 if you have the <audio/audiolib.h> header file. */
#undef HAVE_AUDIO_AUDIOLIB_H
@@ -352,6 +355,9 @@
/* Define to 1 if you have the <ncurses.h> header file. */
#undef HAVE_NCURSES_H
+
+/* Define if you have NetBSD audioio */
+#undef HAVE_NBSDAUDIOIO
/* Define to 1 if you have the <netdb.h> header file. */
#undef HAVE_NETDB_H
diff -urN /home/yorick/software/wine/wine/programs/winecfg/properties.c ./programs/winecfg/properties.c
--- /home/yorick/software/wine/wine/programs/winecfg/properties.c Sun Feb 8 13:09:53 2004
+++ ./programs/winecfg/properties.c Sat Feb 21 23:46:57 2004
@@ -86,6 +86,7 @@
{"OSS", "wineoss.drv"},
{"Jack", "winejack.drv"},
{"Nas", "winenas.drv"},
+ {"NetBSD", "winenbsd.drv"},
{"Audio IO(Solaris)", "wineaudioio.drv"},
{"Disable sound", ""},
{"", ""}
diff -urN /home/yorick/software/wine/wine/tools/winapi/win32.api ./tools/winapi/win32.api
--- /home/yorick/software/wine/wine/tools/winapi/win32.api Sat Aug 9 09:54:52 2003
+++ ./tools/winapi/win32.api Sat Feb 21 23:48:31 2004
@@ -3367,6 +3367,16 @@
WORD
UINT
+%%winenbsd.drv
+
+%long
+
+DWORD
+HDRVR
+LONG
+WORD
+UINT
+
%%wineoss.drv
%long
More information about the wine-patches
mailing list