From 4e061a312b5f6575462e3fa50c7df20788e5f95c Mon Sep 17 00:00:00 2001
From: digit <digit@google.com>
Date: Wed, 10 Jun 2009 14:11:28 +0200
Subject: [PATCH] Add a Windows Wave audio backend to QEMU.

This backend provides both playback and record. It doesn't depend
on any external library like SDL or FMOD, or any non-distributable
headers like the DirectSound ones.

For this reason, it is made the default for Cygwin and Mingw builds

Tested on both Cygwin and MSys builds.

Signed-off-by: David Turner <digit@google.com>
---
 Makefile          |    3 +
 audio/audio_int.h |    1 +
 audio/winaudio.c  |  663 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 configure         |    8 +-
 4 files changed, 672 insertions(+), 3 deletions(-)
 create mode 100644 audio/winaudio.c

diff --git a/Makefile b/Makefile
index 3177616..8f7894a 100644
--- a/Makefile
+++ b/Makefile
@@ -137,6 +137,9 @@ endif
 ifdef CONFIG_DSOUND
 AUDIO_OBJS += dsoundaudio.o
 endif
+ifdef CONFIG_WINAUDIO
+AUDIO_OBJS += winaudio.o
+endif
 ifdef CONFIG_FMOD
 AUDIO_OBJS += fmodaudio.o
 audio/audio.o audio/fmodaudio.o: CPPFLAGS := -I$(CONFIG_FMOD_INC) $(CPPFLAGS)
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 0abed38..f2c08ac 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -207,6 +207,7 @@ extern struct audio_driver coreaudio_audio_driver;
 extern struct audio_driver dsound_audio_driver;
 extern struct audio_driver esd_audio_driver;
 extern struct audio_driver pa_audio_driver;
+extern struct audio_driver winaudio_audio_driver;
 extern struct mixeng_volume nominal_volume;
 
 void audio_pcm_init_info (struct audio_pcm_info *info, struct audsettings *as);
