Name: winePatchOpenALWinmmAndThunk2
ChangeLog: OpenAL Winmm Driver and OpenAL32.dll thunk
GenDate: 2006/11/24 08:11:45 UTC
ModifiedFiles: configure.ac dlls/Makefile.in include/config.h.in
AddedFiles: dlls/winmm/wineopenal/audio.c dlls/winmm/wineopenal/Makefile.in dlls/winmm/wineopenal/openal.c dlls/winmm/wineopenal/openal.h dlls/winmm/wineopenal/wineopenal.drv.spec dlls/openal32/openal.c dlls/openal32/Makefile.in dlls/openal32/openal32.spec
Index: configure.ac
===================================================================
RCS file: /home/wine/wine/configure.ac,v
retrieving revision 1.585
diff -u -r1.585 configure.ac
--- configure.ac	16 Nov 2006 14:02:21 -0000	1.585
+++ configure.ac	24 Nov 2006 08:11:29 -0000
@@ -146,11 +146,14 @@
 AC_SUBST(OPENGLFILES,"")
 AC_SUBST(GLU32FILES,"")
 AC_SUBST(OPENGL_LIBS,"")
+AC_SUBST(OPENAL,"")
 AC_SUBST(QUARTZFILES,"")
 
 dnl **** Check for header files ****
 
 AC_CHECK_HEADERS(\
+	OpenAL/al.h \
+	AL/al.h \
 	AudioUnit/AudioUnit.h \
 	Carbon/Carbon.h \
 	CoreAudio/CoreAudio.h \
@@ -877,6 +880,15 @@
     AC_CHECK_LIB(capi20,capi20_register,[AC_DEFINE(HAVE_CAPI4LINUX,1,[Define if you have capi4linux libs and headers])])
 fi
 
+dnl **** Check for OpenAL support ****
+dnl **** Mac OSX OpenAL support checked below ****
+
+if test "$ac_cv_header_AL_al_h" = "yes"
+	then
+	dnl OpenAL framework
+	AC_SUBST(OPENAL,"-lopenal")
+fi
+
 dnl **** Check for gcc specific options ****
 
 AC_SUBST(EXTRACFLAGS,"")
@@ -1028,6 +1040,11 @@
         dnl CoreServices needed by AudioUnit
         AC_SUBST(COREAUDIO,"-framework CoreAudio -framework AudioUnit -framework CoreServices")
     fi
+	if test "$ac_cv_header_OpenAL_al_h" = "yes"
+	then
+		dnl OpenAL framework
+		AC_SUBST(OPENAL,"-framework OpenAL")
+	fi
     case $host_cpu in
       *powerpc*)
         LDDLLFLAGS="$LDDLLFLAGS -read_only_relocs warning"  dnl FIXME
@@ -1679,6 +1696,7 @@
 dlls/oledlg/Makefile
 dlls/olepro32/Makefile
 dlls/olesvr32/Makefile
+dlls/openal32/Makefile
 dlls/opengl32/Makefile
 dlls/powrprof/Makefile
 dlls/psapi/Makefile
@@ -1766,6 +1784,7 @@
 dlls/winmm/wineesd/Makefile
 dlls/winmm/winejack/Makefile
 dlls/winmm/winenas/Makefile
+dlls/winmm/wineopenal/Makefile
 dlls/winmm/wineoss/Makefile
 dlls/winnls32/Makefile
 dlls/winspool.drv/Makefile
Index: dlls/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/Makefile.in,v
retrieving revision 1.333
diff -u -r1.333 Makefile.in
--- dlls/Makefile.in	16 Nov 2006 14:02:21 -0000	1.333
+++ dlls/Makefile.in	24 Nov 2006 08:11:30 -0000
@@ -133,6 +133,7 @@
 	oledlg \
 	olepro32 \
 	olesvr32 \
+	openal32 \
 	powrprof \
 	psapi \
 	pstorec \
@@ -195,6 +196,7 @@
 	winmm/wineesd \
 	winmm/winejack \
 	winmm/winenas \
+	winmm/wineopenal \
 	winmm/wineoss \
 	winnls32 \
 	winspool.drv \
@@ -351,6 +353,7 @@
 	wineesd.drv.so \
 	winejack.drv.so \
 	winenas.drv.so \
+	wineopenal.drv.so \
 	wineoss.drv.so
 
 # Main target
@@ -389,6 +392,9 @@
 winenas.drv.so: winmm/winenas/winenas.drv.so
 	$(RM) $@ && $(LN_S) winmm/winenas/winenas.drv.so $@
 
+wineopenal.drv.so: winmm/wineopenal/wineopenal.drv.so
+	$(RM) $@ && $(LN_S) winmm/wineopenal/wineopenal.drv.so $@
+
 wineoss.drv.so: winmm/wineoss/wineoss.drv.so
 	$(RM) $@ && $(LN_S) winmm/wineoss/wineoss.drv.so $@
 
@@ -989,6 +995,7 @@
 winmm/wineesd/wineesd.drv.so: winmm/wineesd
 winmm/winejack/winejack.drv.so: winmm/winejack
 winmm/winenas/winenas.drv.so: winmm/winenas
+winmm/wineopenal/wineopenal.drv.so: winmm/wineopenal
 winmm/wineoss/wineoss.drv.so: winmm/wineoss
 dxerr8/libdxerr8.a: dxerr8
 dxerr9/libdxerr9.a: dxerr9
Index: include/config.h.in
===================================================================
RCS file: /home/wine/wine/include/config.h.in,v
retrieving revision 1.271
diff -u -r1.271 config.h.in
--- include/config.h.in	13 Nov 2006 15:26:28 -0000	1.271
+++ include/config.h.in	24 Nov 2006 08:11:30 -0000
@@ -11,6 +11,9 @@
 /* Define to 1 if you have the <alsa/asoundlib.h> header file. */
 #undef HAVE_ALSA_ASOUNDLIB_H
 
+/* Define to 1 if you have the <AL/al.h> header file. */
+#undef HAVE_AL_AL_H
+
 /* Define to 1 if you have the <arpa/inet.h> header file. */
 #undef HAVE_ARPA_INET_H
 
@@ -492,6 +495,9 @@
 /* Define to 1 if the system has the type `off_t'. */
 #undef HAVE_OFF_T
 