diff --git a/audio/winaudio.c b/audio/winaudio.c
new file mode 100644
index 0000000..5000f7b
--- /dev/null
+++ b/audio/winaudio.c
@@ -0,0 +1,663 @@
+/*
+ * QEMU "simple" Windows audio driver
+ *
+ * Copyright (c) 2007 The Android Open Source Project
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <windows.h>
+#include <mmsystem.h>
+
+#include "qemu-common.h"
+#include "audio.h"
+
+#define AUDIO_CAP "winaudio"
+#include "audio_int.h"
+
+/* define DEBUG to 1 to dump audio debugging info at runtime to stderr */
+#define  DEBUG  0
+
+/* define DEBUG_TIMING to 1 to dump timing debugging messages */
+#define  DEBUG_TIMING  0
+
+#if DEBUG
+#  define  D(...)   printf(__VA_ARGS__)
+#else
+#  define  D(...)   ((void)0)
+#endif
+
+static struct {
+    int nb_samples;
+} conf = {
+    1024
+};
+
+#if DEBUG_TIMING
+int64_t  start_time;
+int64_t  last_time;
+#endif
+
+/* XP works well with 4, but Vista struggles with anything less than 8 */
+#define  NUM_OUT_BUFFERS  8  /* must be at least 2 */
+
+/** COMMON UTILITIES
+ **/
+
+#if DEBUG
+static void
+dump_mmerror( const char*  func, MMRESULT  error )
+{
+    const char*  reason = NULL;
+
+    fprintf(stderr, "%s returned error: ", func);
+    switch (error) {
+        case MMSYSERR_ALLOCATED:   reason="specified resource is already allocated"; break;
+        case MMSYSERR_BADDEVICEID: reason="bad device id"; break;
+        case MMSYSERR_NODRIVER:    reason="no driver is present"; break;
+        case MMSYSERR_NOMEM:       reason="unable to allocate or lock memory"; break;
+        case WAVERR_BADFORMAT:     reason="unsupported waveform-audio format"; break;
+        case WAVERR_SYNC:          reason="device is synchronous"; break;
+        default:
+            fprintf(stderr, "unknown(%d)\n", error);
+    }
+    if (reason)
+        fprintf(stderr, "%s\n", reason);
+}
+#else
+#  define  dump_mmerror(func,error)  ((void)0)
+#endif
+
+
+/** AUDIO OUT
+ **/
+
+typedef struct WinAudioOut {
+    HWVoiceOut        hw;
+    HWAVEOUT          waveout;
+    int               silence;
+    CRITICAL_SECTION  lock;
+    unsigned char*    buffer_bytes;
+    WAVEHDR           buffers[ NUM_OUT_BUFFERS ];
+    int               write_index;   /* starting first writable buffer      */
+    int               write_count;   /* available writable buffers count    */
+    int               write_pos;     /* position in current writable buffer */
+    int               write_size;    /* size in bytes of each buffer        */
+} WinAudioOut;
+
+/* The callback that is called when Windows has finished playing a buffer */
+static void CALLBACK
+winaudio_out_buffer_done (HWAVEOUT hwo, UINT uMsg, DWORD_PTR dwInstance,
+                          DWORD dwParam1, DWORD dwParam2)
+{
+    WinAudioOut*  s = (WinAudioOut*) dwInstance;
+
+    /* Only service "buffer done playing" messages */
+    if ( uMsg != WOM_DONE )
+        return;
+
+    /* Signal that we are done playing a buffer */
+    EnterCriticalSection( &s->lock );
+    if (s->write_count < NUM_OUT_BUFFERS)
+        s->write_count += 1;
+    LeaveCriticalSection( &s->lock );
+}
+
+static int
+winaudio_out_write (SWVoiceOut *sw, void *buf, int len)
+{
+    return audio_pcm_sw_write (sw, buf, len);
+}
+
+static void
+winaudio_out_fini (HWVoiceOut *hw)
+{
+    WinAudioOut*  s = (WinAudioOut*) hw;
+    int           i;
+
+    if (s->waveout) {
+        waveOutReset(s->waveout);
+        s->waveout = 0;
+    }
+
+    for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+        if ( s->buffers[i].dwUser != 0xFFFF ) {
+            waveOutUnprepareHeader(
+                s->waveout, &s->buffers[i], sizeof(s->buffers[i]) );
+                s->buffers[i].dwUser = 0xFFFF;
+        }
+    }
+
+    if (s->buffer_bytes != NULL) {
+        qemu_free(s->buffer_bytes);
+        s->buffer_bytes = NULL;
+    }
+
+    if (s->waveout) {
+        waveOutClose(s->waveout);
+        s->waveout = NULL;
+    }
+}
+
+
+static int
+winaudio_out_init (HWVoiceOut *hw, struct audsettings *as)
+{
+    WinAudioOut*   s = (WinAudioOut*) hw;
+    MMRESULT       result;
+    WAVEFORMATEX   format;
+    int            shift, i, samples_size;
+
+    s->waveout = NULL;
+    InitializeCriticalSection( &s->lock );
+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+        s->buffers[i].dwUser = 0xFFFF;
+    }
+    s->buffer_bytes = NULL;
+
+    /* compute desired wave output format */
+    format.wFormatTag      = WAVE_FORMAT_PCM;
+    format.nChannels       = as->nchannels;
+    format.nSamplesPerSec  = as->freq;
+    format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+    s->silence = 0;
+
+    switch (as->fmt) {
+        case AUD_FMT_S8:   shift = 0; break;
+        case AUD_FMT_U8:   shift = 0; s->silence = 0x80; break;
+        case AUD_FMT_S16:  shift = 1; break;
+        case AUD_FMT_U16:  shift = 1; s->silence = 0x8000; break;
+        case AUD_FMT_S32:  shift = 2; break;
+        case AUD_FMT_U32:  shift = 2; s->silence = 0x80000000; break;
+        default:
+            fprintf(stderr, "qemu: winaudio: Bad output audio format: %d\n",
+                    as->fmt);
+                return -1;
+    }
+
+    format.nAvgBytesPerSec = (format.nSamplesPerSec & format.nChannels) << shift;
+    format.nBlockAlign     = format.nChannels << shift;
+    format.wBitsPerSample  = 8 << shift;
+    format.cbSize          = 0;
+
+    /* open the wave out device */
+    result = waveOutOpen( &s->waveout, WAVE_MAPPER, &format,
+                          (DWORD_PTR) winaudio_out_buffer_done,
+                          (DWORD_PTR) hw, CALLBACK_FUNCTION);
+    if ( result != MMSYSERR_NOERROR ) {
+        dump_mmerror( "qemu: winaudio: waveOutOpen()", result);
+            return -1;
+    }
+
+    samples_size    = format.nBlockAlign * conf.nb_samples;
+    s->buffer_bytes = qemu_malloc( NUM_OUT_BUFFERS * samples_size );
+    if (s->buffer_bytes == NULL) {
+            waveOutClose( s->waveout );
+            s->waveout = NULL;
+            fprintf(stderr, "not enough memory for Windows audio buffers\n");
+            return -1;
+    }
+
+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+        memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+        s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);
+        s->buffers[i].dwBufferLength = samples_size;
+        s->buffers[i].dwFlags        = WHDR_DONE;
+
+        result = waveOutPrepareHeader( s->waveout, &s->buffers[i],
+                                       sizeof(s->buffers[i]) );
+        if ( result != MMSYSERR_NOERROR ) {
+            dump_mmerror("waveOutPrepareHeader()", result);
+            return -1;
+        }
+    }
+
+#if DEBUG
+    /* Check the sound device we retrieved */
+    {
+        WAVEOUTCAPS caps;
+
+        result = waveOutGetDevCaps((UINT) s->waveout, &caps, sizeof(caps));
+        if ( result != MMSYSERR_NOERROR ) {
+            dump_mmerror("waveOutGetDevCaps()", result);
+        } else
+            printf("Audio out device: %s\n", caps.szPname);
+    }
+#endif
+
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = conf.nb_samples*2;
+
+    s->write_index = 0;
+    s->write_count = NUM_OUT_BUFFERS;
+    s->write_pos   = 0;
+    s->write_size  = samples_size;
+    return 0;
+}
+
+
+static int
+winaudio_out_run (HWVoiceOut *hw)
+{
+    WinAudioOut*  s      = (WinAudioOut*) hw;
+    int           played = 0;
+    int           has_buffer;
+    int           live = audio_pcm_hw_get_live_out (hw);
+
+    if (!live) {
+        return 0;
+    }
+
+    EnterCriticalSection( &s->lock );
+    has_buffer = (s->write_count > 0);
+    LeaveCriticalSection( &s->lock );
+
+    if (!has_buffer)
+        return 0;
+
+    while (live > 0) {
+        WAVEHDR*      wav_buffer  = s->buffers + s->write_index;
+        int           wav_bytes   = (s->write_size - s->write_pos);
+        int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
+        int           hw_samples  = audio_MIN(hw->samples - hw->rpos, live);
+        struct st_sample*    src  = hw->mix_buf + hw->rpos;
+        uint8_t*      dst         = (uint8_t*)wav_buffer->lpData + s->write_pos;
+
+        if (wav_samples > hw_samples) {
+                wav_samples = hw_samples;
+        }
+
+        wav_bytes = wav_samples << hw->info.shift;
+
+        //D("run_out: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d rpos:%d hwsamples:%d\n", s->write_index,
+        //   s->write_pos, s->write_size, wav_samples, wav_bytes, live, hw->rpos, hw->samples);
+        hw->clip (dst, src, wav_samples);
+        hw->rpos += wav_samples;
+        if (hw->rpos >= hw->samples)
+                hw->rpos -= hw->samples;
+
+        live         -= wav_samples;
+        played       += wav_samples;
+        s->write_pos += wav_bytes;
+        if (s->write_pos == s->write_size) {
+#if DEBUG_TIMING
+            int64_t  now  = qemu_get_clock(vm_clock) - start_time;
+            int64_t  diff = now - last_time;
+
+            D("run_out: (%7.3f:%7d):waveOutWrite buffer:%d\n",
+               now/1e9, (now-last_time)/1e9, s->write_index);
+            last_time = now;
+#endif
+            waveOutWrite( s->waveout, wav_buffer, sizeof(*wav_buffer) );
+            s->write_pos    = 0;
+            s->write_index += 1;
+            if (s->write_index == NUM_OUT_BUFFERS)
+                s->write_index = 0;
+
+            EnterCriticalSection( &s->lock );
+            if (--s->write_count == 0) {
+                live = 0;
+            }
+            LeaveCriticalSection( &s->lock );
+        }
+    }
+    return played;
+}
+
+static int
+winaudio_out_ctl (HWVoiceOut *hw, int cmd, ...)
+{
+    WinAudioOut*  s = (WinAudioOut*) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        waveOutRestart( s->waveout );
+        break;
+
+    case VOICE_DISABLE:
+        waveOutPause( s->waveout );
+        break;
+    }
+    return 0;
+}
+
+/** AUDIO IN
+ **/
+
+#define  NUM_IN_BUFFERS  2
+
+typedef struct WinAudioIn {
+    HWVoiceIn         hw;
+    HWAVEIN           wavein;
+    CRITICAL_SECTION  lock;
+    unsigned char*    buffer_bytes;
+    WAVEHDR           buffers[ NUM_IN_BUFFERS ];
+    int               read_index;
+    int               read_count;
+    int               read_pos;
+    int               read_size;
+} WinAudioIn;
+
+/* The callback that is called when Windows has finished filling a buffer */
+static void CALLBACK
+winaudio_in_buffer_done (HWAVEIN hwi, UINT uMsg, DWORD_PTR dwInstance,
+                         DWORD dwParam1, DWORD dwParam2)
+{
+    WinAudioIn*  s = (WinAudioIn*) dwInstance;
+
+    /* Only service "buffer filled" messages */
+    if ( uMsg != WIM_DATA )
+        return;
+
+    /* Signal that we are done playing a buffer */
+    EnterCriticalSection( &s->lock );
+    if (s->read_count < NUM_IN_BUFFERS)
+        s->read_count += 1;
+    LeaveCriticalSection( &s->lock );
+}
+
+static void
+winaudio_in_fini (HWVoiceIn *hw)
+{
+    WinAudioIn*  s = (WinAudioIn*) hw;
+    int          i;
+
+    if (s->wavein) {
+        waveInReset(s->wavein);
+        s->wavein = 0;
+    }
+
+    for ( i=0; i<NUM_OUT_BUFFERS; ++i ) {
+        if ( s->buffers[i].dwUser != 0xFFFF ) {
+            waveInUnprepareHeader(
+                s->wavein, &s->buffers[i], sizeof(s->buffers[i]) );
+                s->buffers[i].dwUser = 0xFFFF;
+        }
+    }
+
+    if (s->buffer_bytes != NULL) {
+        qemu_free(s->buffer_bytes);
+        s->buffer_bytes = NULL;
+    }
+
+    if (s->wavein) {
+        waveInClose(s->wavein);
+        s->wavein = NULL;
+    }
+}
+
+
+static int
+winaudio_in_init (HWVoiceIn *hw, struct audsettings *as)
+{
+    WinAudioIn*   s = (WinAudioIn*) hw;
+    MMRESULT      result;
+    WAVEFORMATEX  format;
+    int           shift, i, samples_size;
+
+    s->wavein = NULL;
+    InitializeCriticalSection( &s->lock );
+    for (i = 0; i < NUM_OUT_BUFFERS; i++) {
+        s->buffers[i].dwUser = 0xFFFF;
+    }
+    s->buffer_bytes = NULL;
+
+    /* compute desired wave input format */
+    format.wFormatTag      = WAVE_FORMAT_PCM;
+    format.nChannels       = as->nchannels;
+    format.nSamplesPerSec  = as->freq;
+    format.nAvgBytesPerSec = as->freq*as->nchannels;
+
+    switch (as->fmt) {
+        case AUD_FMT_S8:   shift = 0; break;
+        case AUD_FMT_U8:   shift = 0; break;
+        case AUD_FMT_S16:  shift = 1; break;
+        case AUD_FMT_U16:  shift = 1; break;
+        case AUD_FMT_S32:  shift = 2; break;
+        case AUD_FMT_U32:  shift = 2; break;
+        default:
+            fprintf(stderr, "qemu: winaudio: Bad input audio format: %d\n",
+                    as->fmt);
+            return -1;
+    }
+
+    format.nAvgBytesPerSec = (format.nSamplesPerSec * format.nChannels) << shift;
+    format.nBlockAlign     = format.nChannels << shift;
+    format.wBitsPerSample  = 8 << shift;
+    format.cbSize          = 0;
+
+    /* open the wave in device */
+    result = waveInOpen( &s->wavein, WAVE_MAPPER, &format,
+                         (DWORD_PTR)winaudio_in_buffer_done, (DWORD_PTR) hw,
+                         CALLBACK_FUNCTION);
+    if ( result != MMSYSERR_NOERROR ) {
+        dump_mmerror( "qemu: winaudio: waveInOpen()", result);
+            return -1;
+    }
+
+    samples_size    = format.nBlockAlign * conf.nb_samples;
+    s->buffer_bytes = qemu_malloc( NUM_IN_BUFFERS * samples_size );
+    if (s->buffer_bytes == NULL) {
+        waveInClose( s->wavein );
+        s->wavein = NULL;
+        fprintf(stderr, "not enough memory for Windows audio buffers\n");
+        return -1;
+    }
+
+    for (i = 0; i < NUM_IN_BUFFERS; i++) {
+        memset( &s->buffers[i], 0, sizeof(s->buffers[i]) );
+        s->buffers[i].lpData         = (LPSTR)(s->buffer_bytes + i*samples_size);
+        s->buffers[i].dwBufferLength = samples_size;
+        s->buffers[i].dwFlags        = WHDR_DONE;
+
+        result = waveInPrepareHeader( s->wavein, &s->buffers[i],
+                               sizeof(s->buffers[i]) );
+        if ( result != MMSYSERR_NOERROR ) {
+            dump_mmerror("waveInPrepareHeader()", result);
+            return -1;
+        }
+
+        result = waveInAddBuffer( s->wavein, &s->buffers[i],
+                                  sizeof(s->buffers[i]) );
+        if ( result != MMSYSERR_NOERROR ) {
+            dump_mmerror("waveInAddBuffer()", result);
+            return -1;
+        }
+    }
+
+#if DEBUG
+    /* Check the sound device we retrieved */
+    {
+        WAVEINCAPS caps;
+
+        result = waveInGetDevCaps((UINT) s->wavein, &caps, sizeof(caps));
+        if ( result != MMSYSERR_NOERROR ) {
+            dump_mmerror("waveInGetDevCaps()", result);
+        } else
+            printf("Audio in device: %s\n", caps.szPname);
+    }
+#endif
+
+    audio_pcm_init_info (&hw->info, as);
+    hw->samples = conf.nb_samples*2;
+
+    s->read_index = 0;
+    s->read_count = 0;
+    s->read_pos   = 0;
+    s->read_size  = samples_size;
+    return 0;
+}
+
+
+/* report the number of captured samples to the audio subsystem */
+static int
+winaudio_in_run (HWVoiceIn *hw)
+{
+    WinAudioIn*  s        = (WinAudioIn*) hw;
+    int          captured = 0;
+    int          has_buffer;
+    int          live = hw->samples - hw->total_samples_captured;
+
+    if (!live) {
+        return 0;
+    }
+
+    EnterCriticalSection( &s->lock );
+    has_buffer = (s->read_count > 0);
+    LeaveCriticalSection( &s->lock );
+
+    if (!has_buffer)
+        return 0;
+
+    while (live > 0) {
+        WAVEHDR*      wav_buffer  = s->buffers + s->read_index;
+        int           wav_bytes   = (s->read_size - s->read_pos);
+        int           wav_samples = audio_MIN(wav_bytes >> hw->info.shift, live);
+        int           hw_samples  = audio_MIN(hw->samples - hw->wpos, live);
+        struct st_sample*  dst    = hw->conv_buf + hw->wpos;
+        uint8_t*      src         = (uint8_t*)wav_buffer->lpData + s->read_pos;
+
+        if (wav_samples > hw_samples) {
+            wav_samples = hw_samples;
+        }
+
+        wav_bytes = wav_samples << hw->info.shift;
+
+        D("%s: buffer:%d pos:%d size:%d wsamples:%d wbytes:%d live:%d wpos:%d hwsamples:%d\n",
+           __FUNCTION__, s->read_index, s->read_pos, s->read_size, wav_samples, wav_bytes, live,
+           hw->wpos, hw->samples);
+
+        hw->conv(dst, src, wav_samples, &nominal_volume);
+
+        hw->wpos += wav_samples;
+        if (hw->wpos >= hw->samples)
+            hw->wpos -= hw->samples;
+
+        live        -= wav_samples;
+        captured    += wav_samples;
+        s->read_pos += wav_bytes;
+        if (s->read_pos == s->read_size) {
+            s->read_pos    = 0;
+            s->read_index += 1;
+            if (s->read_index == NUM_IN_BUFFERS)
+                s->read_index = 0;
+
+            waveInAddBuffer( s->wavein, wav_buffer, sizeof(*wav_buffer) );
+
+            EnterCriticalSection( &s->lock );
+            if (--s->read_count == 0) {
+                live = 0;
+            }
+            LeaveCriticalSection( &s->lock );
+        }
+    }
+    return  captured;
+}
+
+
+static int
+winaudio_in_read (SWVoiceIn *sw, void *buf, int len)
+{
+    int  ret = audio_pcm_sw_read (sw, buf, len);
+    if (ret > 0)
+        D("%s: (%d) returned %d\n", __FUNCTION__, len, ret);
+    return ret;
+}
+
+
+static int
+winaudio_in_ctl (HWVoiceIn *hw, int cmd, ...)
+{
+    WinAudioIn*  s = (WinAudioIn*) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        D("%s: enable audio in\n", __FUNCTION__);
+        waveInStart( s->wavein );
+        break;
+
+    case VOICE_DISABLE:
+        D("%s: disable audio in\n", __FUNCTION__);
+        waveInStop( s->wavein );
+        break;
+    }
+    return 0;
+}
+
+/** AUDIO STATE
+ **/
+
+typedef struct WinAudioState {
+    int  dummy;
+} WinAudioState;
+
+static WinAudioState  g_winaudio;
+
+static void*
+winaudio_init(void)
+{
+    WinAudioState*  s = &g_winaudio;
+
+#if DEBUG_TIMING
+    start_time = qemu_get_clock(vm_clock);
+    last_time  = 0;
+#endif
+
+    return s;
+}
+
+
+static void
+winaudio_fini (void *opaque)
+{
+}
+
+static struct audio_option winaudio_options[] = {
+    {"SAMPLES", AUD_OPT_INT, &conf.nb_samples,
+     "Size of Windows audio buffer in samples", NULL, 0},
+    {NULL, 0, NULL, NULL, NULL, 0}
+};
+
+static struct audio_pcm_ops winaudio_pcm_ops = {
+    winaudio_out_init,
+    winaudio_out_fini,
+    winaudio_out_run,
+    winaudio_out_write,
+    winaudio_out_ctl,
+
+    winaudio_in_init,
+    winaudio_in_fini,
+    winaudio_in_run,
+    winaudio_in_read,
+    winaudio_in_ctl
+};
+
+struct audio_driver winaudio_audio_driver = {
+    INIT_FIELD (name           = ) "winaudio",
+    INIT_FIELD (descr          = ) "Windows wave audio",
+    INIT_FIELD (options        = ) winaudio_options,
+    INIT_FIELD (init           = ) winaudio_init,
+    INIT_FIELD (fini           = ) winaudio_fini,
+    INIT_FIELD (pcm_ops        = ) &winaudio_pcm_ops,
+    INIT_FIELD (can_be_default = ) 1,
+    INIT_FIELD (max_voices_out = ) 1,
+    INIT_FIELD (max_voices_in  = ) 1,
+    INIT_FIELD (voice_size_out = ) sizeof (WinAudioOut),
+    INIT_FIELD (voice_size_in  = ) sizeof (WinAudioIn)
+};
diff --git a/configure b/configure
index 89e7f53..3ddb138 100755
--- a/configure
+++ b/configure
@@ -219,14 +219,16 @@ OS_CFLAGS="-mno-cygwin"
 if [ "$cpu" = "i386" ] ; then
     kqemu="yes"
 fi
-audio_possible_drivers="sdl"
+audio_drv_list="winaudio"
+audio_possible_drivers="winaudio sdl"
 ;;
 MINGW32*)
 mingw32="yes"
 if [ "$cpu" = "i386" ] ; then
     kqemu="yes"
 fi
-audio_possible_drivers="dsound sdl fmod"
+audio_drv_list=winaudio
+audio_possible_drivers="dsound winaudio sdl fmod"
 ;;
 GNU/kFreeBSD)
 audio_drv_list="oss"
@@ -1033,7 +1035,7 @@ for drv in $audio_drv_list; do
         "pa_simple *s = NULL; pa_simple_free(s); return 0;"
     ;;
 
-    oss|sdl|core|wav|dsound)
+    oss|sdl|core|wav|dsound|winaudio)
     # XXX: Probes for CoreAudio, DirectSound, SDL(?)
     ;;
 
-- 
1.6.1.2