+/* Define to 1 if you have the <OpenAL/al.h> header file. */
+#undef HAVE_OPENAL_AL_H
+
 /* Define if OpenGL is present on the system */
 #undef HAVE_OPENGL
 
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/winmm/wineopenal/audio.c	2006-11-21 11:21:33.000000000 -0800
@@ -0,0 +1,2639 @@
+/*
+ * Wine Driver for OpenAL
+ * http://www.openal.org
+ *
+ * Copyright 1994 Martin Ayotte
+ *           1999 Eric Pouech (async playing in waveOut/waveIn)
+ *           2000 Eric Pouech (loops in waveOut)
+ *           2004 Zhangrong Huang (EsounD version of this file)
+ *           2006 Nick Burns (OpenAL version of this file)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+/*
+ * NOTE: Any problems the ESD driver has this will most likely have.
+ */
+
+/*
+ * TODO: Use AL_EXT_STATIC_BUFFER to cut down on mem copies
+ */
+
+/*
+ * TODO: Query max number of buffers -- never exceed that max
+ * (PARTIAL FIX): OPENAL_MAX_BUFFERS should be set to the smallest max
+ */
+
+/*
+ * (FIXED): There is a problem here in sndrec32.exe
+ * If you play a file and totally keep seeking while its playing
+ * It will keep creating buffers as it thinks many are still in use
+ * However they should not be inuse...
+ *
+ * Note: The above issue is mostly fixed by removing the stop/pause parts from wodReset
+ * -- and limiting the max buffers to OPENAL_MAX_BUFFERS
+ */
+
+/*
+ * (FIXED): Determine why sometimes invalid enums and values spew from wodQueueBuffer
+ * This happened because we have run out of buffers .: OPENAL_MAX_BUFFERS
+ */
+
+/*
+ * (FIXED): Determine why sometimes invalid device spews from OpenAL_WaveClose
+ * This happened because we just closed the device we were checking errors on
+ */
+
+/*
+ * TODO: Test this driver with something other than Mac OSX
+ */
+
+/*
+ * TODO: Sleep for a more accurate amount of time if past OPENAL_MAX_BUFFERS
+ * Know which buffer is being processed and wait exactly for it to be finished
+ * Must be able to get current position and track buffers... (maybe timestamps)
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <math.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+#include <fcntl.h>
+#ifdef HAVE_POLL_H
+#include <poll.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 "mmreg.h"
+#include "dsound.h"
+#include "dsdriver.h"
+#include "ks.h"
+#include "ksguid.h"
+#include "ksmedia.h"
+#include "wine/debug.h"
+
+#if defined(HAVE_OPENAL_AL_H)
+    #include <OpenAL/al.h>
+    #include <OpenAL/alc.h>
+#elif defined(HAVE_AL_AL_H)
+    #include <AL/al.h>
+    #include <AL/alc.h>
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(wave);
+
+#if defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H)
+
+/* OpenAL 1.1 functions */
+/* defined as return_type, func_name, params */
+/* used to define func ptrs, get proc address, etc... */
+#define OPENAL_1_1_ALC_FUNCS(func)\
+    func(ALCdevice*, alcCaptureOpenDevice, (const ALCbyte *deviceName, ALCuint freq, ALCenum fmt, ALCsizei bufsize))\
+    func(ALCboolean, alcCaptureCloseDevice, (ALCdevice *device))\
+    func(ALCvoid, alcCaptureStart, (ALCdevice *device))\
+    func(ALCvoid, alcCaptureStop, (ALCdevice *device))\
+    func(ALCvoid, alcCaptureSamples, (ALCdevice *device, ALCvoid *buf, ALCsizei samps))
+
+#define OPENAL_1_1_AL_FUNCS(func)\
+    func(ALvoid, alListener3i, (ALenum param, ALint v1, ALint v2, ALint v3))\
+    func(ALvoid, alListeneriv, (ALenum param, ALint *values))\
+    func(ALvoid, alGetListener3i, (ALenum param, ALint *v1, ALint *v2, ALint *v3))\
+    func(ALvoid, alSource3i, (ALuint source, ALenum param, ALint v1, ALint v2, ALint v3))\
+    func(ALvoid, alSourceiv, (ALuint source, ALenum param, ALint *values))\
+    func(ALvoid, alGetSource3i, (ALuint source, ALenum param, ALint *v1, ALint *v2, ALint *v3))\
+    func(ALvoid, alBufferf, (ALuint buffer, ALenum param, ALfloat value))\
+    func(ALvoid, alBuffer3f, (ALuint buffer, ALenum param, ALfloat v1, ALfloat v2, ALfloat v3))\
+    func(ALvoid, alBufferfv, (ALuint buffer, ALenum param, ALfloat *values))\
+    func(ALvoid, alBufferi, (ALuint buffer, ALenum param, ALint value))\
+    func(ALvoid, alBuffer3i, (ALuint buffer, ALenum param, ALint v1, ALint v2, ALint v3))\
+    func(ALvoid, alBufferiv, (ALuint buffer, ALenum param, ALint *values))\
+    func(ALvoid, alGetBuffer3f, (ALuint buffer, ALenum pname, ALfloat *v1, ALfloat *v2, ALfloat *v3))\
+    func(ALvoid, alGetBuffer3i, (ALuint buffer, ALenum pname, ALint *v1, ALint *v2, ALint *v3))\
+    func(ALvoid, alSpeedOfSound, (ALfloat value))
+
+#define OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(func)\
+    func(ALvoid, alBufferDataStatic, (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq))
+
+#define MAKE_FUNC_PTR_TYPE(ret, name, params) typedef ret (*t##name) params;
+OPENAL_1_1_ALC_FUNCS(MAKE_FUNC_PTR_TYPE);
+OPENAL_1_1_AL_FUNCS(MAKE_FUNC_PTR_TYPE);
+OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(MAKE_FUNC_PTR_TYPE);
+#undef MAKE_FUNC_PTR_TYPE
+
+#define MAKE_FUNC_PTR(ret, name, params) static t##name p_##name = NULL;
+OPENAL_1_1_ALC_FUNCS(MAKE_FUNC_PTR);
+OPENAL_1_1_AL_FUNCS(MAKE_FUNC_PTR);
+OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(MAKE_FUNC_PTR);
+#undef MAKE_FUNC_PTR
+
+/* OpenAL 1.1 tokens */
+/* I dont know which way is better... */
+/* Since there is a alcGetEnumValue function it seems like thats the best solution */
+#if 0
+#ifndef ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER
+    #define ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER 0x311
+#endif
+#ifndef ALC_CAPTURE_DEVICE_SPECIFIER
+    #define ALC_CAPTURE_DEVICE_SPECIFIER 0x310
+#endif
+#ifndef ALC_CAPTURE_SAMPLES
+    #define ALC_CAPTURE_SAMPLES 0x312
+#endif
+#else
+#define OPENAL_1_1_ALC_TOKENS(func)\
+    func(ALC_CAPTURE_DEFAULT_DEVICE_SPECIFIER)\
+    func(ALC_CAPTURE_DEVICE_SPECIFIER)\
+    func(ALC_CAPTURE_SAMPLES)
+
+#define MAKE_TOKENS(name) static ALCenum t_##name = ALC_INVALID;
+OPENAL_1_1_ALC_TOKENS(MAKE_TOKENS);
+#undef MAKE_TOKENS
+#endif
+
+/* Can we cap audio?? */
+static int al_can_capture;
+
+/* Can use static buffers?? */
+static int al_static_buffer;
+
+/* Enable capture at your own peril -- it SHOULD work and does in certain cases (but I cannot force them) */
+#define OPENAL_DISABLE_IN      (1)
+#define OPENAL_LIMIT_OUT_44100 (0)
+#define OPENAL_LIMIT_IN_44100  (0)
+#define OPENAL_BUF_SIZE        (4 * 1024)
+/* fine tune this for more better goodness */
+/* too many buffers -- laggggggggggg */
+/* too few  buffers -- fun crackling */
+/* inf buffers -- not supported but cannot query limits */
+#define OPENAL_MAX_BUFFERS     (4)
+
+/* unless someone makes a wineserver kernel module, Unix pipes are faster than win32 events */
+#define USE_PIPE_SYNC
+
+#define BUFFER_REFILL_THRESHOLD 4
+
+#define MAX_WAVEOUTDRV (10)
+#define MAX_WAVEINDRV  (10)
+#define MAX_CHANNELS   2
+
+/* 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(mr) do { int x = 0; write((mr)->msg_pipe[1], &x, sizeof(x)); } while (0)
+#define CLEAR_OMR(mr)  do { int x = 0; read((mr)->msg_pipe[0], &x, sizeof(x)); } while (0)
+#define RESET_OMR(mr)  do { } while (0)
+#define WAIT_OMR(mr, sleep) \
+  do { struct pollfd pfd; pfd.fd = (mr)->msg_pipe[0]; \
+       pfd.events = POLLIN; poll(&pfd, 1, sleep); } while (0)
+#else
+#define SIGNAL_OMR(mr) do { SetEvent((mr)->msg_event); } while (0)
+#define CLEAR_OMR(mr)  do { } while (0)
+#define RESET_OMR(mr)  do { ResetEvent((mr)->msg_event); } while (0)
+#define WAIT_OMR(mr, sleep) \
+  do { WaitForSingleObject((mr)->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 */
+} RING_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 ESD_RING_BUFFER_INCREMENT      64
+typedef struct {
+    RING_MSG*           messages;
+    int                 ring_buffer_size;
+    int                 msg_tosave;
+    int                 msg_toget;
+#ifdef USE_PIPE_SYNC
+    int                 msg_pipe[2];
+#else
+    HANDLE              msg_event;
+#endif
+    CRITICAL_SECTION    msg_crst;
+} ESD_MSG_RING;
+
+typedef struct {
+    volatile int        state;                  /* one of the WINE_WS_ manifest constants */
+    WAVEOPENDESC        waveDesc;
+    WORD                wFlags;
+    WAVEFORMATPCMEX     waveFormat;
+    WAVEOUTCAPSW        caps;
+    char                interface_name[32];
+
+    DWORD               dwSleepTime;            /* Num of milliseconds to sleep between filling the dsp buffers */
+
+    /* OpenAL source */
+    int                 live;
+    ALuint              al_source;
+    int                 num_al_buffers;
+    ALuint*             al_buffers;
+    int                 num_free_al_buffers;
+    ALuint*             free_al_buffers;
+
+    int                 bytes_per_frame;
+    DWORD               dwBufferSize;           /* size of whole buffer in bytes */
+
+    DWORD               volume_left;            /* volume control information */
+    DWORD               volume_right;
+
+    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 the audio device since opening */
+
+    /* synchronization stuff */
+    HANDLE              hStartUpEvent;
+    HANDLE              hThread;
+    DWORD               dwThreadID;
+    ESD_MSG_RING        msgRing;
+} WINE_WAVEOUT;
+
+typedef struct {
+    volatile int        state;                  /* one of the WINE_WS_ manifest constants */
+    WAVEOPENDESC        waveDesc;
+    WORD                wFlags;
+    WAVEFORMATPCMEX     waveFormat;
+    WAVEINCAPSW         caps;
+    char                interface_name[32];
+
+    /* OpenAL capture device */
+    int                 live;
+    ALCdevice*          al_device;
+
+    int                 bytes_per_frame;
+
+    LPWAVEHDR           lpQueuePtr;
+    DWORD               dwRecordedTotal;
+
+    /* synchronization stuff */
+    HANDLE              hStartUpEvent;
+    HANDLE              hThread;
+    DWORD               dwThreadID;
+    ESD_MSG_RING        msgRing;
+} WINE_WAVEIN;
+
+/* OpenAL context for wave out */
+static ALCdevice*  al_device  = NULL;
+static ALCcontext* al_context = NULL;
+
+/* OpenAL error checking (should be able to turn this off for release builds) */
+#define CHECK_ALC_ERROR()\
+{\
+    ALenum error;\
+    if(al_device)\
+    {\
+        error = alcGetError(al_device);\
+        if(error)\
+        {\
+            ERR("ALC ERROR: %s at %s:%d\n", alcGetString(al_device, error), __FILE__, __LINE__);\
+        }\
+    }\
+    else\
+    {\
+        ERR("Invalid al device at %s:%d\n", __FILE__, __LINE__);\
+    }\
+}
+
+#define CHECK_AL_ERROR()\
+{\
+    ALenum error;\
+    error = alGetError();\
+    if(error)\
+    {\
+        ERR("AL ERROR: %s at %s:%d\n", alGetString(error), __FILE__, __LINE__);\
+    }\
+}
+
+static WINE_WAVEOUT WOutDev [MAX_WAVEOUTDRV];
+static WINE_WAVEIN  WInDev  [MAX_WAVEINDRV];
+
+static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv);
+static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc);
+
+static void wodDeleteBuffers(WINE_WAVEOUT* wwo);
+
+/* 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",
+};
+
+/*======================================================================*
+ *                  Low level WAVE implementation            *
+ *======================================================================*/
+
+ALenum al_format_for_wave_format(WAVEFORMATPCMEX* waveFormat)
+{
+    /* Start with an invalid enum... */
+    ALenum format = AL_INVALID;
+    switch(waveFormat->Format.wBitsPerSample)
+    {
+    case 8:
+        switch(waveFormat->Format.nChannels)
+        {
+        case 1:
+            format = AL_FORMAT_MONO8;
+            break;
+        case 2:
+            format = AL_FORMAT_STEREO8;
+            break;
+        default:
+            FIXME("unsupported waveFormat->Format.nChannels of %d\n", waveFormat->Format.nChannels);
+            break;
+        }
+        break;
+    case 16:
+        switch(waveFormat->Format.nChannels)
+        {
+        case 1:
+            format = AL_FORMAT_MONO16;
+            break;
+        case 2:
+            format = AL_FORMAT_STEREO16;
+            break;
+        default:
+            FIXME("unsupported waveFormat->Format.nChannels of %d\n", waveFormat->Format.nChannels);
+            break;
+        }
+        break;
+    default:
+        FIXME("unsupported waveFormat->Format.wBitsPerSample of %d\n", waveFormat->Format.wBitsPerSample);
+        break;
+    }
+    return format;
+}
+
+#if 0
+/* Volume functions derived from Alsaplayer source */
+/* length is the number of 16 bit samples */
+void volume_effect16(void *bufin, void* bufout, int length, int left,
+        int right, int nChannels)
+{
+    short *d_out = (short *)bufout;
+    short *d_in = (short *)bufin;
+    int i, v;
+
+/*
+  TRACE("length == %d, nChannels == %d\n", length, nChannels);
+*/
+
+    if (right == -1) right = left;
+    
+    for(i = 0; i < length; i+=(nChannels))
+    {
+        v = (int) ((*(d_in++) * left) / 100);
+        *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
+        if(nChannels == 2)
+        {
+            v = (int) ((*(d_in++) * right) / 100);
+            *(d_out++) = (v>32767) ? 32767 : ((v<-32768) ? -32768 : v);
+        }
+    }
+}
+
+/* length is the number of 8 bit samples */
+void volume_effect8(void *bufin, void* bufout, int length, int left,
+        int right, int     nChannels)
+{
+    BYTE *d_out = (BYTE *)bufout;
+    BYTE *d_in = (BYTE *)bufin;
+    int i, v;
+    
+/*
+  TRACE("length == %d, nChannels == %d\n", length, nChannels);
+*/
+    
+    if (right == -1) right = left;
+    
+    for(i = 0; i < length; i+=(nChannels))
+    {
+        v = (BYTE) ((*(d_in++) * left) / 100);
+        *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
+        if(nChannels == 2)
+        {
+            v = (BYTE) ((*(d_in++) * right) / 100);
+            *(d_out++) = (v>255) ? 255 : ((v<0) ? 0 : v);
+        }
+    }
+}
+#endif
+
+static DWORD bytes_to_mmtime(LPMMTIME lpTime, DWORD position,
+                             WAVEFORMATPCMEX* format)
+{
+    TRACE("wType=%04X wBitsPerSample=%u nSamplesPerSec=%lu nChannels=%u nAvgBytesPerSec=%lu\n",
+          lpTime->wType, format->Format.wBitsPerSample, format->Format.nSamplesPerSec,
+          format->Format.nChannels, format->Format.nAvgBytesPerSec);
+    TRACE("Position in bytes=%lu\n", position);
+
+    switch (lpTime->wType) {
+    case TIME_SAMPLES:
+        lpTime->u.sample = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
+        TRACE("TIME_SAMPLES=%lu\n", lpTime->u.sample);
+        break;
+    case TIME_MS:
+        lpTime->u.ms = 1000.0 * position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels * format->Format.nSamplesPerSec);
+        TRACE("TIME_MS=%lu\n", lpTime->u.ms);
+        break;
+    case TIME_SMPTE:
+        lpTime->u.smpte.fps = 30;
+        position = position / (format->Format.wBitsPerSample / 8 * format->Format.nChannels);
+        position += (format->Format.nSamplesPerSec / lpTime->u.smpte.fps) - 1; /* round up */
+        lpTime->u.smpte.sec = position / format->Format.nSamplesPerSec;
+        position -= lpTime->u.smpte.sec * format->Format.nSamplesPerSec;
+        lpTime->u.smpte.min = lpTime->u.smpte.sec / 60;
+        lpTime->u.smpte.sec -= 60 * lpTime->u.smpte.min;
+        lpTime->u.smpte.hour = lpTime->u.smpte.min / 60;
+        lpTime->u.smpte.min -= 60 * lpTime->u.smpte.hour;
+        lpTime->u.smpte.fps = 30;
+        lpTime->u.smpte.frame = position * lpTime->u.smpte.fps / format->Format.nSamplesPerSec;
+        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:
+        WARN("Format %d not supported, using TIME_BYTES !\n", lpTime->wType);
+        lpTime->wType = TIME_BYTES;
+        /* fall through */
+    case TIME_BYTES:
+        lpTime->u.cb = position;
+        TRACE("TIME_BYTES=%lu\n", lpTime->u.cb);
+        break;
+    }
+    return MMSYSERR_NOERROR;
+}
+
+static BOOL supportedFormat(LPWAVEFORMATEX wf, int input)
+{
+    TRACE("(%p)\n",wf);
+
+    /* Force OpenAL to 44100 */
+    if (((OPENAL_LIMIT_IN_44100 && input) || (OPENAL_LIMIT_OUT_44100 && !input)) && wf->nSamplesPerSec != 44100)
+        return FALSE;
+
+    if (wf->nSamplesPerSec<DSBFREQUENCY_MIN||wf->nSamplesPerSec>DSBFREQUENCY_MAX)
+        return FALSE;
+
+    if (wf->wFormatTag == WAVE_FORMAT_PCM) {
+        if (wf->nChannels >= 1 && wf->nChannels <= MAX_CHANNELS) {
+            if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
+                return TRUE;
+        }
+    } else if (wf->wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
+        WAVEFORMATEXTENSIBLE * wfex = (WAVEFORMATEXTENSIBLE *)wf;
+
+        if (wf->cbSize == 22 && IsEqualGUID(&wfex->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
+            if (wf->nChannels >=1 && wf->nChannels <= MAX_CHANNELS) {
+                if (wf->wBitsPerSample==wfex->Samples.wValidBitsPerSample) {
+                    if (wf->wBitsPerSample==8||wf->wBitsPerSample==16)
+                        return TRUE;
+                } else
+                    WARN("wBitsPerSample != wValidBitsPerSample not supported yet\n");
+            }
+        } else
+            WARN("only KSDATAFORMAT_SUBTYPE_PCM supported\n");
+    } else
+        WARN("only WAVE_FORMAT_PCM and WAVE_FORMAT_EXTENSIBLE supported\n");
+
+    return FALSE;
+}
+
+void copy_format(LPWAVEFORMATEX wf1, LPWAVEFORMATPCMEX wf2)
+{
+    ZeroMemory(wf2, sizeof(wf2));
+    if (wf1->wFormatTag == WAVE_FORMAT_PCM)
+        memcpy(wf2, wf1, sizeof(PCMWAVEFORMAT));
+    else if (wf1->wFormatTag == WAVE_FORMAT_EXTENSIBLE)
+        memcpy(wf2, wf1, sizeof(WAVEFORMATPCMEX));
+    else
+        memcpy(wf2, wf1, sizeof(WAVEFORMATEX) + wf1->cbSize);
+}
+
+/******************************************************************
+ *        OpenAL_CloseWaveOutDevice
+ *
+ */
+void OpenAL_CloseWaveOutDevice(WINE_WAVEOUT* wwo)
+{
+    TRACE("wwo=%p\n", wwo);
+
+    alDeleteSources(1, &wwo->al_source);
+    CHECK_AL_ERROR();
+    wodDeleteBuffers(wwo);
+    wwo->live = 0;
+}
+
+/******************************************************************
+ *        OpenAL_CloseWaveInDevice
+ *
+ */
+void OpenAL_CloseWaveInDevice(WINE_WAVEIN* wwi)
+{
+    ALCboolean success;
+    TRACE("wwi=%p\n", wwi);
+
+    /* recording not supported in OpenAL 1.0 */
+    success = p_alcCaptureCloseDevice(wwi->al_device);
+    if(!success)
+    {
+        ERR("ALC ERROR: Unable to close capture device at %s:%d\n", __FILE__, __LINE__);
+    }
+    wwi->live = 0;
+}
+
+/******************************************************************
+ *        OpenAL_WaveClose
+ */
+LONG OpenAL_WaveClose(void)
+{
+    /* Cannot check for device close failures on OpenAL 1.0... */
+    /* ALCboolean success; */
+    int iDevice;
+
+    TRACE("called\n");
+    /* close all open devices */
+    for(iDevice = 0; iDevice < MAX_WAVEOUTDRV; iDevice++)
+    {
+        if(WOutDev[iDevice].live)
+        {
+            OpenAL_CloseWaveOutDevice(&WOutDev[iDevice]);
+        }
+    }
+
+    for(iDevice = 0; iDevice < MAX_WAVEINDRV; iDevice++)
+    {
+        if(WInDev[iDevice].live)
+        {
+            OpenAL_CloseWaveInDevice(&WInDev[iDevice]);
+        }
+    }
+
+    alcDestroyContext(al_context);
+    CHECK_ALC_ERROR();
+
+    /* Cannot check for device close failures on OpenAL 1.0... */
+    alcCloseDevice(al_device);
+    /*success = alcCloseDevice(al_device);
+    if(!success)
+    {
+        ERR("ALC ERROR: Unable to close device at %s:%d\n", __FILE__, __LINE__);
+    }*/
+
+    return 1;
+}
+
+/******************************************************************
+ *        OpenAL_WaveInit
+ *
+ * Initialize internal structures for OpenAL
+ */
+LONG OpenAL_WaveInit(void)
+{
+    int i;
+
+    TRACE("called\n");
+
+    /* select the "preferred device" */
+    al_device = alcOpenDevice(NULL);
+    TRACE("al_device = %p\n", al_device);
+    CHECK_ALC_ERROR();
+    if(!al_device)
+    {
+        WARN("alcOpenDevice(%p) failed\n", NULL);
+        return -1;
+    }
+    /* Make the best default context */
+    /* TODO: should this use any init params? */
+    al_context = alcCreateContext(al_device, NULL);
+    TRACE("al_context = %p\n", al_context);
+    CHECK_ALC_ERROR();
+    if(!al_context)
+    {
+        return -1;
+    }
+    alcMakeContextCurrent(al_context);
+    CHECK_ALC_ERROR();
+
+    TRACE("AL_VERSION: %s\n", alGetString(AL_VERSION));
+    /* OpenAL version check */
+    {
+        ALCint major = -1;
+        ALCint minor = -1;
+        alcGetIntegerv(al_device, ALC_MAJOR_VERSION, 1, &major);
+        CHECK_ALC_ERROR();
+        alcGetIntegerv(al_device, ALC_MINOR_VERSION, 1, &minor);
+        CHECK_ALC_ERROR();
+        TRACE("ALC_VERSION: %d.%d\n", major, minor);
+    }
+
+/* alcGetProcAddress alGetProcAddress may give warnings about the signedness of the string... this is a bs warning */
+#define GET_ALC_FUNC_PTR(ret, name, params) p_##name = (t##name)alcGetProcAddress(al_device, #name);
+#define GET_AL_FUNC_PTR(ret, name, params) p_##name = (t##name)alGetProcAddress(#name);
+    OPENAL_1_1_ALC_FUNCS(GET_ALC_FUNC_PTR);
+    OPENAL_1_1_AL_FUNCS(GET_AL_FUNC_PTR);
+    OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(GET_AL_FUNC_PTR);
+#undef GET_ALC_FUNC_PTR
+#undef GET_AL_FUNC_PTR
+
+#define GET_TOKENS(name) t_##name = alcGetEnumValue(al_device, #name);
+    OPENAL_1_1_ALC_TOKENS(GET_TOKENS);
+#undef GET_TOKENS
+
+#define HAS_FUNC(ret, name, params) if(p_##name) TRACE("%s is supported\n", #name); else TRACE("%s is not supported\n", #name);
+    OPENAL_1_1_ALC_FUNCS(HAS_FUNC);
+    OPENAL_1_1_AL_FUNCS(HAS_FUNC);
+    OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(HAS_FUNC);
+#undef HAS_FUNC
+
+#define HAS_TOKENS(name) if(t_##name != AL_NONE) TRACE("%s is supported\n", #name); else TRACE("%s is not supported\n", #name);
+    OPENAL_1_1_ALC_TOKENS(HAS_TOKENS);
+#undef HAS_TOKENS
+
+    /* creepy yes -- but the 1.1 alc functions are the capture ones... */
+#define AND_FUNC_PTR(ret, name, params) p_##name &&
+    al_can_capture = OPENAL_1_1_ALC_FUNCS(AND_FUNC_PTR) /* && */ 1;
+    al_static_buffer = OPENAL_AL_EXT_STATIC_BUFFER_FUNCS(AND_FUNC_PTR) /* && */ 1;
+#undef AND_FUNC_PTR
+    /* still creepy yes -- but the 1.1 alc functions are the capture ones... */
+#define AND_TOKENS(name) (t_##name != AL_NONE) &&
+    al_can_capture = al_can_capture && OPENAL_1_1_ALC_TOKENS(AND_TOKENS) /* && */ 1;
+#undef AND_TOKENS
+
+    /* Why not test the extension as well?? */
+    /* You know its fun... */
+    al_can_capture = al_can_capture && alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE");
+
+    /* Why not test the extension as well?? */
+    /* We all love extensions... */
+    al_static_buffer = al_static_buffer && alcIsExtensionPresent(NULL, "AL_EXT_STATIC_BUFFER");
+
+    TRACE("al_can_capture == %d\n", al_can_capture);
+
+    /* initialize all device handles to -1 */
+    for (i = 0; i < MAX_WAVEOUTDRV; ++i)
+    {
+        static const WCHAR ini[] = {'O','p','e','n','A','L',' ','W','a','v','e','O','u','t','D','r','i','v','e','r',0};
+
+        WOutDev[i].live = 0;
+        memset(&WOutDev[i].caps, 0, sizeof(WOutDev[i].caps)); /* zero out caps values */
+        WOutDev[i].caps.wMid = 0x00FF;     /* Manufac ID */
+        WOutDev[i].caps.wPid = 0x0001;     /* Product ID */
+        lstrcpyW(WOutDev[i].caps.szPname, ini);
+        snprintf(WOutDev[i].interface_name, sizeof(WOutDev[i].interface_name), "wineopenal: %d", i);
+
+        WOutDev[i].caps.vDriverVersion = 0x0100;
+        WOutDev[i].caps.dwFormats = 0x00000000;
+        WOutDev[i].caps.dwSupport = WAVECAPS_VOLUME;
+
+        WOutDev[i].caps.wChannels = 2;
+        /* Gain is supported but how do we get independant l/r volume control? */
+        /* WOutDev[i].caps.dwSupport |= WAVECAPS_LRVOLUME; */
+
+#if !OPENAL_LIMIT_OUT_44100
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
+#endif
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
+#if !OPENAL_LIMIT_OUT_44100
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
+        WOutDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
+#endif
+    }
+
+    for (i = 0; i < MAX_WAVEINDRV; ++i)
+    {
+        static const WCHAR ini[] = {'O','p','e','n','A','L',' ','W','a','v','e','I','n','D','r','i','v','e','r',0};
+
+        WInDev[i].live = 0;
+        WInDev[i].al_device = NULL;
+        memset(&WInDev[i].caps, 0, sizeof(WInDev[i].caps)); /* zero out caps values */
+        WInDev[i].caps.wMid = 0x00FF;
+        WInDev[i].caps.wPid = 0x0001;
+        lstrcpyW(WInDev[i].caps.szPname, ini);
+        snprintf(WInDev[i].interface_name, sizeof(WInDev[i].interface_name), "wineopenal: %d", i);
+
+        WInDev[i].caps.vDriverVersion = 0x0100;
+        WInDev[i].caps.dwFormats = 0x00000000;
+
+        WInDev[i].caps.wChannels = 2;
+
+#if !OPENAL_LIMIT_IN_44100
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_96S16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_96M16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_48S16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_48M16;
+#endif
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_4S16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_4M16;
+#if !OPENAL_LIMIT_IN_44100
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_2M16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_2S16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S08;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_1M16;
+        WInDev[i].caps.dwFormats |= WAVE_FORMAT_1S16;
+#endif
+
+        WInDev[i].caps.wReserved1 = 0;
+    }
+    return 0;
+}
+
+/******************************************************************
+ *        OpenAL_InitRingMessage
+ *
+ * Initialize the ring of messages for passing between driver's caller and playback/record
+ * thread
+ */
+static int OpenAL_InitRingMessage(ESD_MSG_RING* mr)
+{
+    mr->msg_toget = 0;
+    mr->msg_tosave = 0;
+#ifdef USE_PIPE_SYNC
+    if (pipe(mr->msg_pipe) < 0) {
+        mr->msg_pipe[0] = -1;
+        mr->msg_pipe[1] = -1;
+        ERR("could not create pipe, error=%s\n", strerror(errno));
+    }
+#else
+    mr->msg_event = CreateEventW(NULL, FALSE, FALSE, NULL);
+#endif
+    mr->ring_buffer_size = ESD_RING_BUFFER_INCREMENT;
+    mr->messages = HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,mr->ring_buffer_size * sizeof(RING_MSG));
+    InitializeCriticalSection(&mr->msg_crst);
+    mr->msg_crst.DebugInfo->Spare[0] = (DWORD_PTR)"WINEOpenAL_msg_crst";
+    return 0;
+}
+
+/******************************************************************
+ *        OpenAL_DestroyRingMessage
+ *
+ */
+static int OpenAL_DestroyRingMessage(ESD_MSG_RING* mr)
+{
+    TRACE("mr=%p\n", mr);
+#ifdef USE_PIPE_SYNC
+    close(mr->msg_pipe[0]);
+    close(mr->msg_pipe[1]);
+#else
+    CloseHandle(mr->msg_event);
+#endif
+    HeapFree(GetProcessHeap(),0,mr->messages);
+    mr->messages=NULL;
+    DeleteCriticalSection(&mr->msg_crst);
+    return 0;
+}
+
+/******************************************************************
+ *        OpenAL_AddRingMessage
+ *
+ * Inserts a new message into the ring (should be called from DriverProc derivated routines)
+ */
+static int OpenAL_AddRingMessage(ESD_MSG_RING* mr, enum win_wm_message msg, DWORD param, BOOL wait)
+{
+    HANDLE hEvent = INVALID_HANDLE_VALUE;
+    TRACE("mr=%p, msg=%d, param=%08X, wait=%d\n", mr, msg, (unsigned int)param, wait);
+
+    EnterCriticalSection(&mr->msg_crst);
+    if ((mr->msg_toget == ((mr->msg_tosave + 1) % mr->ring_buffer_size)))
+    {
+        int old_ring_buffer_size = mr->ring_buffer_size;
+        mr->ring_buffer_size += ESD_RING_BUFFER_INCREMENT;
+        TRACE("mr->ring_buffer_size=%d\n",mr->ring_buffer_size);
+        mr->messages = HeapReAlloc(GetProcessHeap(),0,mr->messages, mr->ring_buffer_size * sizeof(RING_MSG));
+        /* Now we need to rearrange the ring buffer so that the new
+           buffers just allocated are in between mr->msg_tosave and
+           mr->msg_toget.
+        */
+        if (mr->msg_tosave < mr->msg_toget)
+        {
+            memmove(&(mr->messages[mr->msg_toget + ESD_RING_BUFFER_INCREMENT]),
+                &(mr->messages[mr->msg_toget]),
+                sizeof(RING_MSG)*(old_ring_buffer_size - mr->msg_toget)
+                );
+            mr->msg_toget += ESD_RING_BUFFER_INCREMENT;
+        }
+    }
+    if (wait)
+    {
+        hEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+        if (hEvent == INVALID_HANDLE_VALUE)
+        {
+            ERR("can't create event !?\n");
+            LeaveCriticalSection(&mr->msg_crst);
+            return 0;
+        }
+        if (mr->msg_toget != mr->msg_tosave && mr->messages[mr->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 */
+        mr->msg_toget = (mr->msg_toget + mr->ring_buffer_size - 1) % mr->ring_buffer_size;
+
+        mr->messages[mr->msg_toget].msg = msg;
+        mr->messages[mr->msg_toget].param = param;
+        mr->messages[mr->msg_toget].hEvent = hEvent;
+    }
+    else
+    {
+        mr->messages[mr->msg_tosave].msg = msg;
+        mr->messages[mr->msg_tosave].param = param;
+        mr->messages[mr->msg_tosave].hEvent = INVALID_HANDLE_VALUE;
+        mr->msg_tosave = (mr->msg_tosave + 1) % mr->ring_buffer_size;
+    }
+
+    LeaveCriticalSection(&mr->msg_crst);
+
+    /* signal a new message */
+    SIGNAL_OMR(mr);
+    if (wait)
+    {
+        /* wait for playback/record thread to have processed the message */
+        WaitForSingleObject(hEvent, INFINITE);
+        CloseHandle(hEvent);
+    }
+
+    return 1;
+}
+
+/******************************************************************
+ *        OpenAL_RetrieveRingMessage
+ *
+ * Get a message from the ring. Should be called by the playback/record thread.
+ */
+static int OpenAL_RetrieveRingMessage(ESD_MSG_RING* mr,
+                                   enum win_wm_message *msg, DWORD *param, HANDLE *hEvent)
+{
+    EnterCriticalSection(&mr->msg_crst);
+
+    if (mr->msg_toget == mr->msg_tosave) /* buffer empty ? */
+    {
+        LeaveCriticalSection(&mr->msg_crst);
+        return 0;
+    }
+
+    *msg = mr->messages[mr->msg_toget].msg;
+    mr->messages[mr->msg_toget].msg = 0;
+    *param = mr->messages[mr->msg_toget].param;
+    *hEvent = mr->messages[mr->msg_toget].hEvent;
+    mr->msg_toget = (mr->msg_toget + 1) % mr->ring_buffer_size;
+    CLEAR_OMR(mr);
+    LeaveCriticalSection(&mr->msg_crst);
+    return 1;
+}
+
+/*======================================================================*
+ *                  Low level WAVE OUT implementation            *
+ *======================================================================*/
+
+/**************************************************************************
+ *             wodPrintBuffers            [internal]
+ */
+static void wodPrintBuffers(WINE_WAVEOUT* wwo)
+{
+    int onBuffer;
+
+    TRACE("-----------\n");
+    TRACE("wwo->num_al_buffers=%d\n", wwo->num_al_buffers);
+    TRACE("wwo->num_free_al_buffers=%d\n", wwo->num_free_al_buffers);
+    for(onBuffer = 0; onBuffer < wwo->num_al_buffers; onBuffer++)
+    {
+        int onFreeBuffer;
+        int free = 0;
+    
+        for(onFreeBuffer = 0; onFreeBuffer < wwo->num_free_al_buffers; onFreeBuffer++)
+        {
+            free = free || (wwo->free_al_buffers[onFreeBuffer] == wwo->al_buffers[onBuffer]);
+        }
+        TRACE("buffer[%d] buf=%d free=%d\n", onBuffer, wwo->al_buffers[onBuffer], free);
+    }
+    TRACE("-----------\n");
+}
+
+/**************************************************************************
+ *             wodAddBuffer            [internal]
+ */
+static void wodAddBuffer(WINE_WAVEOUT* wwo)
+{
+    wwo->num_al_buffers++;
+    /* HeapReAlloc does not work like realloc... realloc on null is a malloc... */
+    /* This starts with 1 entry (an ignored one) */
+    wwo->al_buffers = (ALuint*)HeapReAlloc(GetProcessHeap(), 0, wwo->al_buffers, wwo->num_al_buffers * sizeof(ALuint));
+
+    alGenBuffers(1, &wwo->al_buffers[wwo->num_al_buffers - 1]);
+    CHECK_AL_ERROR();
+    TRACE("added buffer #%d == %d\n", wwo->num_al_buffers - 1, wwo->al_buffers[wwo->num_al_buffers - 1]);
+}
+
+/**************************************************************************
+ *             wodGetFreeBuffer            [internal]
+ */
+static ALuint wodGetFreeBuffer(WINE_WAVEOUT* wwo)
+{
+    ALint processed;
+    ALuint freeBuffer;
+
+    int found = 0;
+    while(!found)
+    {
+        if(wwo->num_free_al_buffers)
+        {
+            wwo->num_free_al_buffers--;
+            freeBuffer = wwo->free_al_buffers[wwo->num_free_al_buffers];
+    
+            TRACE("Found free buffer %d\n", freeBuffer);
+            found = 1;
+        }
+        else
+        {
+            alGetSourcei(wwo->al_source, AL_BUFFERS_PROCESSED, &processed);
+            CHECK_AL_ERROR();
+            TRACE("Found %d processed buffers\n", processed);
+    
+            if(processed)
+            {
+                wwo->num_free_al_buffers = processed;
+                /* HeapReAlloc does not work like realloc... realloc on null is a malloc... */
+                /* This starts with 1 entry (an ignored one) */
+                wwo->free_al_buffers = (ALuint*)HeapReAlloc(GetProcessHeap(), 0, wwo->free_al_buffers, wwo->num_free_al_buffers * sizeof(ALuint));
+    
+                alSourceUnqueueBuffers(wwo->al_source, processed, wwo->free_al_buffers);
+                CHECK_AL_ERROR();
+    
+                TRACE("Freed %d buffers\n", processed);
+            }
+            else
+            {
+                if(wwo->num_al_buffers < OPENAL_MAX_BUFFERS)
+                {
+                    /* no free buffer -- add one and return that */
+                    wodAddBuffer(wwo);
+                    freeBuffer = wwo->al_buffers[wwo->num_al_buffers - 1];
+                    TRACE("No free buffers\n");
+                    TRACE("Made new buffer %d\n", freeBuffer);
+                    found = 1;
+                }
+                else
+                {
+                    TRACE("No free buffers -- reached max -- waiting\n");
+                    /* Sleep for a more valid amount of time? */
+                    usleep(1000);
+                }
+            }
+        }
+    }
+    return freeBuffer;
+}
+
+/**************************************************************************
+ *             wodQueueBuffer            [internal]
+ */
+static void wodQueueBuffer(WINE_WAVEOUT* wwo, void* data, int toWrite)
+{
+    ALuint freeBuffer;
+
+    TRACE("Buffers before queue\n");
+    wodPrintBuffers(wwo);
+
+    freeBuffer = wodGetFreeBuffer(wwo);
+    TRACE("Found %d free buffer\n", freeBuffer);
+
+    /* send the audio data to OpenAL for playing */
+    {
+        ALenum format = al_format_for_wave_format(&wwo->waveFormat);
+        ALsizei freq = wwo->waveFormat.Format.nSamplesPerSec;
+
+        if(al_static_buffer)
+        {
+            p_alBufferDataStatic(freeBuffer, format, data, toWrite, freq);
+            TRACE("alBufferDataStatic(freeBuffer=%d, format=%08x, data=%p, toWrite=%d, freq=%d)\n", freeBuffer, format, data, toWrite, freq);
+        }
+        else
+        {
+            alBufferData(freeBuffer, format, data, toWrite, freq);
+            TRACE("alBufferData(freeBuffer=%d, format=%08x, data=%p, toWrite=%d, freq=%d)\n", freeBuffer, format, data, toWrite, freq);
+        }
+        CHECK_AL_ERROR();
+    }
+
+    /* Queue up the buffer */
+    alSourceQueueBuffers(wwo->al_source, 1, &freeBuffer);
+    CHECK_AL_ERROR();
+
+    TRACE("Buffers after queue\n");
+    wodPrintBuffers(wwo);
+}
+
+/**************************************************************************
+ *             wodDeleteBuffers            [internal]
+ */
+static void wodDeleteBuffers(WINE_WAVEOUT* wwo)
+{
+    alDeleteBuffers(wwo->num_al_buffers, wwo->al_buffers);
+    CHECK_AL_ERROR();
+
+    /* There is always at least 1 of each of these */
+    HeapFree(GetProcessHeap(), 0, wwo->al_buffers);
+    HeapFree(GetProcessHeap(), 0, wwo->free_al_buffers);
+}
+
+/**************************************************************************
+ *             wodNotifyClient            [internal]
+ */
+static DWORD wodNotifyClient(WINE_WAVEOUT* wwo, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, 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)
+{
+    /* total played is the bytes written less the bytes to write ;-) */
+    wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+
+    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);
+            TRACE("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_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->waveFormat.Format.nAvgBytesPerSec;
+        if(!dwMillis) dwMillis = 1;
+    }
+
+    TRACE("dwMillis = %ld\n", dwMillis);
+
+    return dwMillis;
+}
+
+/**************************************************************************
+ *                  wodPlayer_WriteMaxFrags            [internal]
+ * Writes the maximum number of bytes possible to the DSP and returns
+ * the number of bytes written.
+ */
+static int wodPlayer_WriteMaxFrags(WINE_WAVEOUT* wwo, DWORD* bytes)
+{
+    /* Only attempt to write to free bytes */
+    DWORD dwLength = wwo->lpPlayPtr->dwBufferLength - wwo->dwPartialOffset;
+    int toWrite = min(dwLength, *bytes);
+    int written;
+
+    TRACE("Writing wavehdr %p.%lu[%lu]\n",
+          wwo->lpPlayPtr, wwo->dwPartialOffset, wwo->lpPlayPtr->dwBufferLength);
+#if 0
+/* TODO: use the OpenAL buffer for this... */
+    /* see if our buffer isn't large enough for the data we are writing */
+    if(wwo->buffer_size < toWrite)
+    {
+        if(wwo->sound_buffer)
+        {
+            wwo->sound_buffer = HeapReAlloc(GetProcessHeap(), 0, wwo->sound_buffer, toWrite);
+            wwo->buffer_size = toWrite;
+        }
+    }
+
+/* TODO: use the OpenAL buffer for this... */
+    /* if we don't have a buffer then get one */
+    if(!wwo->sound_buffer)
+    {
+        /* allocate some memory for the buffer */
+        wwo->sound_buffer = HeapAlloc(GetProcessHeap(), 0, toWrite);
+        wwo->buffer_size = toWrite;
+    }
+
+    /* if we don't have a buffer then error out */
+    if(!wwo->sound_buffer)
+    {
+        ERR("error allocating sound_buffer memory\n");
+        return 0;
+    }
+
+    TRACE("toWrite == %d\n", toWrite);
+
+/* TODO: use OpenAL gain for this... */
+    /* apply volume to the bits */
+    /* for single channel audio streams we only use the LEFT volume */
+    if(wwo->waveFormat.Format.wBitsPerSample == 16)
+    {
+        /* apply volume to the buffer we are about to send */
+        /* divide toWrite(bytes) by 2 as volume processes by 16 bits */
+        volume_effect16(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
+                wwo->sound_buffer, toWrite>>1, wwo->volume_left,
+                wwo->volume_right, wwo->waveFormat.Format.nChannels);
+    } else if(wwo->waveFormat.Format.wBitsPerSample == 8)
+    {
+        /* apply volume to the buffer we are about to send */
+        volume_effect8(wwo->lpPlayPtr->lpData + wwo->dwPartialOffset,
+            wwo->sound_buffer, toWrite, wwo->volume_left,
+            wwo->volume_right, wwo->waveFormat.Format.nChannels);
+    } else
+    {
+        FIXME("unsupported wwo->format.wBitsPerSample of %d\n",
+            wwo->waveFormat.Format.wBitsPerSample);
+    }
+
+    TRACE("al_device=%p,al_context=%p,wwo->al_buffer=%d,wwo->al_source=%d\n", al_device, al_context, wwo->al_buffer, wwo->al_source);
+    /* send the audio data to OpenAL for playing */
+    ALenum format = al_format_for_wave_format(&wwo->waveFormat);
+    ALsizei freq = wwo->waveFormat.Format.nSamplesPerSec;
+
+    wodQueueBuffer(wwo, wwo->sound_buffer, toWrite);
+
+    /* Continue playing... */
+    {
+        ALint state;
+        alGetSourcei(wwo->al_source, AL_SOURCE_STATE, &state);
+        CHECK_AL_ERROR();
+        if(state != AL_PLAYING)
+        {
+            alSourcePlay(wwo->al_source);
+            CHECK_AL_ERROR();
+        }
+    }
+    written = toWrite;
+#else
+    /* Modify the gain... needs fixing (no right/left control) */
+    /* No pain... no gain? */
+    {
+        ALfloat gain = (ALfloat)max(wwo->volume_left, wwo->volume_right) / 100.0f;
+        /* TODO: control the left/right audio separately */
+        alSourcef(wwo->al_source, AL_GAIN, gain);
+        CHECK_AL_ERROR();
+    }
+    TRACE("al_device=%p,wwo->al_buffers=%p,wwo->al_source=%d\n", al_device, wwo->al_buffers, wwo->al_source);
+    /* send the audio data to OpenAL for playing */
+    wodQueueBuffer(wwo, wwo->lpPlayPtr->lpData + wwo->dwPartialOffset, toWrite);
+
+    /* Continue playing... */
+    {
+        ALint state;
+        alGetSourcei(wwo->al_source, AL_SOURCE_STATE, &state);
+        CHECK_AL_ERROR();
+        if(state != AL_PLAYING)
+        {
+            alSourcePlay(wwo->al_source);
+            CHECK_AL_ERROR();
+        }
+    }
+    written = toWrite;
+#endif
+    TRACE("written = %d\n", written);
+
+    if (written <= 0) 
+    {
+        *bytes = 0; /* REMOVE COMMENT: apparently esd is actually full */
+        return written; /* if we wrote nothing just return */
+    }
+
+    if (written >= dwLength)
+        wodPlayer_PlayPtrNext(wwo);   /* If we wrote all current wavehdr, skip to the next one */
+    else
+        wwo->dwPartialOffset += written;    /* Remove the amount written */
+
+    if (written < toWrite)
+        *bytes = 0;
+    else
+        *bytes -= written;
+
+    wwo->dwWrittenTotal += written; /* update stats on this wave device */
+
+    return written; /* return the number of bytes written */
+}
+
+/**************************************************************************
+ *                 wodPlayer_NotifyCompletions    [internal]
+ *
+ * Notifies and remove from queue all wavehdrs which have been played to
+ * the speaker (ie. they have cleared the audio device).  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;
+
+    if (wwo->lpQueuePtr) {
+    TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p), reserved=(%ld), dwWrittenTotal=(%ld), force=(%d)\n", 
+          wwo->lpQueuePtr,
+          wwo->lpPlayPtr,
+          wwo->lpLoopPtr,
+          wwo->lpQueuePtr->reserved,
+          wwo->dwWrittenTotal,
+          force);
+    } else {
+    TRACE("lpWaveHdr=(%p), lpPlayPtr=(%p), lpLoopPtr=(%p),  dwWrittenTotal=(%ld), force=(%d)\n", 
+          wwo->lpQueuePtr,
+          wwo->lpPlayPtr,
+          wwo->lpLoopPtr,
+          wwo->dwWrittenTotal,
+          force);
+    }
+
+    /* 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->dwWrittenTotal))) {
+
+        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);
+
+    wodPlayer_NotifyCompletions(wwo, FALSE); /* updates current notify list */
+
+    /* REMOVE COMMENT: we aren't able to flush any data that has already been written */
+    /* to esd, otherwise we would do the flushing here */
+
+    if (reset) {
+        enum win_wm_message     msg;
+        DWORD                   param;
+        HANDLE                  ev;
+
+        /* stop OpenAL on this wod */
+        /*TRACE("alSourceStop(%d)\n", wwo->al_source);
+        alSourceStop(wwo->al_source);
+        CHECK_AL_ERROR();*/
+
+        /* remove any buffer */
+        wodPlayer_NotifyCompletions(wwo, TRUE);
+
+        wwo->lpPlayPtr = wwo->lpQueuePtr = wwo->lpLoopPtr = NULL;
+        wwo->state = WINE_WS_STOPPED;
+        wwo->dwPlayedTotal = wwo->dwWrittenTotal = 0;
+
+        wwo->dwPartialOffset = 0;        /* Clear partial wavehdr */
+
+        /* remove any existing message in the ring */
+        EnterCriticalSection(&wwo->msgRing.msg_crst);
+
+        /* return all pending headers in queue */
+        while (OpenAL_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &ev))
+        {
+            TRACE("flushing msg\n");
+            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 {
+        /* Not sure about this ones interaction with OpenAL */
+        /* It seems like Pausing causes lots of ... problems */
+
+        /* pause OpenAL on this wod */
+        /*TRACE("alSourcePause(%d)\n", wwo->al_source);
+        alSourcePause(wwo->al_source);
+        CHECK_AL_ERROR();*/
+
+        /* stop OpenAL on this wod */
+        /*TRACE("alSourceStop(%d)\n", wwo->al_source);
+        alSourceStop(wwo->al_source);
+        CHECK_AL_ERROR();*/
+
+        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 {
+            /* the data already written is going to be played, so take */
+            /* this fact into account here */
+            wwo->dwPlayedTotal = wwo->dwWrittenTotal;
+        }
+        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 (OpenAL_RetrieveRingMessage(&wwo->msgRing, &msg, &param, &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:
+            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);
+            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);
+            TRACE("Killing Thread ... how?\n");
+            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)
+{
+    DWORD availInQ;
+
+    wodUpdatePlayedTotal(wwo);
+    /* better way to set availInQ? */
+    availInQ = OPENAL_BUF_SIZE;
+    TRACE("availInQ = %ld\n", availInQ);
+
+    /* input queue empty */
+    if (!wwo->lpPlayPtr) {
+        TRACE("Run out of wavehdr:s... flushing\n");
+        return INFINITE;
+    }
+
+#if 0
+    /* no more room... no need to try to feed */
+    if(!availInQ)
+    {
+        TRACE("no more room, no need to try to feed\n");
+        return wwo->dwSleepTime;
+    }
+#endif
+
+    /* Feed from partial wavehdr */
+    if (wwo->lpPlayPtr && wwo->dwPartialOffset != 0)
+    {
+        TRACE("feeding from partial wavehdr\n");
+        wodPlayer_WriteMaxFrags(wwo, &availInQ);
+    }
+
+    /* Feed wavehdrs until we run out of wavehdrs or DSP space */
+    if (!wwo->dwPartialOffset)
+    {
+        while(wwo->lpPlayPtr && availInQ)
+        {
+            TRACE("feeding waveheaders until we run out of space\n");
+            /* note the value that dwPlayedTotal will return when this wave finishes playing */
+            wwo->lpPlayPtr->reserved = wwo->dwWrittenTotal + wwo->lpPlayPtr->dwBufferLength;
+            TRACE("reserved=(%ld) dwWrittenTotal=(%ld) dwBufferLength=(%ld)\n",
+                wwo->lpPlayPtr->reserved,
+                wwo->dwWrittenTotal,
+                wwo->lpPlayPtr->dwBufferLength
+            );
+            wodPlayer_WriteMaxFrags(wwo, &availInQ);
+        }
+    }
+
+    if (!wwo->lpPlayPtr) {
+        TRACE("Ran out of wavehdrs\n");
+        return INFINITE;
+    }
+
+    return wwo->dwSleepTime;
+}
+
+/**************************************************************************
+ *                 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);
+        } else {
+            dwNextFeedTime = dwNextNotifyTime = INFINITE;
+        }
+    }
+}
+
+/**************************************************************************
+ *             wodGetDevCaps                [internal]
+ */
+static DWORD wodGetDevCaps(WORD wDevID, LPWAVEOUTCAPSW lpCaps, DWORD dwSize)
+{
+    TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
+
+    if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+    if (wDevID >= MAX_WAVEOUTDRV) {
+        TRACE("MAX_WAVOUTDRV reached !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    memcpy(lpCaps, &WOutDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodOpen                [internal]
+ */
+static DWORD wodOpen(WORD wDevID, LPWAVEOPENDESC lpDesc, DWORD dwFlags)
+{
+    WINE_WAVEOUT*    wwo;
+
+    TRACE("(%u, %p, %08lX);\n", wDevID, lpDesc, dwFlags);
+    if (lpDesc == NULL) {
+        WARN("Invalid Parameter !\n");
+        return MMSYSERR_INVALPARAM;
+    }
+    if (wDevID >= MAX_WAVEOUTDRV) {
+        TRACE("MAX_WAVOUTDRV reached !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    /* if this device is already open tell the app that it is allocated */
+    if(WOutDev[wDevID].live)
+    {
+        TRACE("device already allocated\n");
+        return MMSYSERR_ALLOCATED;
+    }
+
+    /* only PCM format is supported so far... */
+    if (!supportedFormat(lpDesc->lpFormat, 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];
+
+    /* direct sound not supported, ignore the flag */
+    dwFlags &= ~WAVE_DIRECTSOUND;
+
+    wwo->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+    memcpy(&wwo->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+    copy_format(lpDesc->lpFormat, &wwo->waveFormat);
+
+    if (wwo->waveFormat.Format.wBitsPerSample == 0) {
+        WARN("Resetting zeroed wBitsPerSample\n");
+        wwo->waveFormat.Format.wBitsPerSample = 8 *
+            (wwo->waveFormat.Format.nAvgBytesPerSec /
+             wwo->waveFormat.Format.nSamplesPerSec) /
+            wwo->waveFormat.Format.nChannels;
+    }
+
+    wwo->bytes_per_frame = (wwo->waveFormat.Format.wBitsPerSample * wwo->waveFormat.Format.nChannels) / 8;
+
+    /* output to OpenAL... */
+    wwo->live = 1;
+    alGenSources(1, &wwo->al_source);
+    CHECK_AL_ERROR();
+
+    wwo->num_al_buffers = 0;
+    wwo->al_buffers = (ALuint*)HeapAlloc(GetProcessHeap(), 0, sizeof(ALuint));
+    wwo->num_free_al_buffers = 0;
+    wwo->free_al_buffers = (ALuint*)HeapAlloc(GetProcessHeap(), 0, sizeof(ALuint));
+
+    if(!wwo->live) return MMSYSERR_ALLOCATED;
+
+    wwo->dwBufferSize = OPENAL_BUF_SIZE;
+    TRACE("Buffer size is now (%ld)\n", wwo->dwBufferSize);
+
+    wwo->dwPlayedTotal = 0;
+    wwo->dwWrittenTotal = 0;
+
+    wwo->dwSleepTime = (1024 * 1000 * BUFFER_REFILL_THRESHOLD) / wwo->waveFormat.Format.nAvgBytesPerSec;
+
+    /* Initialize volume to full level */
+    wwo->volume_left = 100;
+    wwo->volume_right = 100;
+
+    OpenAL_InitRingMessage(&wwo->msgRing);
+
+    /* create player thread */
+    if (!(dwFlags & WAVE_DIRECTSOUND)) {
+        wwo->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+        wwo->hThread = CreateThread(NULL, 0, wodPlayer, (LPVOID)(DWORD)wDevID, 0, &(wwo->dwThreadID));
+        WaitForSingleObject(wwo->hStartUpEvent, INFINITE);
+        CloseHandle(wwo->hStartUpEvent);
+    } else {
+        wwo->hThread = INVALID_HANDLE_VALUE;
+        wwo->dwThreadID = 0;
+    }
+    wwo->hStartUpEvent = INVALID_HANDLE_VALUE;
+
+    TRACE("live=0x%lx, dwBufferSize=%ld\n",
+        (long)wwo->live, wwo->dwBufferSize);
+
+    TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
+        wwo->waveFormat.Format.wBitsPerSample, wwo->waveFormat.Format.nAvgBytesPerSec,
+        wwo->waveFormat.Format.nSamplesPerSec, wwo->waveFormat.Format.nChannels,
+        wwo->waveFormat.Format.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 >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    wwo = &WOutDev[wDevID];
+    if (wwo->lpQueuePtr) {
+        WARN("buffers still playing !\n");
+        ret = WAVERR_STILLPLAYING;
+    } else {
+        TRACE("imhere[3-close]\n");
+        if (wwo->hThread != INVALID_HANDLE_VALUE) {
+            OpenAL_AddRingMessage(&wwo->msgRing, WINE_WM_CLOSING, 0, TRUE);
+        }
+
+        OpenAL_DestroyRingMessage(&wwo->msgRing);
+
+        OpenAL_CloseWaveOutDevice(wwo);    /* close the stream and clean things up */
+
+        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 >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad dev ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    if (lpWaveHdr->lpData == NULL || !(lpWaveHdr->dwFlags & WHDR_PREPARED))
+    {
+        TRACE("unprepared\n");
+        return WAVERR_UNPREPARED;
+    }
+
+    if (lpWaveHdr->dwFlags & WHDR_INQUEUE)
+    {
+        TRACE("still playing\n");
+        return WAVERR_STILLPLAYING;
+    }
+
+    lpWaveHdr->dwFlags &= ~WHDR_DONE;
+    lpWaveHdr->dwFlags |= WHDR_INQUEUE;
+    lpWaveHdr->lpNext = 0;
+
+    TRACE("adding ring message\n");
+    OpenAL_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *             wodPause                [internal]
+ */
+static DWORD wodPause(WORD wDevID)
+{
+    TRACE("(%u);!\n", wDevID);
+
+    if (wDevID >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    TRACE("imhere[3-PAUSING]\n");
+    OpenAL_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 >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    if (WOutDev[wDevID].state == WINE_WS_PAUSED) {
+        TRACE("imhere[3-RESTARTING]\n");
+        OpenAL_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 >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    TRACE("imhere[3-RESET]\n");
+    OpenAL_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodGetPosition            [internal]
+ */
+static DWORD wodGetPosition(WORD wDevID, LPMMTIME lpTime, DWORD uSize)
+{
+    WINE_WAVEOUT* wwo;
+
+    TRACE("(%u, %p, %lu);\n", wDevID, lpTime, uSize);
+
+    if (wDevID >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    if (lpTime == NULL)    {
+        WARN("invalid parameter: lpTime == NULL\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    wwo = &WOutDev[wDevID];
+    OpenAL_AddRingMessage(&wwo->msgRing, WINE_WM_UPDATE, 0, TRUE);
+
+    return bytes_to_mmtime(lpTime, wwo->dwPlayedTotal, &wwo->waveFormat);
+}
+
+/**************************************************************************
+ *                 wodBreakLoop            [internal]
+ */
+static DWORD wodBreakLoop(WORD wDevID)
+{
+    TRACE("(%u);\n", wDevID);
+
+    if (wDevID >= MAX_WAVEOUTDRV || !WOutDev[wDevID].live) {
+        WARN("bad device ID !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+    OpenAL_AddRingMessage(&WOutDev[wDevID].msgRing, WINE_WM_BREAKLOOP, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodGetVolume            [internal]
+ */
+static DWORD wodGetVolume(WORD wDevID, LPDWORD lpdwVol)
+{
+    DWORD left, right;
+
+    left = WOutDev[wDevID].volume_left;
+    right = WOutDev[wDevID].volume_right;
+
+    TRACE("(%u, %p);\n", wDevID, lpdwVol);
+
+    *lpdwVol = ((left * 0xFFFFl) / 100) + (((right * 0xFFFFl) / 100) <<
+        16);
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodSetVolume            [internal]
+ */
+static DWORD wodSetVolume(WORD wDevID, DWORD dwParam)
+{
+    DWORD left, right;
+
+    left  = (LOWORD(dwParam) * 100) / 0xFFFFl;
+    right = (HIWORD(dwParam) * 100) / 0xFFFFl;
+
+    TRACE("(%u, %08lX);\n", wDevID, dwParam);
+
+    WOutDev[wDevID].volume_left = left;
+    WOutDev[wDevID].volume_right = right;
+
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodGetNumDevs            [internal]
+ */
+static DWORD wodGetNumDevs(void)
+{
+    return MAX_WAVEOUTDRV;
+}
+
+/**************************************************************************
+ *                wodDevInterfaceSize             [internal]
+ */
+static DWORD wodDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
+{
+    TRACE("(%u, %p)\n", wDevID, dwParam1);
+
+    *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                                    NULL, 0 ) * sizeof(WCHAR);
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 wodDevInterface                 [internal]
+ */
+static DWORD wodDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
+{
+    if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                                        NULL, 0 ) * sizeof(WCHAR))
+    {
+        MultiByteToWideChar(CP_ACP, 0, WOutDev[wDevID].interface_name, -1,
+                            dwParam1, dwParam2 / sizeof(WCHAR));
+        return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/**************************************************************************
+ *                 wodMessage (WINEOPENAL.@)
+ */
+DWORD WINAPI OpenAL_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 MMSYSERR_NOTSUPPORTED;
+    case WODM_UNPREPARE:       return MMSYSERR_NOTSUPPORTED;
+    case WODM_GETDEVCAPS:      return wodGetDevCaps  (wDevID, (LPWAVEOUTCAPSW)dwParam1, dwParam2);
+    case WODM_GETNUMDEVS:      return wodGetNumDevs  ();
+    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 wodDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
+    case DRV_QUERYDEVICEINTERFACE:     return wodDevInterface     (wDevID, (PWCHAR)dwParam1, dwParam2);
+    case DRV_QUERYDSOUNDIFACE:         return wodDsCreate         (wDevID, (PIDSDRIVER*)dwParam1);
+    case DRV_QUERYDSOUNDDESC:          return wodDsDesc           (wDevID, (PDSDRIVERDESC)dwParam1);
+    default:
+        FIXME("unknown message %d!\n", wMsg);
+    }
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+/*======================================================================*
+ *                  Low level WAVE IN implementation            *
+ *======================================================================*/
+
+/**************************************************************************
+ *                 widGetNumDevs            [internal]
+ */
+static DWORD widGetNumDevs(void)
+{
+    return (OPENAL_DISABLE_IN || !al_can_capture) ? 0 : MAX_WAVEINDRV;
+}
+
+/**************************************************************************
+ *                              widDevInterfaceSize             [internal]
+ */
+static DWORD widDevInterfaceSize(UINT wDevID, LPDWORD dwParam1)
+{
+    TRACE("(%u, %p)\n", wDevID, dwParam1);
+
+    *dwParam1 = MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
+                                    NULL, 0 ) * sizeof(WCHAR);
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                              widDevInterface                 [internal]
+ */
+static DWORD widDevInterface(UINT wDevID, PWCHAR dwParam1, DWORD dwParam2)
+{
+    if (dwParam2 >= MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
+                                        NULL, 0 ) * sizeof(WCHAR))
+    {
+        MultiByteToWideChar(CP_ACP, 0, WInDev[wDevID].interface_name, -1,
+                            dwParam1, dwParam2 / sizeof(WCHAR));
+        return MMSYSERR_NOERROR;
+    }
+    return MMSYSERR_INVALPARAM;
+}
+
+/**************************************************************************
+ *             widNotifyClient            [internal]
+ */
+static DWORD widNotifyClient(WINE_WAVEIN* wwi, WORD wMsg, DWORD dwParam1, DWORD dwParam2)
+{
+    TRACE("wMsg = 0x%04x dwParm1 = %04lX dwParam2 = %04lX\n", wMsg, 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, LPWAVEINCAPSW lpCaps, DWORD dwSize)
+{
+    TRACE("(%u, %p, %lu);\n", wDevID, lpCaps, dwSize);
+
+    if (lpCaps == NULL) return MMSYSERR_NOTENABLED;
+
+    if (wDevID >= MAX_WAVEINDRV) {
+        TRACE("MAX_WAVINDRV reached !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    memcpy(lpCaps, &WInDev[wDevID].caps, min(dwSize, sizeof(*lpCaps)));
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 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;
+    enum win_wm_message msg;
+    DWORD               param;
+    HANDLE              ev;
+
+    SetEvent(wwi->hStartUpEvent);
+
+    /* make sleep time to be # of ms to record one packet */
+    dwSleepTime = (1024 * 1000) / wwi->waveFormat.Format.nAvgBytesPerSec;
+    TRACE("sleeptime=%ld ms\n", dwSleepTime);
+
+    for(;;) {
+        TRACE("wwi->lpQueuePtr=(%p), wwi->state=(%d)\n",wwi->lpQueuePtr,wwi->state);
+
+        /* read data from OpenAL */
+        if ((wwi->lpQueuePtr != NULL) && (wwi->state == WINE_WS_PLAYING))
+        {
+            lpWaveHdr = wwi->lpQueuePtr;
+
+            TRACE("read as much as we can\n");
+            while(wwi->lpQueuePtr)
+            {
+                ALCint samples = 0;
+
+                TRACE("attempt to read %ld bytes\n",lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+
+                /* Get samples from OpenAL on this wid */
+                alcGetIntegerv(wwi->al_device, t_ALC_CAPTURE_SAMPLES, 1, &samples);
+                CHECK_ALC_ERROR();
+                bytesRead = (wwi->waveFormat.Format.wBitsPerSample * samples) / 8;
+                TRACE("Got %d samples == %ld bytes\n", samples, bytesRead);
+                /* I have no idea if the below is needed... I dont want to just overrun buffers that are not big enough... */
+                /*if(bytesRead > (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded))
+                {
+                    FIXME("Buffer overrun detected... cutting back samples -- is this correct?");
+                    bytesRead = (lpWaveHdr->dwBufferLength - lpWaveHdr->dwBytesRecorded);
+                    samples = (bytesRead * 8) / wwi->waveFormat.Format.wBitsPerSample;
+                }*/
+                p_alcCaptureSamples(wwi->al_device, lpWaveHdr->lpData + lpWaveHdr->dwBytesRecorded, samples);
+                CHECK_ALC_ERROR();
+
+                TRACE("bytesRead=%ld\n",bytesRead);
+                if (bytesRead == -1 && errno == EAGAIN)
+                    bytesRead = 0;
+                if (bytesRead==0) break; /* So we can stop recording smoothly */
+                if (bytesRead < 0)
+                    bytesRead = 0;
+
+                lpWaveHdr->dwBytesRecorded += bytesRead;
+                wwi->dwRecordedTotal       += bytesRead;
+
+                /* buffer 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("waveHdr full.\n");
+
+                    lpWaveHdr->dwFlags &= ~WHDR_INQUEUE;
+                    lpWaveHdr->dwFlags |=  WHDR_DONE;
+
+                    widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+                    lpWaveHdr = wwi->lpQueuePtr = lpNext;
+                    TRACE("wwi->lpQueuePtr=%p\n", wwi->lpQueuePtr);
+                }
+            }
+        }
+
+        /* wait for dwSleepTime or an event in thread's queue */
+        WAIT_OMR(&wwi->msgRing, dwSleepTime);
+
+        while (OpenAL_RetrieveRingMessage(&wwi->msgRing, &msg, &param, &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;
+
+                /* stop OpenAL on this wid -- correct or no? */
+                p_alcCaptureStop(wwi->al_device);
+                CHECK_ALC_ERROR();
+
+                SetEvent(ev);
+                break;
+            case WINE_WM_STARTING:
+                wwi->state = WINE_WS_PLAYING;
+
+                /* start OpenAL on this wid */
+                p_alcCaptureStart(wwi->al_device);
+                CHECK_ALC_ERROR();
+
+                SetEvent(ev);
+                break;
+            case WINE_WM_HEADER:
+                lpWaveHdr = (LPWAVEHDR)param;
+                /* insert buffer at end of queue */
+                {
+                    LPWAVEHDR* wh;
+                    int num_headers = 0;
+                    for (wh = &(wwi->lpQueuePtr); *wh; wh = &((*wh)->lpNext))
+                    {
+                        num_headers++;
+                    }
+                    *wh=lpWaveHdr;
+                }
+                break;
+            case WINE_WM_STOPPING:
+                if (wwi->state != WINE_WS_STOPPED)
+                {
+                    /* stop OpenAL on this wid */
+                    p_alcCaptureStop(wwi->al_device);
+                    CHECK_ALC_ERROR();
+
+                    /* 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;
+                        widNotifyClient(wwi, WIM_DATA, (DWORD)lpWaveHdr, 0);
+                        wwi->lpQueuePtr = lpNext;
+                    }
+                }
+                wwi->state = WINE_WS_STOPPED;
+                SetEvent(ev);
+                break;
+            case WINE_WM_RESETTING:
+                wwi->state = WINE_WS_STOPPED;
+                wwi->dwRecordedTotal = 0;
+
+                /* stop OpenAL on this wid */
+                p_alcCaptureStop(wwi->al_device);
+                CHECK_ALC_ERROR();
+
+                /* 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;
+
+                    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);
+                TRACE("Killing Thread ... how?\n");
+                ExitThread(0);
+                /* shouldn't go here */
+            default:
+                FIXME("unknown message %d\n", msg);
+                break;
+            }
+        }
+    }
+    TRACE("Killing Thread ... how?\n");
+    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;
+
+    TRACE("(%u, %p %08lX);\n",wDevID, lpDesc, dwFlags);
+    if (lpDesc == NULL) {
+        WARN("Invalid Parametr (lpDesc == NULL)!\n");
+        return MMSYSERR_INVALPARAM;
+    }
+
+    if (wDevID >= MAX_WAVEINDRV) {
+        TRACE ("MAX_WAVEINDRV reached !\n");
+        return MMSYSERR_BADDEVICEID;
+    }
+
+    /* if this device is already open tell the app that it is allocated */
+    if(WInDev[wDevID].live)
+    {
+        TRACE("device already allocated\n");
+        return MMSYSERR_ALLOCATED;
+    }
+
+    /* only PCM format is support so far... */
+    if (!supportedFormat(lpDesc->lpFormat, 1)) {
+        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];
+
+    /* direct sound not supported, ignore the flag */
+    dwFlags &= ~WAVE_DIRECTSOUND;
+
+    wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+    memcpy(&wwi->waveDesc, lpDesc, sizeof(WAVEOPENDESC));
+    copy_format(lpDesc->lpFormat, &wwi->waveFormat);
+
+    if (wwi->waveFormat.Format.wBitsPerSample == 0) {
+        WARN("Resetting zerod wBitsPerSample\n");
+        wwi->waveFormat.Format.wBitsPerSample = 8 *
+            (wwi->waveFormat.Format.nAvgBytesPerSec /
+             wwi->waveFormat.Format.nSamplesPerSec) /
+            wwi->waveFormat.Format.nChannels;
+    }
+
+    /* input OpenAL... */
+    wwi->bytes_per_frame = (wwi->waveFormat.Format.wBitsPerSample * wwi->waveFormat.Format.nChannels) / 8;
+    {
+        ALenum format = al_format_for_wave_format(&wwi->waveFormat);
+        ALuint freq = wwi->waveFormat.Format.nSamplesPerSec;
+        /* Setup the OpenAL cap device */ /* Be able to hold 4 secs worth of data? */
+        wwi->al_device = p_alcCaptureOpenDevice(NULL, freq, format, 4 * wwi->waveFormat.Format.nSamplesPerSec * wwi->waveFormat.Format.wBitsPerSample / 8);
+/* woraround force freq == 44100 -- enable if desired */
+/*wwi->al_device = p_alcCaptureOpenDevice(NULL, 44100, format, 4 * wwi->waveFormat.Format.nSamplesPerSec * wwi->waveFormat.Format.wBitsPerSample / 8);*/
+        TRACE("wwi->al_device = %p\n", wwi->al_device);
+        CHECK_ALC_ERROR();
+        if(!wwi->al_device)
+        {
+            WARN("alcCaptureOpenDevice(%p, %d, %d, %ld) failed\n", NULL, freq, format, 4 * wwi->waveFormat.Format.nSamplesPerSec * wwi->waveFormat.Format.wBitsPerSample / 8);
+            return MMSYSERR_ALLOCATED;
+        }
+        wwi->live = 1;
+    }
+
+    if(!wwi->live) return MMSYSERR_ALLOCATED;
+
+    wwi->dwRecordedTotal = 0;
+    wwi->wFlags = HIWORD(dwFlags & CALLBACK_TYPEMASK);
+
+    OpenAL_InitRingMessage(&wwi->msgRing);
+
+    /* create recorder thread */
+    if (!(dwFlags & WAVE_DIRECTSOUND)) {
+        wwi->hStartUpEvent = CreateEventW(NULL, FALSE, FALSE, NULL);
+        wwi->hThread = CreateThread(NULL, 0, widRecorder, (LPVOID)(DWORD)wDevID, 0, &(wwi->dwThreadID));
+        WaitForSingleObject(wwi->hStartUpEvent, INFINITE);
+        CloseHandle(wwi->hStartUpEvent);
+    } else {
+        wwi->hThread = INVALID_HANDLE_VALUE;
+        wwi->dwThreadID = 0;
+    }
+    wwi->hStartUpEvent = INVALID_HANDLE_VALUE;
+
+    TRACE("wBitsPerSample=%u, nAvgBytesPerSec=%lu, nSamplesPerSec=%lu, nChannels=%u nBlockAlign=%u!\n",
+        wwi->waveFormat.Format.wBitsPerSample, wwi->waveFormat.Format.nAvgBytesPerSec,
+        wwi->waveFormat.Format.nSamplesPerSec, wwi->waveFormat.Format.nChannels,
+        wwi->waveFormat.Format.nBlockAlign);
+    return widNotifyClient(wwi, WIM_OPEN, 0L, 0L);
+}
+
+/**************************************************************************
+ *                 widClose            [internal]
+ */
+static DWORD widClose(WORD wDevID)
+{
+    WINE_WAVEIN*    wwi;
+
+    TRACE("(%u);\n", wDevID);
+    if (wDevID >= MAX_WAVEINDRV || 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;
+    }
+
+    OpenAL_AddRingMessage(&wwi->msgRing, WINE_WM_CLOSING, 0, TRUE);
+    OpenAL_CloseWaveInDevice(wwi);
+    wwi->state = WINE_WS_CLOSED;
+    OpenAL_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 >= MAX_WAVEINDRV || 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;
+
+    OpenAL_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_HEADER, (DWORD)lpWaveHdr, FALSE);
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *             widStart                [internal]
+ */
+static DWORD widStart(WORD wDevID)
+{
+    TRACE("(%u);\n", wDevID);
+    if (wDevID >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+        WARN("can't start recording !\n");
+        return MMSYSERR_INVALHANDLE;
+    }
+
+    OpenAL_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 >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+        WARN("can't stop !\n");
+        return MMSYSERR_INVALHANDLE;
+    }
+
+    OpenAL_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 >= MAX_WAVEINDRV || WInDev[wDevID].state == WINE_WS_CLOSED) {
+        WARN("can't reset !\n");
+        return MMSYSERR_INVALHANDLE;
+    }
+    OpenAL_AddRingMessage(&WInDev[wDevID].msgRing, WINE_WM_RESETTING, 0, TRUE);
+    return MMSYSERR_NOERROR;
+}
+
+/**************************************************************************
+ *                 widMessage (WINEOPENAL.6)
+ */
+DWORD WINAPI OpenAL_widMessage(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 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 MMSYSERR_NOTSUPPORTED;
+    case WIDM_UNPREPARE:    return MMSYSERR_NOTSUPPORTED;
+    case WIDM_GETDEVCAPS:   return widGetDevCaps (wDevID, (LPWAVEINCAPSW)dwParam1, dwParam2);
+    case WIDM_GETNUMDEVS:   return widGetNumDevs ();
+    case WIDM_RESET:        return widReset      (wDevID);
+    case WIDM_START:        return widStart      (wDevID);
+    case WIDM_STOP:         return widStop       (wDevID);
+    case DRV_QUERYDEVICEINTERFACESIZE: return widDevInterfaceSize (wDevID, (LPDWORD)dwParam1);
+    case DRV_QUERYDEVICEINTERFACE:     return widDevInterface     (wDevID, (PWCHAR)dwParam1, dwParam2);
+    default:
+        FIXME("unknown message %d!\n", wMsg);
+    }
+    return MMSYSERR_NOTSUPPORTED;
+}
+
+/*======================================================================*
+ *                  Low level DSOUND implementation            *
+ *======================================================================*/
+static DWORD wodDsCreate(UINT wDevID, PIDSDRIVER* drv)
+{
+    /* REMOVE COMMENT: we can't perform memory mapping as we don't have a file stream
+    interface with esd like we do with oss */
+    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;
+}
+
+static DWORD wodDsDesc(UINT wDevID, PDSDRIVERDESC desc)
+{
+    memset(desc, 0, sizeof(*desc));
+    strcpy(desc->szDesc, "Wine OpenAL DirectSound Driver");
+    strcpy(desc->szDrvname, "wineopenal.drv");
+    return MMSYSERR_NOERROR;
+}
+
+#else /* !defined(HAVE_OPENAL_AL_H) && !defined(HAVE_AL_AL_H) */
+
+/**************************************************************************
+ *                 wodMessage (WINEOPENAL.@)
+ */
+DWORD WINAPI OpenAL_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 (WINEOPENAL.6)
+ */
+DWORD WINAPI OpenAL_widMessage(UINT wDevID, UINT wMsg, DWORD dwUser,
+                DWORD dwParam1, DWORD dwParam2)
+{
+    FIXME("(%u, %04X, %08lX, %08lX, %08lX):stub\n", wDevID, wMsg, dwUser, dwParam1, dwParam2);
+    return MMSYSERR_NOTENABLED;
+}
+
+#endif /* defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H) */
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/winmm/wineopenal/Makefile.in	2006-06-01 23:55:52.000000000 -0700
@@ -0,0 +1,15 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = wineopenal.drv
+IMPORTS   = winmm user32 kernel32
+EXTRALIBS = $(LIBUUID) @OPENAL@
+
+C_SRCS = \
+	audio.c \
+	openal.c
+
+@MAKE_DLL_RULES@
+
+### Dependencies:
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/winmm/wineopenal/openal.c	2006-06-04 18:03:56.000000000 -0700
@@ -0,0 +1,87 @@
+/*
+ * Wine Driver for OpenAL
+ * http://www.openal.org
+ *
+ * Copyright 2004 Zhangrong Huang <hzhr@users.sourceforge.net>
+ *           2006 Nick Burns <adger44@hotmail.com> (OpenAL version)
+ *
+ * Code massively copied from Eric Pouech's OSS driver
+ * and Chris Morgan aRts driver
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "mmddk.h"
+#include "openal.h"
+#include "wine/library.h"
+#include "wine/debug.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(coreaudio);
+
+#if defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H)
+
+/**************************************************************************
+ *                 OpenAL_drvOpen            [internal]
+ */
+static LRESULT OpenAL_drvOpen(LPSTR str)
+{
+    return 1;
+}
+
+/**************************************************************************
+ *                 OpenAL_drvClose            [internal]
+ */
+static LRESULT OpenAL_drvClose(DWORD_PTR dwDevID)
+{
+    return 1;
+}
+#endif /* defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H) */
+
+
+/**************************************************************************
+ *                 DriverProc (WINEOPENAL.@)
+ */
+LRESULT CALLBACK OpenAL_DriverProc(DWORD_PTR dwDevID, HDRVR hDriv, UINT wMsg,
+                                LPARAM dwParam1, LPARAM dwParam2)
+{
+/* EPP     TRACE("(%08lX, %04X, %08lX, %08lX, %08lX)\n",  */
+/* EPP       dwDevID, hDriv, wMsg, dwParam1, dwParam2); */
+
+    switch(wMsg) {
+#if defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H)
+    case DRV_LOAD:           if (OpenAL_WaveInit()<0) return 0;
+                                 return 1;
+    case DRV_FREE:           return OpenAL_WaveClose();
+    case DRV_OPEN:           return OpenAL_drvOpen((LPSTR)dwParam1);
+    case DRV_CLOSE:          return OpenAL_drvClose(dwDevID);
+    case DRV_ENABLE:         return 1;
+    case DRV_DISABLE:        return 1;
+    case DRV_QUERYCONFIGURE: return 1;
+    case DRV_CONFIGURE:      MessageBoxA(0, "OpenAL MultiMedia Driver!", "OpenAL Driver", MB_OK);    return 1;
+    case DRV_INSTALL:        return DRVCNF_RESTART;
+    case DRV_REMOVE:         return DRVCNF_RESTART;
+#endif /* defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H) */
+    default:
+        return DefDriverProc(dwDevID, hDriv, wMsg, dwParam1, dwParam2);
+    }
+}
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/winmm/wineopenal/openal.h	2006-06-04 18:04:01.000000000 -0700
@@ -0,0 +1,35 @@
+/* Definition for OpenAL drivers : wine multimedia system
+ *
+ * Copyright 2004 Zhangrong Huang <hzhr@users.sourceforge.net>
+ *           2006 Nick Burns <adger44@hotmail.com> (OpenAL version)
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_OPENAL_H
+#define __WINE_OPENAL_H
+
+#ifndef __WINE_CONFIG_H
+# error You must include config.h to use this header
+#endif
+
+#ifdef HAVE_SYS_ERRNO_H
+#include <sys/errno.h>
+#endif
+
+extern LONG OpenAL_WaveInit(void);
+extern LONG OpenAL_WaveClose(void);
+
+#endif  /* __WINE_OPENAL_H */
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/winmm/wineopenal/wineopenal.drv.spec	2006-05-27 20:48:06.000000000 -0700
@@ -0,0 +1,3 @@
+@ stdcall -private DriverProc(long long long long long) OpenAL_DriverProc
+@ stdcall -private wodMessage(long long long long long) OpenAL_wodMessage
+@ stdcall -private widMessage(long long long long long) OpenAL_widMessage
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/openal32/openal.c	2006-11-24 00:06:28.000000000 -0800
@@ -0,0 +1,433 @@
+/* Written by Nick Burns (adger44@hotmail.com) */
+/* while sick */
+
+#include "config.h"
+
+#include <stdarg.h>
+#include "windef.h"
+#include "winbase.h"
+#include "wine/debug.h"
+
+/* TODO: add alut (from freealut) -- fill out stubs */
+/* TODO: add other openal extensions */
+/* TODO: better 1.1/ext handling -- do not getProcAddress each and every time */
+
+#if defined(HAVE_OPENAL_AL_H)
+    #include <OpenAL/al.h>
+    #include <OpenAL/alc.h>
+#elif defined(HAVE_AL_AL_H)
+    #include <AL/al.h>
+    #include <AL/alc.h>
+#endif
+
+WINE_DEFAULT_DEBUG_CHANNEL(openal);
+
+/* use builtin deprecated alut functions -- warning not always defined -- use freealut if you have build errors with this on */
+#define USE_BUILTIN_ALUT 0
+
+/* OpenAL exts */
+/* defined as (ext) */
+#define WINE_AL_EXTS(func)\
+    func(ALC_1_0)\
+    func(AL_1_0)\
+    func(ALC_1_1)\
+    func(AL_1_1)\
+    func(ALC_EXT_MAC_OSX)\
+    func(AL_EXT_STATIC_BUFFER)\
+    func(ALC_EXT_ASA)\
+    func(ALUT)
+
+/* OpenAL ext function groups */
+enum
+{
+#define MAKE_WINE_ENUMS(ext) WINE_##ext,
+    WINE_AL_EXTS(MAKE_WINE_ENUMS)
+#undef MAKE_WINE_ENUMS
+};
+
+static const char* wine_al_ext_to_string(const int ext)
+{
+    switch(ext)
+    {
+#define WINE_AL_EXT_TO_STRING_HELPER(ext) case WINE_##ext: return "WINE_" #ext;
+    WINE_AL_EXTS(WINE_AL_EXT_TO_STRING_HELPER)
+#undef WINE_AL_EXT_TO_STRING_HELPER
+    }
+    return "ERROR";
+}
+
+/* OpenAL 1.0 functions */
+/* defined as (ext, return_type, func_name, params, args) */
+/* used to define func ptrs, get proc address, etc... */
+#define WINE_ALC_1_0_FUNCS(func)\
+    func(WINE_ALC_1_0, ALCcontext *    , alcCreateContext, ( ALCdevice *device, const ALCint* attrlist ), ( device, attrlist ))\
+    func(WINE_ALC_1_0, ALCboolean      , alcMakeContextCurrent, ( ALCcontext *context ), ( context ))\
+    func(WINE_ALC_1_0, void            , alcProcessContext, ( ALCcontext *context ), ( context ))\
+    func(WINE_ALC_1_0, void            , alcSuspendContext, ( ALCcontext *context ), ( context ))\
+    func(WINE_ALC_1_0, void            , alcDestroyContext, ( ALCcontext *context ), ( context ))\
+    func(WINE_ALC_1_0, ALCcontext *    , alcGetCurrentContext, ( ALCvoid ), (  ))\
+    func(WINE_ALC_1_0, ALCdevice *     , alcGetContextsDevice, ( ALCcontext *context ), ( context ))\
+    func(WINE_ALC_1_0, ALCdevice *     , alcOpenDevice, ( const ALCchar *devicename ), ( devicename ))\
+    func(WINE_ALC_1_0, ALCboolean      , alcCloseDevice, ( ALCdevice *device ), ( device ))\
+    func(WINE_ALC_1_0, ALCenum         , alcGetError, ( ALCdevice *device ), ( device ))\
+    func(WINE_ALC_1_0, ALCboolean      , alcIsExtensionPresent, ( ALCdevice *device, const ALCchar *extname ), ( device, extname ))\
+/* must be handled differently */\
+    /*func(WINE_ALC_1_0, void *          , alcGetProcAddress, ( ALCdevice *device, const ALCchar *funcname ), ( device, funcname ))*/\
+    func(WINE_ALC_1_0, ALCenum         , alcGetEnumValue, ( ALCdevice *device, const ALCchar *enumname ), ( device, enumname ))\
+    func(WINE_ALC_1_0, const ALCchar*  , alcGetString, ( ALCdevice *device, ALCenum param ), ( device, param ))\
+    func(WINE_ALC_1_0, void            , alcGetIntegerv, ( ALCdevice *device, ALCenum param, ALCsizei size, ALCint *dest ), ( device, param, size, dest ))
+
+#define WINE_AL_1_0_FUNCS(func)\
+    func(WINE_AL_1_0, void          , alEnable, ( ALenum capability ), ( capability ))\
+    func(WINE_AL_1_0, void          , alDisable, ( ALenum capability ), ( capability ))\
+    func(WINE_AL_1_0, ALboolean     , alIsEnabled, ( ALenum capability ), ( capability ))\
+    func(WINE_AL_1_0, const ALchar* , alGetString, ( ALenum param ), ( param ))\
+    func(WINE_AL_1_0, void          , alGetBooleanv, ( ALenum param, ALboolean* data ), ( param, data ))\
+    func(WINE_AL_1_0, void          , alGetIntegerv, ( ALenum param, ALint* data ), ( param, data ))\
+    func(WINE_AL_1_0, void          , alGetFloatv, ( ALenum param, ALfloat* data ), ( param, data ))\
+    func(WINE_AL_1_0, void          , alGetDoublev, ( ALenum param, ALdouble* data ), ( param, data ))\
+    func(WINE_AL_1_0, ALboolean     , alGetBoolean, ( ALenum param ), ( param ))\
+    func(WINE_AL_1_0, ALint         , alGetInteger, ( ALenum param ), ( param ))\
+    func(WINE_AL_1_0, ALfloat       , alGetFloat, ( ALenum param ), ( param ))\
+    func(WINE_AL_1_0, ALdouble      , alGetDouble, ( ALenum param ), ( param ))\
+    func(WINE_AL_1_0, ALenum        , alGetError, ( void ), (  ))\
+    func(WINE_AL_1_0, ALboolean     , alIsExtensionPresent, (const ALchar* extname ), (extname ))\
+/* must be handled differently */\
+    /*func(WINE_AL_1_0, void*         , alGetProcAddress, ( const ALchar* fname ), ( fname ))*/\
+    func(WINE_AL_1_0, ALenum        , alGetEnumValue, ( const ALchar* ename ), ( ename ))\
+    func(WINE_AL_1_0, void          , alListenerf, ( ALenum param, ALfloat value ), ( param, value ))\
+    func(WINE_AL_1_0, void          , alListener3f, ( ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ), ( param, value1, value2, value3 ))\
+    func(WINE_AL_1_0, void          , alListenerfv, ( ALenum param, const ALfloat* values ), ( param, values ))\
+    func(WINE_AL_1_0, void          , alListeneri, ( ALenum param, ALint value ), ( param, value ))\
+    func(WINE_AL_1_0, void          , alGetListenerf, ( ALenum param, ALfloat* value ), ( param, value ))\
+    func(WINE_AL_1_0, void          , alGetListener3f, ( ALenum param, ALfloat *value1, ALfloat *value2, ALfloat *value3 ), ( param, value1, value2, value3 ))\
+    func(WINE_AL_1_0, void          , alGetListenerfv, ( ALenum param, ALfloat* values ), ( param, values ))\
+    func(WINE_AL_1_0, void          , alGetListeneri, ( ALenum param, ALint* value ), ( param, value ))\
+    func(WINE_AL_1_0, void          , alGetListeneriv, ( ALenum param, ALint* values ), ( param, values ))\
+    func(WINE_AL_1_0, void          , alGenSources, ( ALsizei n, ALuint* sources ), ( n, sources ))\
+    func(WINE_AL_1_0, void          , alDeleteSources, ( ALsizei n, const ALuint* sources ), ( n, sources ))\
+    func(WINE_AL_1_0, ALboolean     , alIsSource, ( ALuint sid ), ( sid ))\
+    func(WINE_AL_1_0, void          , alSourcef, ( ALuint sid, ALenum param, ALfloat value), ( sid, param, value))\
+    func(WINE_AL_1_0, void          , alSource3f, ( ALuint sid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ), ( sid, param, value1, value2, value3 ))\
+    func(WINE_AL_1_0, void          , alSourcefv, ( ALuint sid, ALenum param, const ALfloat* values ), ( sid, param, values ))\
+    func(WINE_AL_1_0, void          , alSourcei, ( ALuint sid, ALenum param, ALint value), ( sid, param, value))\
+    func(WINE_AL_1_0, void          , alGetSourcef, ( ALuint sid, ALenum param, ALfloat* value ), ( sid, param, value ))\
+    func(WINE_AL_1_0, void          , alGetSource3f, ( ALuint sid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3), ( sid, param, value1, value2, value3))\
+    func(WINE_AL_1_0, void          , alGetSourcefv, ( ALuint sid, ALenum param, ALfloat* values ), ( sid, param, values ))\
+    func(WINE_AL_1_0, void          , alGetSourcei, ( ALuint sid, ALenum param, ALint* value ), ( sid, param, value ))\
+    func(WINE_AL_1_0, void          , alGetSourceiv, ( ALuint sid, ALenum param, ALint* values ), ( sid, param, values ))\
+    func(WINE_AL_1_0, void          , alSourcePlayv, ( ALsizei ns, const ALuint *sids ), ( ns, sids ))\
+    func(WINE_AL_1_0, void          , alSourceStopv, ( ALsizei ns, const ALuint *sids ), ( ns, sids ))\
+    func(WINE_AL_1_0, void          , alSourceRewindv, ( ALsizei ns, const ALuint *sids ), ( ns, sids ))\
+    func(WINE_AL_1_0, void          , alSourcePausev, ( ALsizei ns, const ALuint *sids ), ( ns, sids ))\
+    func(WINE_AL_1_0, void          , alSourcePlay, ( ALuint sid ), ( sid ))\
+    func(WINE_AL_1_0, void          , alSourceStop, ( ALuint sid ), ( sid ))\
+    func(WINE_AL_1_0, void          , alSourceRewind, ( ALuint sid ), ( sid ))\
+    func(WINE_AL_1_0, void          , alSourcePause, ( ALuint sid ), ( sid ))\
+    func(WINE_AL_1_0, void          , alSourceQueueBuffers, ( ALuint sid, ALsizei numEntries, const ALuint *bids ), ( sid, numEntries, bids ))\
+    func(WINE_AL_1_0, void          , alSourceUnqueueBuffers, ( ALuint sid, ALsizei numEntries, ALuint *bids ), ( sid, numEntries, bids ))\
+    func(WINE_AL_1_0, void          , alGenBuffers, ( ALsizei n, ALuint* buffers ), ( n, buffers ))\
+    func(WINE_AL_1_0, void          , alDeleteBuffers, ( ALsizei n, const ALuint* buffers ), ( n, buffers ))\
+    func(WINE_AL_1_0, ALboolean     , alIsBuffer, ( ALuint bid ), ( bid ))\
+    func(WINE_AL_1_0, void          , alBufferData, ( ALuint bid, ALenum format, const ALvoid* data, ALsizei size, ALsizei freq ), ( bid, format, data, size, freq ))\
+    func(WINE_AL_1_0, void          , alGetBufferf, ( ALuint bid, ALenum param, ALfloat* value ), ( bid, param, value ))\
+    func(WINE_AL_1_0, void          , alGetBufferfv, ( ALuint bid, ALenum param, ALfloat* values ), ( bid, param, values ))\
+    func(WINE_AL_1_0, void          , alGetBufferi, ( ALuint bid, ALenum param, ALint* value ), ( bid, param, value ))\
+    func(WINE_AL_1_0, void          , alGetBufferiv, ( ALuint bid, ALenum param, ALint* values ), ( bid, param, values ))\
+    func(WINE_AL_1_0, void          , alDopplerFactor, ( ALfloat value ), ( value ))\
+    func(WINE_AL_1_0, void          , alDopplerVelocity, ( ALfloat value ), ( value ))\
+    func(WINE_AL_1_0, void          , alDistanceModel, ( ALenum distanceModel ), ( distanceModel ))
+
+/* OpenAL 1.1 functions */
+#define WINE_ALC_1_1_FUNCS(func)\
+    func(WINE_ALC_1_1, ALCdevice *     , alcCaptureOpenDevice, ( const ALCchar *devicename, ALCuint frequency, ALCenum format, ALCsizei buffersize ), ( devicename, frequency, format, buffersize ))\
+    func(WINE_ALC_1_1, ALCboolean      , alcCaptureCloseDevice, ( ALCdevice *device ), ( device ))\
+    func(WINE_ALC_1_1, void            , alcCaptureStart, ( ALCdevice *device ), ( device ))\
+    func(WINE_ALC_1_1, void            , alcCaptureStop, ( ALCdevice *device ), ( device ))\
+    func(WINE_ALC_1_1, void            , alcCaptureSamples, ( ALCdevice *device, ALCvoid *buffer, ALCsizei samples ), ( device, buffer, samples ))
+
+#define WINE_AL_1_1_FUNCS(func)\
+    func(WINE_AL_1_1, void          , alListener3i, ( ALenum param, ALint value1, ALint value2, ALint value3 ), ( param, value1, value2, value3 ))\
+    func(WINE_AL_1_1, void          , alListeneriv, ( ALenum param, const ALint* values ), ( param, values ))\
+    func(WINE_AL_1_1, void          , alGetListener3i, ( ALenum param, ALint *value1, ALint *value2, ALint *value3 ), ( param, value1, value2, value3 ))\
+    func(WINE_AL_1_1, void          , alSource3i, ( ALuint sid, ALenum param, ALint value1, ALint value2, ALint value3 ), ( sid, param, value1, value2, value3 ))\
+    func(WINE_AL_1_1, void          , alSourceiv, ( ALuint sid, ALenum param, const ALint* values ), ( sid, param, values ))\
+    func(WINE_AL_1_1, void          , alGetSource3i, ( ALuint sid, ALenum param, ALint* value1, ALint* value2, ALint* value3), ( sid, param, value1, value2, value3))\
+    func(WINE_AL_1_1, void          , alBufferf, ( ALuint bid, ALenum param, ALfloat value), ( bid, param, value))\
+    func(WINE_AL_1_1, void          , alBuffer3f, ( ALuint bid, ALenum param, ALfloat value1, ALfloat value2, ALfloat value3 ), ( bid, param, value1, value2, value3 ))\
+    func(WINE_AL_1_1, void          , alBufferfv, ( ALuint bid, ALenum param, const ALfloat* values ), ( bid, param, values ))\
+    func(WINE_AL_1_1, void          , alBufferi, ( ALuint bid, ALenum param, ALint value), ( bid, param, value))\
+    func(WINE_AL_1_1, void          , alBuffer3i, ( ALuint bid, ALenum param, ALint value1, ALint value2, ALint value3 ), ( bid, param, value1, value2, value3 ))\
+    func(WINE_AL_1_1, void          , alBufferiv, ( ALuint bid, ALenum param, const ALint* values ), ( bid, param, values ))\
+    func(WINE_AL_1_1, void          , alGetBuffer3f, ( ALuint bid, ALenum param, ALfloat* value1, ALfloat* value2, ALfloat* value3), ( bid, param, value1, value2, value3))\
+    func(WINE_AL_1_1, void          , alGetBuffer3i, ( ALuint bid, ALenum param, ALint* value1, ALint* value2, ALint* value3), ( bid, param, value1, value2, value3))\
+    func(WINE_AL_1_1, void          , alSpeedOfSound, ( ALfloat value ), ( value ))
+
+/* OpenAL extension functions */
+#define WINE_ALC_EXT_FUNCS(func)\
+    func(WINE_ALC_EXT_MAC_OSX, ALvoid, alcMacOSXRenderingQualityProcPtr, (const ALint value), (value))\
+    func(WINE_ALC_EXT_MAC_OSX, ALvoid, alMacOSXRenderChannelCountProcPtr, (const ALint value), (value))\
+    func(WINE_ALC_EXT_MAC_OSX, ALvoid, alcMacOSXMixerMaxiumumBussesProcPtr, (const ALint value), (value))\
+    func(WINE_ALC_EXT_MAC_OSX, ALvoid, alcMacOSXMixerOutputRateProcPtr, (const ALdouble value), (value))\
+    func(WINE_ALC_EXT_MAC_OSX, ALint, alcMacOSXGetRenderingQualityProcPtr, (), ())\
+    func(WINE_ALC_EXT_MAC_OSX, ALint, alMacOSXGetRenderChannelCountProcPtr, (), ())\
+    func(WINE_ALC_EXT_MAC_OSX, ALint, alcMacOSXGetMixerMaxiumumBussesProcPtr, (), ())\
+    func(WINE_ALC_EXT_MAC_OSX, ALdouble, alcMacOSXGetMixerOutputRateProcPtr, (), ())\
+    func(WINE_ALC_EXT_ASA, ALenum, alcASAGetSourceProcPtr, (const ALuint property, ALuint source, ALvoid *data, ALuint* dataSize), (property, source, data, dataSize))\
+    func(WINE_ALC_EXT_ASA, ALenum, alcASASetSourceProcPtr, (const ALuint property, ALuint source, ALvoid *data, ALuint dataSize), (property, source, data, dataSize))\
+    func(WINE_ALC_EXT_ASA, ALenum, alcASAGetListenerProcPtr, (const ALuint property, ALvoid *data, ALuint* dataSize), (property, data, dataSize))\
+    func(WINE_ALC_EXT_ASA, ALenum, alcASASetListenerProcPtr, (const ALuint property, ALvoid *data, ALuint dataSize), (property, data, dataSize))
+
+#define WINE_AL_EXT_FUNCS(func)\
+    func(WINE_AL_EXT_STATIC_BUFFER, ALvoid, alBufferDataStatic, (const ALint bid, ALenum format, ALvoid* data, ALsizei size, ALsizei freq), (bid, format, data, size, freq))
+
+#define WINE_ALUT_FUNCS(func)\
+    func(WINE_ALUT, ALvoid , alutInit , (ALint *argcp, ALbyte **argv), (argcp, argv))\
+    func(WINE_ALUT, ALvoid , alutExit , (ALvoid), ())\
+/* Nasty Compatibility stuff, WARNING: THESE FUNCTIONS ARE STRONGLY DEPRECATED */\
+/* handled differently */\
+/*    func(WINE_ALUT, ALvoid , alutLoadWAVFile , (ALbyte *fileName, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency), (fileName, format, data, size, frequency))*/\
+/*    func(WINE_ALUT, ALvoid , alutLoadWAVMemory , (ALbyte *buffer, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency), (buffer, format, data, size, frequency))*/\
+    func(WINE_ALUT, ALvoid , alutUnloadWAV , (ALenum format, ALvoid *data, ALsizei size, ALsizei frequency), (format, data, size, frequency))
+
+#if USE_BUILTIN_ALUT && defined(__APPLE__)
+    /* Incorrectly defined in Mac OSX */
+    #define WINE_ALUT_BAD_FUNCS(func)
+#else
+    #define WINE_ALUT_BAD_FUNCS(func)\
+        func(WINE_ALUT, ALvoid , alutLoadWAVFile , (ALbyte *fileName, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency, ALboolean *loop), (fileName, format, data, size, frequency, loop))\
+        func(WINE_ALUT, ALvoid , alutLoadWAVMemory , (ALbyte *buffer, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency, ALboolean *loop), (buffer, format, data, size, frequency, loop))
+#endif
+
+#if defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H)
+
+/* OpenAL error checking (should be able to turn this off for release builds) */
+/* only useful for debugging -- dont steal errs from OpenAL */
+#define CHECK_ALC_ERROR(al_device)\
+{\
+    ALenum error;\
+    if (al_device)\
+    {\
+        error = alcGetError(al_device);\
+        if (error)\
+        {\
+            ERR("ALC ERROR: %s at %s:%d\n", alcGetString(al_device, error), __FILE__, __LINE__);\
+        }\
+    }\
+    else\
+    {\
+        ERR("Invalid al device at %s:%d\n", __FILE__, __LINE__);\
+    }\
+}
+
+#define CHECK_AL_ERROR()\
+{\
+    ALenum error;\
+    error = alGetError();\
+    if (error)\
+    {\
+        ERR("AL ERROR: %s at %s:%d\n", alGetString(error), __FILE__, __LINE__);\
+    }\
+}
+
+static HMODULE openal32_handle;
+
+/* Make the thunking functions */
+
+#define MAKE_WINE_ALC_FUNCS(ext, ret, name, params, args)\
+    ret CDECL wine_##name params\
+    {\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        return name args;\
+    }
+
+#define MAKE_WINE_AL_FUNCS(ext, ret, name, params, args)\
+    ret CDECL wine_##name params\
+    {\
+        /*CHECK_AL_ERROR();*/\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        return name args;\
+    }
+
+#define MAKE_WINE_ALC_EXT_FUNCS(ext, ret, name, params, args)\
+    typedef ret (*t##name) params;\
+    static t##name p_##name = NULL;\
+    ret CDECL wine_##name params\
+    {\
+        /* should these functions be cached? */\
+        p_##name = (t##name)alcGetProcAddress(NULL, #name);\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        return p_##name args;\
+    }
+
+#define MAKE_WINE_AL_EXT_FUNCS(ext, ret, name, params, args)\
+    typedef ret (*t##name) params;\
+    static t##name p_##name = NULL;\
+    ret CDECL wine_##name params\
+    {\
+        /*CHECK_AL_ERROR();*/\
+        /* should these functions be cached? */\
+        p_##name = (t##name)alGetProcAddress(#name);\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        return p_##name args;\
+    }
+
+#if USE_BUILTIN_ALUT
+#define MAKE_WINE_ALUT_FUNCS(ext, ret, name, params, args)\
+    extern ret name params;\
+    ret CDECL wine_##name params\
+    {\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        return name args;\
+    }
+#else
+#define MAKE_WINE_ALUT_FUNCS(ext, ret, name, params, args)\
+    void CDECL wine_##name params\
+    {\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        FIXME("stub for unimplemented deprecated alut function %s\n", #name);\
+        return;\
+    }
+#endif
+    WINE_ALC_1_0_FUNCS(MAKE_WINE_ALC_FUNCS)
+    WINE_AL_1_0_FUNCS(MAKE_WINE_AL_FUNCS)
+    WINE_ALC_1_1_FUNCS(MAKE_WINE_ALC_EXT_FUNCS)
+    WINE_AL_1_1_FUNCS(MAKE_WINE_AL_EXT_FUNCS)
+    WINE_ALC_EXT_FUNCS(MAKE_WINE_ALC_EXT_FUNCS)
+    WINE_AL_EXT_FUNCS(MAKE_WINE_AL_EXT_FUNCS)
+    WINE_ALUT_FUNCS(MAKE_WINE_ALUT_FUNCS)
+    WINE_ALUT_BAD_FUNCS(MAKE_WINE_ALUT_FUNCS)
+
+#undef MAKE_WINE_ALC_FUNCS
+#undef MAKE_WINE_AL_FUNCS
+#undef MAKE_WINE_ALC_EXT_FUNCS
+#undef MAKE_WINE_AL_EXT_FUNCS
+#undef MAKE_WINE_ALUT_FUNCS
+
+
+void* CDECL wine_alcGetProcAddress(ALCdevice *device, const ALCchar *funcname)
+{
+    void* ret = NULL;
+    TRACE("funcname == %s, ext == %s\n", funcname, wine_al_ext_to_string(WINE_ALC_1_0));
+    /* TODO: maybe check an ext registry/versions? */
+    /*
+        Checks similar to the following could be done...
+        alcIsExtensionPresent(NULL, "ALC_EXT_CAPTURE");
+        alcIsExtensionPresent(NULL, "AL_EXT_STATIC_BUFFER");
+    */
+    /* be consistent with the underlying impl */
+    if (alcGetProcAddress(device, funcname))
+    {
+        ret = GetProcAddress(openal32_handle, funcname);
+    }
+    TRACE("funcname == %s, ret == %p\n", funcname, ret);
+    return ret;
+}
+
+void* CDECL wine_alGetProcAddress(const ALchar* fname)
+{
+    void* ret = NULL;
+    TRACE("fname == %s, ext == %s\n", fname, wine_al_ext_to_string(WINE_AL_1_0));
+    /* TODO: maybe check an ext registry/versions? */
+    /*
+        Checks similar to the following could be done...
+        alIsExtensionPresent("ALC_EXT_CAPTURE");
+        alIsExtensionPresent("AL_EXT_STATIC_BUFFER");
+    */
+    /* be consistent with the underlying impl */
+    if (alGetProcAddress(fname))
+    {
+        ret = GetProcAddress(openal32_handle, fname);
+    }
+    TRACE("fname == %s, ret == %p\n", fname, ret);
+    return ret;
+}
+
+#if USE_BUILTIN_ALUT && defined(__APPLE__)
+    /* Incorrectly defined in Mac OSX */
+    extern ALvoid alutLoadWAVFile (ALbyte *fileName, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency);
+    ALvoid CDECL wine_alutLoadWAVFile (ALbyte *fileName, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency, ALboolean *loop)
+    {
+        TRACE("ext == %s\n", wine_al_ext_to_string(WINE_ALUT));
+        if (loop) *loop = AL_FALSE;
+        return alutLoadWAVFile (fileName, format, data, size, frequency);
+    }
+
+    extern ALvoid alutLoadWAVMemory (ALbyte *buffer, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency);
+    ALvoid CDECL wine_alutLoadWAVMemory (ALbyte *buffer, ALenum *format, ALvoid **data, ALsizei *size, ALsizei *frequency, ALboolean *loop)
+    {
+        TRACE("ext == %s\n", wine_al_ext_to_string(WINE_ALUT));
+        if (loop) *loop = AL_FALSE;
+        return alutLoadWAVMemory (buffer, format, data, size, frequency);
+    }
+#endif
+
+
+/***********************************************************************
+ *           OpenAL initialisation routine
+ */
+BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
+{
+    switch(reason)
+    {
+    case DLL_PROCESS_ATTACH:
+        openal32_handle = hinst;
+        /*DisableThreadLibraryCalls(hinst);*/
+        break;
+    case DLL_PROCESS_DETACH:
+        /*process_detach();*/
+        break;
+    }
+    return TRUE;
+}
+
+
+#else /* !defined(HAVE_OPENAL_AL_H) && !defined(HAVE_AL_AL_H) */
+
+#define MAKE_FAKE_WINE_FUNCS(ext, ret, name, params, args)\
+    void CDECL wine_##name ()\
+    {\
+        TRACE("ext == %s\n", wine_al_ext_to_string(ext));\
+        FIXME("OpenAL not supported on this system\n");\
+    }
+    WINE_ALC_1_0_FUNCS(MAKE_FAKE_WINE_FUNCS)
+    WINE_AL_1_0_FUNCS(MAKE_FAKE_WINE_FUNCS)
+    WINE_ALUT_FUNCS(MAKE_FAKE_WINE_FUNCS)
+    WINE_ALUT_BAD_FUNCS(MAKE_FAKE_WINE_FUNCS)
+#undef MAKE_WINE_FUNCS
+
+
+void CDECL wine_alcGetProcAddress()
+{
+    FIXME("OpenAL not supported on this system\n");
+}
+
+void CDECL wine_alGetProcAddress()
+{
+    FIXME("OpenAL not supported on this system\n");
+}
+
+#if USE_BUILTIN_ALUT && defined(__APPLE__)
+    /* Incorrectly defined in Mac OSX */
+    void CDECL wine_alutLoadWAVFile ()
+    {
+        FIXME("OpenAL not supported on this system\n");
+    }
+
+    void CDECL wine_alutLoadWAVMemory ()
+    {
+        FIXME("OpenAL not supported on this system\n");
+    }
+#endif
+
+
+/***********************************************************************
+ *           OpenAL initialisation routine
+ */
+BOOL WINAPI DllMain( HINSTANCE hinst, DWORD reason, LPVOID reserved )
+{
+    FIXME("OpenAL not supported on this system\n");
+    return TRUE;
+}
+
+#endif /* defined(HAVE_OPENAL_AL_H) || defined(HAVE_AL_AL_H) */
\ No newline at end of file
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/openal32/Makefile.in	2006-11-20 07:16:28.000000000 -0800
@@ -0,0 +1,14 @@
+TOPSRCDIR = @top_srcdir@
+TOPOBJDIR = ../..
+SRCDIR    = @srcdir@
+VPATH     = @srcdir@
+MODULE    = openal32.dll
+IMPORTS   = kernel32 ntdll
+EXTRALIBS = @OPENAL@
+
+C_SRCS = \
+	openal.c
+
+@MAKE_DLL_RULES@
+
+@DEPENDENCIES@  # everything below this line is overwritten by make depend
--- /dev/null	2006-11-24 00:08:17.000000000 -0800
+++ dlls/openal32/openal32.spec	2006-11-20 20:15:36.000000000 -0800
@@ -0,0 +1,133 @@
+#OpenAL ALC_1_0
+@ cdecl alcCreateContext(ptr ptr ) wine_alcCreateContext
+@ cdecl alcMakeContextCurrent(ptr) wine_alcMakeContextCurrent
+@ cdecl alcProcessContext(ptr) wine_alcProcessContext
+@ cdecl alcSuspendContext(ptr) wine_alcSuspendContext
+@ cdecl alcDestroyContext(ptr) wine_alcDestroyContext
+@ cdecl alcGetCurrentContext( ) wine_alcGetCurrentContext
+@ cdecl alcGetContextsDevice(ptr) wine_alcGetContextsDevice
+@ cdecl alcOpenDevice(str) wine_alcOpenDevice
+@ cdecl alcCloseDevice(ptr) wine_alcCloseDevice
+@ cdecl alcGetError(ptr) wine_alcGetError
+@ cdecl alcIsExtensionPresent(ptr str) wine_alcIsExtensionPresent
+@ cdecl alcGetProcAddress(ptr str) wine_alcGetProcAddress
+@ cdecl alcGetEnumValue(ptr str) wine_alcGetEnumValue
+@ cdecl alcGetString(ptr long ) wine_alcGetString
+@ cdecl alcGetIntegerv(ptr long  long  ptr) wine_alcGetIntegerv
+#OpenAL AL_1_0
+@ cdecl alEnable(long ) wine_alEnable
+@ cdecl alDisable(long ) wine_alDisable
+@ cdecl alIsEnabled(long ) wine_alIsEnabled
+@ cdecl alGetString(long ) wine_alGetString
+@ cdecl alGetBooleanv(long  ptr ) wine_alGetBooleanv
+@ cdecl alGetIntegerv(long  ptr ) wine_alGetIntegerv
+@ cdecl alGetFloatv(long  ptr ) wine_alGetFloatv
+@ cdecl alGetDoublev(long  ptr ) wine_alGetDoublev
+@ cdecl alGetBoolean(long ) wine_alGetBoolean
+@ cdecl alGetInteger(long ) wine_alGetInteger
+@ cdecl alGetFloat(long ) wine_alGetFloat
+@ cdecl alGetDouble(long ) wine_alGetDouble
+@ cdecl alGetError( ) wine_alGetError
+@ cdecl alIsExtensionPresent(str ) wine_alIsExtensionPresent
+@ cdecl alGetProcAddress(str ) wine_alGetProcAddress
+@ cdecl alGetEnumValue(str ) wine_alGetEnumValue
+@ cdecl alListenerf(long  long ) wine_alListenerf
+@ cdecl alListener3f(long  long  long  long ) wine_alListener3f
+@ cdecl alListenerfv(long  ptr ) wine_alListenerfv
+@ cdecl alListeneri(long  long ) wine_alListeneri
+@ cdecl alGetListenerf(long  ptr ) wine_alGetListenerf
+@ cdecl alGetListener3f(long  ptr ptr ptr) wine_alGetListener3f
+@ cdecl alGetListenerfv(long  ptr ) wine_alGetListenerfv
+@ cdecl alGetListeneri(long  ptr ) wine_alGetListeneri
+@ cdecl alGetListeneriv(long  ptr ) wine_alGetListeneriv
+@ cdecl alGenSources(long  ptr ) wine_alGenSources
+@ cdecl alDeleteSources(long  ptr ) wine_alDeleteSources
+@ cdecl alIsSource(long ) wine_alIsSource
+@ cdecl alSourcef(long  long  long ) wine_alSourcef
+@ cdecl alSource3f(long  long  long  long  long ) wine_alSource3f
+@ cdecl alSourcefv(long  long  ptr ) wine_alSourcefv
+@ cdecl alSourcei(long  long  long ) wine_alSourcei
+@ cdecl alGetSourcef(long  long  ptr ) wine_alGetSourcef
+@ cdecl alGetSource3f(long  long  ptr  ptr  ptr ) wine_alGetSource3f
+@ cdecl alGetSourcefv(long  long  ptr ) wine_alGetSourcefv
+@ cdecl alGetSourcei(long  long  ptr ) wine_alGetSourcei
+@ cdecl alGetSourceiv(long  long  ptr ) wine_alGetSourceiv
+@ cdecl alSourcePlayv(long  ptr) wine_alSourcePlayv
+@ cdecl alSourceStopv(long  ptr) wine_alSourceStopv
+@ cdecl alSourceRewindv(long  ptr) wine_alSourceRewindv
+@ cdecl alSourcePausev(long  ptr) wine_alSourcePausev
+@ cdecl alSourcePlay(long ) wine_alSourcePlay
+@ cdecl alSourceStop(long ) wine_alSourceStop
+@ cdecl alSourceRewind(long ) wine_alSourceRewind
+@ cdecl alSourcePause(long ) wine_alSourcePause
+@ cdecl alSourceQueueBuffers(long  long  ptr) wine_alSourceQueueBuffers
+@ cdecl alSourceUnqueueBuffers(long  long  ptr) wine_alSourceUnqueueBuffers
+@ cdecl alGenBuffers(long  ptr ) wine_alGenBuffers
+@ cdecl alDeleteBuffers(long  ptr ) wine_alDeleteBuffers
+@ cdecl alIsBuffer(long ) wine_alIsBuffer
+@ cdecl alBufferData(long  long  ptr  long  long ) wine_alBufferData
+@ cdecl alGetBufferf(long  long  ptr ) wine_alGetBufferf
+@ cdecl alGetBufferfv(long  long  ptr ) wine_alGetBufferfv
+@ cdecl alGetBufferi(long  long  ptr ) wine_alGetBufferi
+@ cdecl alGetBufferiv(long  long  ptr ) wine_alGetBufferiv
+@ cdecl alDopplerFactor(long ) wine_alDopplerFactor
+@ cdecl alDopplerVelocity(long ) wine_alDopplerVelocity
+@ cdecl alDistanceModel(long ) wine_alDistanceModel
+#OpenAL ALC_1_1
+#@ cdecl alcCaptureOpenDevice(str long  long  long ) wine_alcCaptureOpenDevice
+#@ cdecl alcCaptureCloseDevice(ptr) wine_alcCaptureCloseDevice
+#@ cdecl alcCaptureStart(ptr) wine_alcCaptureStart
+#@ cdecl alcCaptureStop(ptr) wine_alcCaptureStop
+#@ cdecl alcCaptureSamples(ptr ptr long ) wine_alcCaptureSamples
+#OpenAL AL_1_1
+#@ cdecl alListener3i(long  long  long  long ) wine_alListener3i
+#@ cdecl alListeneriv(long  ptr ) wine_alListeneriv
+#@ cdecl alGetListener3i(long  ptr ptr ptr) wine_alGetListener3i
+#@ cdecl alSource3i(long  long  long  long  long ) wine_alSource3i
+#@ cdecl alSourceiv(long  long  ptr ) wine_alSourceiv
+#@ cdecl alGetSource3i(long  long  ptr  ptr  ptr ) wine_alGetSource3i
+#@ cdecl alBufferf(long  long  long ) wine_alBufferf
+#@ cdecl alBuffer3f(long  long  long  long  long ) wine_alBuffer3f
+#@ cdecl alBufferfv(long  long  ptr ) wine_alBufferfv
+#@ cdecl alBufferi(long  long  long ) wine_alBufferi
+#@ cdecl alBuffer3i(long  long  long  long  long ) wine_alBuffer3i
+#@ cdecl alBufferiv(long  long  ptr ) wine_alBufferiv
+#@ cdecl alGetBuffer3f(long  long  ptr  ptr  ptr ) wine_alGetBuffer3f
+#@ cdecl alGetBuffer3i(long  long  ptr  ptr  ptr ) wine_alGetBuffer3i
+#@ cdecl alSpeedOfSound(long ) wine_alSpeedOfSound
+#OpenAL extensions
+#@ cdecl alcMacOSXRenderingQualityProcPtr(long ) wine_alcMacOSXRenderingQualityProcPtr
+#@ cdecl alMacOSXRenderChannelCountProcPtr(long ) wine_alMacOSXRenderChannelCountProcPtr
+#@ cdecl alcMacOSXMixerMaxiumumBussesProcPtr(long ) wine_alcMacOSXMixerMaxiumumBussesProcPtr
+#@ cdecl alcMacOSXMixerOutputRateProcPtr(double ) wine_alcMacOSXMixerOutputRateProcPtr
+#@ cdecl alcMacOSXGetRenderingQualityProcPtr() wine_alcMacOSXGetRenderingQualityProcPtr
+#@ cdecl alMacOSXGetRenderChannelCountProcPtr() wine_alMacOSXGetRenderChannelCountProcPtr
+#@ cdecl alcMacOSXGetMixerMaxiumumBussesProcPtr() wine_alcMacOSXGetMixerMaxiumumBussesProcPtr
+#@ cdecl alcMacOSXGetMixerOutputRateProcPtr() wine_alcMacOSXGetMixerOutputRateProcPtr
+#@ cdecl alBufferDataStatic(long  long  ptr  long  long ) wine_alBufferDataStatic
+#@ cdecl alcASAGetSourceProcPtr(long  long  ptr ptr ) wine_alcASAGetSourceProcPtr
+#@ cdecl alcASASetSourceProcPtr(long  long  ptr long ) wine_alcASASetSourceProcPtr
+#@ cdecl alcASAGetListenerProcPtr(long  ptr ptr ) wine_alcASAGetListenerProcPtr
+#@ cdecl alcASASetListenerProcPtr(long  ptr long ) wine_alcASASetListenerProcPtr
+#OpenAL ALUT -- now part of freealut -- but binary compat requires this section
+@ cdecl alutInit ( ptr  ptr) wine_alutInit 
+@ cdecl alutExit () wine_alutExit 
+@ cdecl alutLoadWAVFile ( ptr  ptr  ptr  ptr  ptr  ptr) wine_alutLoadWAVFile 
+@ cdecl alutLoadWAVMemory ( ptr  ptr  ptr  ptr  ptr  ptr) wine_alutLoadWAVMemory 
+@ cdecl alutUnloadWAV ( long  ptr  long  long) wine_alutUnloadWAV 
+#OpenAL ALUT new
+#@ cdecl alutInitWithoutContext ( ptr  ptr) wine_alutInitWithoutContext 
+#@ cdecl alutGetError () wine_alutGetError 
+#@ cdecl alutGetErrorString ( long) wine_alutGetErrorString 
+#@ cdecl alutCreateBufferFromFile ( ptr) wine_alutCreateBufferFromFile 
+#@ cdecl alutCreateBufferFromFileImage ( ptr  long) wine_alutCreateBufferFromFileImage 
+#@ cdecl alutCreateBufferHelloWorld () wine_alutCreateBufferHelloWorld 
+#@ cdecl alutCreateBufferWaveform ( long  long  long  long) wine_alutCreateBufferWaveform 
+#@ cdecl alutLoadMemoryFromFile ( ptr  ptr  ptr  ptr) wine_alutLoadMemoryFromFile 
+#@ cdecl alutLoadMemoryFromFileImage ( ptr  long  ptr  ptr  ptr) wine_alutLoadMemoryFromFileImage 
+#@ cdecl alutLoadMemoryHelloWorld ( ptr  ptr  ptr) wine_alutLoadMemoryHelloWorld 
+#@ cdecl alutLoadMemoryWaveform ( long  long  long  long  ptr  ptr  ptr) wine_alutLoadMemoryWaveform 
+#@ cdecl alutGetMIMETypes ( long) wine_alutGetMIMETypes 
+#@ cdecl alutGetMajorVersion () wine_alutGetMajorVersion 
+#@ cdecl alutGetMinorVersion () wine_alutGetMinorVersion 
+#@ cdecl alutSleep ( long) wine_alutSleep 
