WARNING - OLD ARCHIVES

This is an archived copy of the Xen.org mailing list, which we have preserved to ensure that existing links to archives are not broken. The live archive, which contains the latest emails, can be found at http://lists.xen.org/
   
 
 
Xen 
 
Home Products Support Community News
 
   
 

xen-devel

[Xen-devel] [PATCH] Add sound blaster support in device model

To: Ian Pratt <Ian.Pratt@xxxxxxxxxxxx>, Keir Fraser <Keir.Fraser@xxxxxxxxxxxx>
Subject: [Xen-devel] [PATCH] Add sound blaster support in device model
From: Edwin Zhai <edwin.zhai@xxxxxxxxx>
Date: Tue, 20 Dec 2005 17:34:34 +0800
Cc: xen-devel@xxxxxxxxxxxxxxxxxxx
Delivery-date: Tue, 20 Dec 2005 09:38:14 +0000
Envelope-to: www-data@xxxxxxxxxxxxxxxxxxx
List-help: <mailto:xen-devel-request@lists.xensource.com?subject=help>
List-id: Xen developer discussion <xen-devel.lists.xensource.com>
List-post: <mailto:xen-devel@lists.xensource.com>
List-subscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=subscribe>
List-unsubscribe: <http://lists.xensource.com/cgi-bin/mailman/listinfo/xen-devel>, <mailto:xen-devel-request@lists.xensource.com?subject=unsubscribe>
Sender: xen-devel-bounces@xxxxxxxxxxxxxxxxxxx
User-agent: Mutt/1.5.8i
Add sound blaster support in device model
Signed-off-by: Zhai Edwin <edwin.zhai@xxxxxxxxx>

diff -r 1283d309a603 tools/examples/xmexample.vmx
--- a/tools/examples/xmexample.vmx      Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/examples/xmexample.vmx      Tue Dec 20 16:58:20 2005 +0800
@@ -135,7 +135,7 @@
 
 #-----------------------------------------------------------------------------
 #   enable audio support
-#enable-audio=1
+#audio=1
 
 
 #-----------------------------------------------------------------------------
diff -r 1283d309a603 tools/ioemu/hw/pc.c
--- a/tools/ioemu/hw/pc.c       Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/hw/pc.c       Tue Dec 20 16:58:20 2005 +0800
@@ -563,6 +563,15 @@
 
     kbd_init();
     DMA_init(0);
+   
+    if (audio_enabled) {
+        AUD_init();
+#ifdef USE_SB16
+        if (sb16_enabled)
+            SB16_init();
+#endif
+    }
+    
 
     floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
 
diff -r 1283d309a603 tools/ioemu/target-i386-dm/Makefile
--- a/tools/ioemu/target-i386-dm/Makefile       Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/target-i386-dm/Makefile       Tue Dec 20 16:58:20 2005 +0800
@@ -272,6 +272,7 @@
 VL_OBJS+= ide.o ne2000.o pckbd.o vga.o dma.o
 VL_OBJS+= fdc.o mc146818rtc.o serial.o i8259_stub.o i8254.o pc.o port-e9.o
 VL_OBJS+= cirrus_vga.o pcnet.o
+VL_OBJS+= $(SOUND_HW) $(AUDIODRV) mixeng.o
 
 ifeq ($(TARGET_ARCH), ppc)
 VL_OBJS+= ppc.o ide.o ne2000.o pckbd.o vga.o $(SOUND_HW) dma.o $(AUDIODRV)
diff -r 1283d309a603 tools/ioemu/vl.h
--- a/tools/ioemu/vl.h  Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/vl.h  Tue Dec 20 16:58:20 2005 +0800
@@ -37,6 +37,7 @@
 #include <unistd.h>
 #include <fcntl.h>
 #include <sys/stat.h>
+#include "audio/audio.h"
 
 #ifndef O_LARGEFILE
 #define O_LARGEFILE 0
diff -r 1283d309a603 tools/python/xen/xend/image.py
--- a/tools/python/xen/xend/image.py    Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/python/xen/xend/image.py    Tue Dec 20 16:58:20 2005 +0800
@@ -237,7 +237,7 @@
     # Return a list of cmd line args to the device models based on the
     # xm config file
     def parseDeviceModelArgs(self, imageConfig, deviceConfig):
-        dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'ne2000', 
+        dmargs = [ 'cdrom', 'boot', 'fda', 'fdb', 'ne2000', 'audio',
                    'localtime', 'serial', 'stdvga', 'isa', 'vcpus']
         ret = []
         for a in dmargs:
@@ -246,9 +246,10 @@
             # python doesn't allow '-' in variable names
             if a == 'stdvga': a = 'std-vga'
             if a == 'ne2000': a = 'nic-ne2000'
+            if a == 'audio': a = 'enable-audio'
 
             # Handle booleans gracefully
-            if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000']:
+            if a in ['localtime', 'std-vga', 'isa', 'nic-ne2000', 
'enable-audio']:
                 if v != None: v = int(v)
                 if v: ret.append("-%s" % a)
             else:
diff -r 1283d309a603 tools/python/xen/xm/create.py
--- a/tools/python/xen/xm/create.py     Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/python/xen/xm/create.py     Tue Dec 20 16:58:20 2005 +0800
@@ -371,6 +371,10 @@
 gopts.var('ne2000', val='no|yes',
           fn=set_bool, default=0,
           use="Should device models use ne2000?")
+
+gopts.var('audio', val='no|yes',
+          fn=set_bool, default=0,
+          use="Should device models enable audio?")
 
 gopts.var('vnc', val='',
           fn=set_value, default=None,
@@ -521,7 +525,7 @@
     """Create the config for VMX devices.
     """
     args = [ 'device_model', 'vcpus', 'cdrom', 'boot', 'fda', 'fdb',
-             'localtime', 'serial', 'stdvga', 'isa', 'nographic',
+             'localtime', 'serial', 'stdvga', 'isa', 'nographic', 'audio',
              'vnc', 'vncviewer', 'sdl', 'display', 'ne2000', 'lapic']
     for a in args:
         if (vals.__dict__[a]):
diff -r 1283d309a603 tools/ioemu/audio/audio.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/audio.c Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,910 @@
+/*
+ * QEMU Audio subsystem
+ * 
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ * 
+ * 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 <assert.h>
+#include "vl.h"
+
+#define USE_WAV_AUDIO
+
+#include "audio/audio_int.h"
+
+#define dolog(...) AUD_log ("audio", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define QC_AUDIO_DRV    "QEMU_AUDIO_DRV"
+#define QC_VOICES       "QEMU_VOICES"
+#define QC_FIXED_FORMAT "QEMU_FIXED_FORMAT"
+#define QC_FIXED_FREQ   "QEMU_FIXED_FREQ"
+
+static HWVoice *hw_voices;
+
+AudioState audio_state = {
+    1,                          /* use fixed settings */
+    44100,                      /* fixed frequency */
+    2,                          /* fixed channels */
+    AUD_FMT_S16,                /* fixed format */
+    1,                          /* number of hw voices */
+    -1                          /* voice size */
+};
+
+/* http://www.df.lth.se/~john_e/gems/gem002d.html */
+/* http://www.multi-platforms.com/Tips/PopCount.htm */
+uint32_t popcount (uint32_t u)
+{
+    u = ((u&0x55555555) + ((u>>1)&0x55555555));
+    u = ((u&0x33333333) + ((u>>2)&0x33333333));
+    u = ((u&0x0f0f0f0f) + ((u>>4)&0x0f0f0f0f));
+    u = ((u&0x00ff00ff) + ((u>>8)&0x00ff00ff));
+    u = ( u&0x0000ffff) + (u>>16);
+    return u;
+}
+
+inline uint32_t lsbindex (uint32_t u)
+{
+    return popcount ((u&-u)-1);
+}
+
+int audio_get_conf_int (const char *key, int defval)
+{
+    int val = defval;
+    char *strval;
+
+    strval = getenv (key);
+    if (strval) {
+        val = atoi (strval);
+    }
+
+    return val;
+}
+
+const char *audio_get_conf_str (const char *key, const char *defval)
+{
+    const char *val = getenv (key);
+    if (!val)
+        return defval;
+    else
+        return val;
+}
+
+void AUD_log (const char *cap, const char *fmt, ...)
+{
+    va_list ap;
+    fprintf (stderr, "%s: ", cap);
+    va_start (ap, fmt);
+    vfprintf (stderr, fmt, ap);
+    va_end (ap);
+}
+
+/*
+ * Soft Voice
+ */
+void pcm_sw_free_resources (SWVoice *sw)
+{
+    if (sw->buf) qemu_free (sw->buf);
+    if (sw->rate) st_rate_stop (sw->rate);
+    sw->buf = NULL;
+    sw->rate = NULL;
+}
+
+int pcm_sw_alloc_resources (SWVoice *sw)
+{
+    sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t));
+    if (!sw->buf)
+        return -1;
+
+    sw->rate = st_rate_start (sw->freq, sw->hw->freq);
+    if (!sw->rate) {
+        qemu_free (sw->buf);
+        sw->buf = NULL;
+        return -1;
+    }
+    return 0;
+}
+
+void pcm_sw_fini (SWVoice *sw)
+{
+    pcm_sw_free_resources (sw);
+}
+
+int pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
+                 int nchannels, audfmt_e fmt)
+{
+    int bits = 8, sign = 0;
+
+    switch (fmt) {
+    case AUD_FMT_S8:
+        sign = 1;
+    case AUD_FMT_U8:
+        break;
+
+    case AUD_FMT_S16:
+        sign = 1;
+    case AUD_FMT_U16:
+        bits = 16;
+        break;
+    }
+
+    sw->hw = hw;
+    sw->freq = freq;
+    sw->fmt = fmt;
+    sw->nchannels = nchannels;
+    sw->shift = (nchannels == 2) + (bits == 16);
+    sw->align = (1 << sw->shift) - 1;
+    sw->left = 0;
+    sw->pos = 0;
+    sw->wpos = 0;
+    sw->live = 0;
+    sw->ratio = (sw->hw->freq * ((int64_t) INT_MAX)) / sw->freq;
+    sw->bytes_per_second = sw->freq << sw->shift;
+    sw->conv = mixeng_conv[nchannels == 2][sign][bits == 16];
+
+    pcm_sw_free_resources (sw);
+    return pcm_sw_alloc_resources (sw);
+}
+
+/* Hard voice */
+void pcm_hw_free_resources (HWVoice *hw)
+{
+    if (hw->mix_buf)
+        qemu_free (hw->mix_buf);
+    hw->mix_buf = NULL;
+}
+
+int pcm_hw_alloc_resources (HWVoice *hw)
+{
+    hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t));
+    if (!hw->mix_buf)
+        return -1;
+    return 0;
+}
+
+void pcm_hw_fini (HWVoice *hw)
+{
+    if (hw->active) {
+        ldebug ("pcm_hw_fini: %d %d %d\n", hw->freq, hw->nchannels, hw->fmt);
+        pcm_hw_free_resources (hw);
+        hw->pcm_ops->fini (hw);
+        memset (hw, 0, audio_state.drv->voice_size);
+    }
+}
+
+void pcm_hw_gc (HWVoice *hw)
+{
+    if (hw->nb_voices)
+        return;
+
+    pcm_hw_fini (hw);
+}
+
+int pcm_hw_get_live (HWVoice *hw)
+{
+    int i, alive = 0, live = hw->samples;
+
+    for (i = 0; i < hw->nb_voices; i++) {
+        if (hw->pvoice[i]->live) {
+            live = audio_MIN (hw->pvoice[i]->live, live);
+            alive += 1;
+        }
+    }
+
+    if (alive)
+        return live;
+    else
+        return -1;
+}
+
+int pcm_hw_get_live2 (HWVoice *hw, int *nb_active)
+{
+    int i, alive = 0, live = hw->samples;
+
+    *nb_active = 0;
+    for (i = 0; i < hw->nb_voices; i++) {
+        if (hw->pvoice[i]->live) {
+            if (hw->pvoice[i]->live < live) {
+                *nb_active = hw->pvoice[i]->active != 0;
+                live = hw->pvoice[i]->live;
+            }
+            alive += 1;
+        }
+    }
+
+    if (alive)
+        return live;
+    else
+        return -1;
+}
+
+void pcm_hw_dec_live (HWVoice *hw, int decr)
+{
+    int i;
+
+    for (i = 0; i < hw->nb_voices; i++) {
+        if (hw->pvoice[i]->live) {
+            hw->pvoice[i]->live -= decr;
+        }
+    }
+}
+
+void pcm_hw_clear (HWVoice *hw, void *buf, int len)
+{
+    if (!len)
+        return;
+
+    switch (hw->fmt) {
+    case AUD_FMT_S16:
+    case AUD_FMT_S8:
+        memset (buf, len << hw->shift, 0x00);
+        break;
+
+    case AUD_FMT_U8:
+        memset (buf, len << hw->shift, 0x80);
+        break;
+
+    case AUD_FMT_U16:
+        {
+            unsigned int i;
+            uint16_t *p = buf;
+            int shift = hw->nchannels - 1;
+
+            for (i = 0; i < len << shift; i++) {
+                p[i] = INT16_MAX;
+            }
+        }
+        break;
+    }
+}
+
+int pcm_hw_write (SWVoice *sw, void *buf, int size)
+{
+    int hwsamples, samples, isamp, osamp, wpos, live, dead, left, swlim, blck;
+    int ret = 0, pos = 0;
+    if (!sw)
+        return size;
+
+    hwsamples = sw->hw->samples;
+    samples = size >> sw->shift;
+
+    if (!sw->live) {
+        sw->wpos = sw->hw->rpos;
+    }
+    wpos = sw->wpos;
+    live = sw->live;
+    dead = hwsamples - live;
+    swlim = (dead * ((int64_t) INT_MAX)) / sw->ratio;
+    swlim = audio_MIN (swlim, samples);
+
+    ldebug ("size=%d live=%d dead=%d swlim=%d wpos=%d\n",
+           size, live, dead, swlim, wpos);
+    if (swlim)
+        sw->conv (sw->buf, buf, swlim);
+
+    while (swlim) {
+        dead = hwsamples - live;
+        left = hwsamples - wpos;
+        blck = audio_MIN (dead, left);
+        if (!blck) {
+            /* dolog ("swlim=%d\n", swlim); */
+            break;
+        }
+        isamp = swlim;
+        osamp = blck;
+        st_rate_flow (sw->rate, sw->buf + pos, sw->hw->mix_buf + wpos, &isamp, 
&osamp);
+        ret += isamp;
+        swlim -= isamp;
+        pos += isamp;
+        live += osamp;
+        wpos = (wpos + osamp) % hwsamples;
+    }
+
+    sw->wpos = wpos;
+    sw->live = live;
+    return ret << sw->shift;
+}
+
+int pcm_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
+{
+    int sign = 0, bits = 8;
+
+    pcm_hw_fini (hw);
+    ldebug ("pcm_hw_init: %d %d %d\n", freq, nchannels, fmt);
+    if (hw->pcm_ops->init (hw, freq, nchannels, fmt)) {
+        memset (hw, 0, audio_state.drv->voice_size);
+        return -1;
+    }
+
+    switch (hw->fmt) {
+    case AUD_FMT_S8:
+        sign = 1;
+    case AUD_FMT_U8:
+        break;
+
+    case AUD_FMT_S16:
+        sign = 1;
+    case AUD_FMT_U16:
+        bits = 16;
+        break;
+    }
+
+    hw->nb_voices = 0;
+    hw->active = 1;
+    hw->shift = (hw->nchannels == 2) + (bits == 16);
+    hw->bytes_per_second = hw->freq << hw->shift;
+    hw->align = (1 << hw->shift) - 1;
+    hw->samples = hw->bufsize >> hw->shift;
+    hw->clip = mixeng_clip[hw->nchannels == 2][sign][bits == 16];
+    if (pcm_hw_alloc_resources (hw)) {
+        pcm_hw_fini (hw);
+        return -1;
+    }
+    return 0;
+}
+
+static int dist (void *hw)
+{
+    if (hw) {
+        return (((uint8_t *) hw - (uint8_t *) hw_voices)
+                / audio_state.voice_size) + 1;
+    }
+    else {
+        return 0;
+    }
+}
+
+#define ADVANCE(hw) hw ? advance (hw, audio_state.voice_size) : hw_voices
+
+HWVoice *pcm_hw_find_any (HWVoice *hw)
+{
+    int i = dist (hw);
+    for (; i < audio_state.nb_hw_voices; i++) {
+        hw = ADVANCE (hw);
+        return hw;
+    }
+    return NULL;
+}
+
+HWVoice *pcm_hw_find_any_active (HWVoice *hw)
+{
+    int i = dist (hw);
+    for (; i < audio_state.nb_hw_voices; i++) {
+        hw = ADVANCE (hw);
+        if (hw->active)
+            return hw;
+    }
+    return NULL;
+}
+
+HWVoice *pcm_hw_find_any_active_enabled (HWVoice *hw)
+{
+    int i = dist (hw);
+    for (; i < audio_state.nb_hw_voices; i++) {
+        hw = ADVANCE (hw);
+        if (hw->active && hw->enabled)
+            return hw;
+    }
+    return NULL;
+}
+
+HWVoice *pcm_hw_find_any_passive (HWVoice *hw)
+{
+    int i = dist (hw);
+    for (; i < audio_state.nb_hw_voices; i++) {
+        hw = ADVANCE (hw);
+        if (!hw->active)
+            return hw;
+    }
+    return NULL;
+}
+
+HWVoice *pcm_hw_find_specific (HWVoice *hw, int freq,
+                               int nchannels, audfmt_e fmt)
+{
+    while ((hw = pcm_hw_find_any_active (hw))) {
+        if (hw->freq == freq &&
+            hw->nchannels == nchannels &&
+            hw->fmt == fmt)
+            return hw;
+    }
+    return NULL;
+}
+
+HWVoice *pcm_hw_add (int freq, int nchannels, audfmt_e fmt)
+{
+    HWVoice *hw;
+
+    if (audio_state.fixed_format) {
+        freq = audio_state.fixed_freq;
+        nchannels = audio_state.fixed_channels;
+        fmt = audio_state.fixed_fmt;
+    }
+
+    hw = pcm_hw_find_specific (NULL, freq, nchannels, fmt);
+
+    if (hw)
+        return hw;
+
+    hw = pcm_hw_find_any_passive (NULL);
+    if (hw) {
+        hw->pcm_ops = audio_state.drv->pcm_ops;
+        if (!hw->pcm_ops)
+            return NULL;
+
+        if (pcm_hw_init (hw, freq, nchannels, fmt)) {
+            pcm_hw_gc (hw);
+            return NULL;
+        }
+        else
+            return hw;
+    }
+
+    return pcm_hw_find_any (NULL);
+}
+
+int pcm_hw_add_sw (HWVoice *hw, SWVoice *sw)
+{
+    SWVoice **pvoice = qemu_mallocz ((hw->nb_voices + 1) * sizeof (sw));
+    if (!pvoice)
+        return -1;
+
+    memcpy (pvoice, hw->pvoice, hw->nb_voices * sizeof (sw));
+    qemu_free (hw->pvoice);
+    hw->pvoice = pvoice;
+    hw->pvoice[hw->nb_voices++] = sw;
+    return 0;
+}
+
+int pcm_hw_del_sw (HWVoice *hw, SWVoice *sw)
+{
+    int i, j;
+    if (hw->nb_voices > 1) {
+        SWVoice **pvoice = qemu_mallocz ((hw->nb_voices - 1) * sizeof (sw));
+
+        if (!pvoice) {
+            dolog ("Can not maintain consistent state (not enough memory)\n");
+            return -1;
+        }
+
+        for (i = 0, j = 0; i < hw->nb_voices; i++) {
+            if (j >= hw->nb_voices - 1) {
+                dolog ("Can not maintain consistent state "
+                       "(invariant violated)\n");
+                return -1;
+            }
+            if (hw->pvoice[i] != sw)
+                pvoice[j++] = hw->pvoice[i];
+        }
+        qemu_free (hw->pvoice);
+        hw->pvoice = pvoice;
+        hw->nb_voices -= 1;
+    }
+    else {
+        qemu_free (hw->pvoice);
+        hw->pvoice = NULL;
+        hw->nb_voices = 0;
+    }
+    return 0;
+}
+
+SWVoice *pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt)
+{
+    SWVoice *sw;
+    HWVoice *hw;
+
+    sw = qemu_mallocz (sizeof (*sw));
+    if (!sw)
+        goto err1;
+
+    hw = pcm_hw_add (freq, nchannels, fmt);
+    if (!hw)
+        goto err2;
+
+    if (pcm_hw_add_sw (hw, sw))
+        goto err3;
+
+    if (pcm_sw_init (sw, hw, freq, nchannels, fmt))
+        goto err4;
+
+    return sw;
+
+err4:
+    pcm_hw_del_sw (hw, sw);
+err3:
+    pcm_hw_gc (hw);
+err2:
+    qemu_free (sw);
+err1:
+    return NULL;
+}
+
+SWVoice *AUD_open (SWVoice *sw, const char *name,
+                   int freq, int nchannels, audfmt_e fmt)
+{
+    if (!audio_state.drv) {
+        return NULL;
+    }
+
+    if (sw && freq == sw->freq && sw->nchannels == nchannels && sw->fmt == 
fmt) {
+        return sw;
+    }
+
+    if (sw) {
+        ldebug ("Different format %s %d %d %d\n",
+                name,
+                sw->freq == freq,
+                sw->nchannels == nchannels,
+                sw->fmt == fmt);
+    }
+
+    if (nchannels != 1 && nchannels != 2) {
+        dolog ("Bogus channel count %d for voice %s\n", nchannels, name);
+        return NULL;
+    }
+
+    if (!audio_state.fixed_format && sw) {
+        pcm_sw_fini (sw);
+        pcm_hw_del_sw (sw->hw, sw);
+        pcm_hw_gc (sw->hw);
+        if (sw->name) {
+            qemu_free (sw->name);
+            sw->name = NULL;
+        }
+        qemu_free (sw);
+        sw = NULL;
+    }
+
+    if (sw) {
+        HWVoice *hw = sw->hw;
+        if (!hw) {
+            dolog ("Internal logic error voice %s has no hardware store\n",
+                   name);
+            return sw;
+        }
+
+        if (pcm_sw_init (sw, hw, freq, nchannels, fmt)) {
+            pcm_sw_fini (sw);
+            pcm_hw_del_sw (hw, sw);
+            pcm_hw_gc (hw);
+            if (sw->name) {
+                qemu_free (sw->name);
+                sw->name = NULL;
+            }
+            qemu_free (sw);
+            return NULL;
+        }
+    }
+    else {
+        sw = pcm_create_voice_pair (freq, nchannels, fmt);
+        if (!sw) {
+            dolog ("Failed to create voice %s\n", name);
+            return NULL;
+        }
+    }
+
+    if (sw->name) {
+        qemu_free (sw->name);
+        sw->name = NULL;
+    }
+    sw->name = qemu_strdup (name);
+    return sw;
+}
+
+void AUD_close (SWVoice *sw)
+{
+    if (!sw)
+        return;
+
+    pcm_sw_fini (sw);
+    pcm_hw_del_sw (sw->hw, sw);
+    pcm_hw_gc (sw->hw);
+    if (sw->name) {
+        qemu_free (sw->name);
+        sw->name = NULL;
+    }
+    qemu_free (sw);
+}
+
+int AUD_write (SWVoice *sw, void *buf, int size)
+{
+    int bytes;
+
+    if (!sw->hw->enabled)
+        dolog ("Writing to disabled voice %s\n", sw->name);
+    bytes = sw->hw->pcm_ops->write (sw, buf, size);
+    return bytes;
+}
+
+void AUD_run (void)
+{
+    HWVoice *hw = NULL;
+
+    while ((hw = pcm_hw_find_any_active_enabled (hw))) {
+        int i;
+        if (hw->pending_disable && pcm_hw_get_live (hw) <= 0) {
+            hw->enabled = 0;
+            hw->pcm_ops->ctl (hw, VOICE_DISABLE);
+            for (i = 0; i < hw->nb_voices; i++) {
+                hw->pvoice[i]->live = 0;
+                /* hw->pvoice[i]->old_ticks = 0; */
+            }
+            continue;
+        }
+
+        hw->pcm_ops->run (hw);
+        assert (hw->rpos < hw->samples);
+        for (i = 0; i < hw->nb_voices; i++) {
+            SWVoice *sw = hw->pvoice[i];
+            if (!sw->active && !sw->live && sw->old_ticks) {
+                int64_t delta = qemu_get_clock (vm_clock) - sw->old_ticks;
+                if (delta > audio_state.ticks_threshold) {
+                    ldebug ("resetting old_ticks for %s\n", sw->name);
+                    sw->old_ticks = 0;
+                }
+            }
+        }
+    }
+}
+
+int AUD_get_free (SWVoice *sw)
+{
+    int free;
+
+    if (!sw)
+        return 4096;
+
+    free = ((sw->hw->samples - sw->live) << sw->hw->shift) * sw->ratio
+        / INT_MAX;
+
+    free &= ~sw->hw->align;
+    if (!free) return 0;
+
+    return free;
+}
+
+int AUD_get_buffer_size (SWVoice *sw)
+{
+    return sw->hw->bufsize;
+}
+
+void AUD_adjust (SWVoice *sw, int bytes)
+{
+    if (!sw)
+        return;
+    sw->old_ticks += (ticks_per_sec * (int64_t) bytes) / sw->bytes_per_second;
+}
+
+void AUD_reset (SWVoice *sw)
+{
+    sw->active = 0;
+    sw->old_ticks = 0;
+}
+
+int AUD_calc_elapsed (SWVoice *sw)
+{
+    int64_t now, delta, bytes;
+    int dead, swlim;
+
+    if (!sw)
+        return 0;
+
+    now = qemu_get_clock (vm_clock);
+    delta = now - sw->old_ticks;
+    bytes = (delta * sw->bytes_per_second) / ticks_per_sec;
+    if (delta < 0) {
+        dolog ("whoops delta(<0)=%lld\n", delta);
+        return 0;
+    }
+
+    dead = sw->hw->samples - sw->live;
+    swlim = ((dead * (int64_t) INT_MAX) / sw->ratio);
+
+    if (bytes > swlim) {
+        return swlim;
+    }
+    else {
+        return bytes;
+    }
+}
+
+void AUD_enable (SWVoice *sw, int on)
+{
+    int i;
+    HWVoice *hw;
+
+    if (!sw)
+        return;
+
+    hw = sw->hw;
+    if (on) {
+        if (!sw->live)
+            sw->wpos = sw->hw->rpos;
+        if (!sw->old_ticks) {
+            sw->old_ticks = qemu_get_clock (vm_clock);
+        }
+    }
+
+    if (sw->active != on) {
+        if (on) {
+            hw->pending_disable = 0;
+            if (!hw->enabled) {
+                hw->enabled = 1;
+                for (i = 0; i < hw->nb_voices; i++) {
+                    ldebug ("resetting voice\n");
+                    sw = hw->pvoice[i];
+                    sw->old_ticks = qemu_get_clock (vm_clock);
+                }
+                hw->pcm_ops->ctl (hw, VOICE_ENABLE);
+            }
+        }
+        else {
+            if (hw->enabled && !hw->pending_disable) {
+                int nb_active = 0;
+                for (i = 0; i < hw->nb_voices; i++) {
+                    nb_active += hw->pvoice[i]->active != 0;
+                }
+
+                if (nb_active == 1) {
+                    hw->pending_disable = 1;
+                }
+            }
+        }
+        sw->active = on;
+    }
+}
+
+static struct audio_output_driver *drvtab[] = {
+#ifdef CONFIG_OSS
+    &oss_output_driver,
+#endif
+#ifdef CONFIG_FMOD
+    &fmod_output_driver,
+#endif
+#ifdef CONFIG_SDL
+    &sdl_output_driver,
+#endif
+    &no_output_driver,
+#ifdef USE_WAV_AUDIO
+    &wav_output_driver,
+#endif
+};
+
+static int voice_init (struct audio_output_driver *drv)
+{
+    audio_state.opaque = drv->init ();
+    if (audio_state.opaque) {
+        if (audio_state.nb_hw_voices > drv->max_voices) {
+            dolog ("`%s' does not support %d multiple hardware channels\n"
+                   "Resetting to %d\n",
+                   drv->name, audio_state.nb_hw_voices, drv->max_voices);
+            audio_state.nb_hw_voices = drv->max_voices;
+        }
+        hw_voices = qemu_mallocz (audio_state.nb_hw_voices * drv->voice_size);
+        if (hw_voices) {
+            audio_state.drv = drv;
+            return 1;
+        }
+        else {
+            dolog ("Not enough memory for %d `%s' voices (each %d bytes)\n",
+                   audio_state.nb_hw_voices, drv->name, drv->voice_size);
+            drv->fini (audio_state.opaque);
+            return 0;
+        }
+    }
+    else {
+        dolog ("Could not init `%s' audio\n", drv->name);
+        return 0;
+    }
+}
+
+static void audio_vm_stop_handler (void *opaque, int reason)
+{
+    HWVoice *hw = NULL;
+
+    while ((hw = pcm_hw_find_any (hw))) {
+        if (!hw->pcm_ops)
+            continue;
+
+        hw->pcm_ops->ctl (hw, reason ? VOICE_ENABLE : VOICE_DISABLE);
+    }
+}
+
+static void audio_atexit (void)
+{
+    HWVoice *hw = NULL;
+
+    while ((hw = pcm_hw_find_any (hw))) {
+        if (!hw->pcm_ops)
+            continue;
+
+        hw->pcm_ops->ctl (hw, VOICE_DISABLE);
+        hw->pcm_ops->fini (hw);
+    }
+    audio_state.drv->fini (audio_state.opaque);
+}
+
+static void audio_save (QEMUFile *f, void *opaque)
+{
+}
+
+static int audio_load (QEMUFile *f, void *opaque, int version_id)
+{
+    if (version_id != 1)
+        return -EINVAL;
+
+    return 0;
+}
+
+void AUD_init (void)
+{
+    int i;
+    int done = 0;
+    const char *drvname;
+
+    audio_state.fixed_format =
+        !!audio_get_conf_int (QC_FIXED_FORMAT, audio_state.fixed_format);
+    audio_state.fixed_freq =
+        audio_get_conf_int (QC_FIXED_FREQ, audio_state.fixed_freq);
+    audio_state.nb_hw_voices =
+        audio_get_conf_int (QC_VOICES, audio_state.nb_hw_voices);
+
+    if (audio_state.nb_hw_voices <= 0) {
+        dolog ("Bogus number of voices %d, resetting to 1\n",
+               audio_state.nb_hw_voices);
+    }
+
+    drvname = audio_get_conf_str (QC_AUDIO_DRV, NULL);
+    if (drvname) {
+        int found = 0;
+        for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+            if (!strcmp (drvname, drvtab[i]->name)) {
+                done = voice_init (drvtab[i]);
+                found = 1;
+                break;
+            }
+        }
+        if (!found) {
+            dolog ("Unknown audio driver `%s'\n", drvname);
+        }
+    }
+
+    qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL);
+    atexit (audio_atexit);
+
+    if (!done) {
+        for (i = 0; !done && i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
+            if (drvtab[i]->can_be_default)
+                done = voice_init (drvtab[i]);
+        }
+    }
+
+    audio_state.ticks_threshold = ticks_per_sec / 50;
+    audio_state.freq_threshold = 100;
+
+    register_savevm ("audio", 0, 1, audio_save, audio_load, NULL);
+    if (!done) {
+        dolog ("Can not initialize audio subsystem\n");
+        voice_init (&no_output_driver);
+    }
+}
diff -r 1283d309a603 tools/ioemu/audio/audio.h
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/audio.h Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,65 @@
+/*
+ * QEMU Audio subsystem header
+ * 
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ * 
+ * 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.
+ */
+#ifndef QEMU_AUDIO_H
+#define QEMU_AUDIO_H
+
+#include "mixeng.h"
+
+typedef enum {
+  AUD_FMT_U8,
+  AUD_FMT_S8,
+  AUD_FMT_U16,
+  AUD_FMT_S16
+} audfmt_e;
+
+typedef struct SWVoice SWVoice;
+
+SWVoice * AUD_open (SWVoice *sw, const char *name, int freq,
+                    int nchannels, audfmt_e fmt);
+void   AUD_init (void);
+void   AUD_log (const char *cap, const char *fmt, ...)
+    __attribute__ ((__format__ (__printf__, 2, 3)));;
+void   AUD_close (SWVoice *sw);
+int    AUD_write (SWVoice *sw, void *pcm_buf, int size);
+void   AUD_adjust (SWVoice *sw, int leftover);
+void   AUD_reset (SWVoice *sw);
+int    AUD_get_free (SWVoice *sw);
+int    AUD_get_buffer_size (SWVoice *sw);
+void   AUD_run (void);
+void   AUD_enable (SWVoice *sw, int on);
+int    AUD_calc_elapsed (SWVoice *sw);
+
+static inline void *advance (void *p, int incr)
+{
+    uint8_t *d = p;
+    return (d + incr);
+}
+
+uint32_t popcount (uint32_t u);
+inline uint32_t lsbindex (uint32_t u);
+
+#define audio_MIN(a, b) ((a)>(b)?(b):(a))
+#define audio_MAX(a, b) ((a)<(b)?(b):(a))
+
+#endif  /* audio.h */
diff -r 1283d309a603 tools/ioemu/audio/audio_int.h
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/audio_int.h     Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,164 @@
+/*
+ * QEMU Audio subsystem header
+ * 
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ * 
+ * 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.
+ */
+#ifndef QEMU_AUDIO_INT_H
+#define QEMU_AUDIO_INT_H
+
+#include "vl.h"
+
+struct pcm_ops;
+
+typedef struct HWVoice {
+    int active;
+    int enabled;
+    int pending_disable;
+    int valid;
+    int freq;
+
+    f_sample *clip;
+    audfmt_e fmt;
+    int nchannels;
+
+    int align;
+    int shift;
+
+    int rpos;
+    int bufsize;
+
+    int bytes_per_second;
+    st_sample_t *mix_buf;
+
+    int samples;
+    int64_t old_ticks;
+    int nb_voices;
+    struct SWVoice **pvoice;
+    struct pcm_ops *pcm_ops;
+} HWVoice;
+
+extern struct pcm_ops no_pcm_ops;
+extern struct audio_output_driver no_output_driver;
+
+extern struct pcm_ops oss_pcm_ops;
+extern struct audio_output_driver oss_output_driver;
+
+extern struct pcm_ops sdl_pcm_ops;
+extern struct audio_output_driver sdl_output_driver;
+
+extern struct pcm_ops wav_pcm_ops;
+extern struct audio_output_driver wav_output_driver;
+
+extern struct pcm_ops fmod_pcm_ops;
+extern struct audio_output_driver fmod_output_driver;
+
+struct audio_output_driver {
+    const char *name;
+    void *(*init) (void);
+    void (*fini) (void *);
+    struct pcm_ops *pcm_ops;
+    int can_be_default;
+    int max_voices;
+    int voice_size;
+};
+
+typedef struct AudioState {
+    int fixed_format;
+    int fixed_freq;
+    int fixed_channels;
+    int fixed_fmt;
+    int nb_hw_voices;
+    int voice_size;
+    int64_t ticks_threshold;
+    int freq_threshold;
+    void *opaque;
+    struct audio_output_driver *drv;
+} AudioState;
+extern AudioState audio_state;
+
+struct SWVoice {
+    int freq;
+    audfmt_e fmt;
+    int nchannels;
+
+    int shift;
+    int align;
+
+    t_sample *conv;
+
+    int left;
+    int pos;
+    int bytes_per_second;
+    int64_t ratio;
+    st_sample_t *buf;
+    void *rate;
+
+    int wpos;
+    int live;
+    int active;
+    int64_t old_ticks;
+    HWVoice *hw;
+    char *name;
+};
+
+struct pcm_ops {
+    int  (*init)  (HWVoice *hw, int freq, int nchannels, audfmt_e fmt);
+    void (*fini)  (HWVoice *hw);
+    void (*run)   (HWVoice *hw);
+    int  (*write) (SWVoice *sw, void *buf, int size);
+    int  (*ctl)   (HWVoice *hw, int cmd, ...);
+};
+
+void      pcm_sw_free_resources (SWVoice *sw);
+int       pcm_sw_alloc_resources (SWVoice *sw);
+void      pcm_sw_fini (SWVoice *sw);
+int       pcm_sw_init (SWVoice *sw, HWVoice *hw, int freq,
+                       int nchannels, audfmt_e fmt);
+
+void      pcm_hw_clear (HWVoice *hw, void *buf, int len);
+HWVoice * pcm_hw_find_any (HWVoice *hw);
+HWVoice * pcm_hw_find_any_active (HWVoice *hw);
+HWVoice * pcm_hw_find_any_passive (HWVoice *hw);
+HWVoice * pcm_hw_find_specific (HWVoice *hw, int freq,
+                                int nchannels, audfmt_e fmt);
+HWVoice * pcm_hw_add (int freq, int nchannels, audfmt_e fmt);
+int       pcm_hw_add_sw (HWVoice *hw, SWVoice *sw);
+int       pcm_hw_del_sw (HWVoice *hw, SWVoice *sw);
+SWVoice * pcm_create_voice_pair (int freq, int nchannels, audfmt_e fmt);
+
+void      pcm_hw_free_resources (HWVoice *hw);
+int       pcm_hw_alloc_resources (HWVoice *hw);
+void      pcm_hw_fini (HWVoice *hw);
+void      pcm_hw_gc (HWVoice *hw);
+int       pcm_hw_get_live (HWVoice *hw);
+int       pcm_hw_get_live2 (HWVoice *hw, int *nb_active);
+void      pcm_hw_dec_live (HWVoice *hw, int decr);
+int       pcm_hw_write (SWVoice *sw, void *buf, int len);
+
+int         audio_get_conf_int (const char *key, int defval);
+const char *audio_get_conf_str (const char *key, const char *defval);
+
+struct audio_output_driver;
+
+#define VOICE_ENABLE 1
+#define VOICE_DISABLE 2
+
+#endif /* audio_int.h */
diff -r 1283d309a603 tools/ioemu/audio/mixeng.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/mixeng.c        Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,255 @@
+/*
+ * QEMU Mixing engine
+ *
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * Copyright (c) 1998 Fabrice Bellard
+ *
+ * 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 "vl.h"
+//#define DEBUG_FP
+#include "audio/mixeng.h"
+
+#define IN_T int8_t
+#define IN_MIN CHAR_MIN
+#define IN_MAX CHAR_MAX
+#define SIGNED
+#include "mixeng_template.h"
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+
+#define IN_T uint8_t
+#define IN_MIN 0
+#define IN_MAX UCHAR_MAX
+#include "mixeng_template.h"
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+
+#define IN_T int16_t
+#define IN_MIN SHRT_MIN
+#define IN_MAX SHRT_MAX
+#define SIGNED
+#include "mixeng_template.h"
+#undef SIGNED
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+
+#define IN_T uint16_t
+#define IN_MIN 0
+#define IN_MAX USHRT_MAX
+#include "mixeng_template.h"
+#undef IN_MAX
+#undef IN_MIN
+#undef IN_T
+
+t_sample *mixeng_conv[2][2][2] = {
+    {
+        {
+            conv_uint8_t_to_mono,
+            conv_uint16_t_to_mono
+        },
+        {
+            conv_int8_t_to_mono,
+            conv_int16_t_to_mono
+        }
+    },
+    {
+        {
+            conv_uint8_t_to_stereo,
+            conv_uint16_t_to_stereo
+        },
+        {
+            conv_int8_t_to_stereo,
+            conv_int16_t_to_stereo
+        }
+    }
+};
+
+f_sample *mixeng_clip[2][2][2] = {
+    {
+        {
+            clip_uint8_t_from_mono,
+            clip_uint16_t_from_mono
+        },
+        {
+            clip_int8_t_from_mono,
+            clip_int16_t_from_mono
+        }
+    },
+    {
+        {
+            clip_uint8_t_from_stereo,
+            clip_uint16_t_from_stereo
+        },
+        {
+            clip_int8_t_from_stereo,
+            clip_int16_t_from_stereo
+        }
+    }
+};
+
+/*
+ * August 21, 1998
+ * Copyright 1998 Fabrice Bellard.
+ *
+ * [Rewrote completly the code of Lance Norskog And Sundry
+ * Contributors with a more efficient algorithm.]
+ *
+ * This source code is freely redistributable and may be used for
+ * any purpose.  This copyright notice must be maintained. 
+ * Lance Norskog And Sundry Contributors are not responsible for 
+ * the consequences of using this software.  
+ */
+
+/*
+ * Sound Tools rate change effect file.
+ */
+/*
+ * Linear Interpolation.
+ *
+ * The use of fractional increment allows us to use no buffer. It
+ * avoid the problems at the end of the buffer we had with the old
+ * method which stored a possibly big buffer of size
+ * lcm(in_rate,out_rate).
+ *
+ * Limited to 16 bit samples and sampling frequency <= 65535 Hz. If
+ * the input & output frequencies are equal, a delay of one sample is
+ * introduced.  Limited to processing 32-bit count worth of samples.
+ *
+ * 1 << FRAC_BITS evaluating to zero in several places.  Changed with
+ * an (unsigned long) cast to make it safe.  MarkMLl 2/1/99
+ */
+
+/* Private data */
+typedef struct ratestuff {
+    uint64_t opos;
+    uint64_t opos_inc;
+    uint32_t ipos;              /* position in the input stream (integer) */
+    st_sample_t ilast;          /* last sample in the input stream */
+} *rate_t;
+
+/*
+ * Prepare processing.
+ */
+void *st_rate_start (int inrate, int outrate)
+{
+    rate_t rate = (rate_t) qemu_mallocz (sizeof (struct ratestuff));
+
+    if (!rate) {
+        exit (EXIT_FAILURE);
+    }
+
+    if (inrate == outrate) {
+        // exit (EXIT_FAILURE);
+    }
+
+    if (inrate >= 65535 || outrate >= 65535) {
+        // exit (EXIT_FAILURE);
+    }
+
+    rate->opos = 0;
+
+    /* increment */
+    rate->opos_inc = (inrate * ((int64_t) UINT_MAX)) / outrate;
+
+    rate->ipos = 0;
+    rate->ilast.l = 0;
+    rate->ilast.r = 0;
+    return rate;
+}
+
+/*
+ * Processed signed long samples from ibuf to obuf.
+ * Return number of samples processed.
+ */
+void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+                   int *isamp, int *osamp)
+{
+    rate_t rate = (rate_t) opaque;
+    st_sample_t *istart, *iend;
+    st_sample_t *ostart, *oend;
+    st_sample_t ilast, icur, out;
+    int64_t t;
+
+    ilast = rate->ilast;
+
+    istart = ibuf;
+    iend = ibuf + *isamp;
+
+    ostart = obuf;
+    oend = obuf + *osamp;
+
+    if (rate->opos_inc == 1ULL << 32) {
+        int i, n = *isamp > *osamp ? *osamp : *isamp;
+        for (i = 0; i < n; i++) {
+            obuf[i].l += ibuf[i].r;
+            obuf[i].r += ibuf[i].r;
+        }
+        *isamp = n;
+        *osamp = n;
+        return;
+    }
+
+    while (obuf < oend) {
+
+        /* Safety catch to make sure we have input samples.  */
+        if (ibuf >= iend)
+            break;
+
+        /* read as many input samples so that ipos > opos */
+
+        while (rate->ipos <= (rate->opos >> 32)) {
+            ilast = *ibuf++;
+            rate->ipos++;
+            /* See if we finished the input buffer yet */
+            if (ibuf >= iend) goto the_end;
+        }
+
+        icur = *ibuf;
+
+        /* interpolate */
+        t = rate->opos & 0xffffffff;
+        out.l = (ilast.l * (INT_MAX - t) + icur.l * t) / INT_MAX;
+        out.r = (ilast.r * (INT_MAX - t) + icur.r * t) / INT_MAX;
+
+        /* output sample & increment position */
+#if 0
+        *obuf++ = out;
+#else
+        obuf->l += out.l;
+        obuf->r += out.r;
+        obuf += 1;
+#endif
+        rate->opos += rate->opos_inc;
+    }
+
+the_end:
+    *isamp = ibuf - istart;
+    *osamp = obuf - ostart;
+    rate->ilast = ilast;
+}
+
+void st_rate_stop (void *opaque)
+{
+    qemu_free (opaque);
+}
diff -r 1283d309a603 tools/ioemu/audio/mixeng.h
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/mixeng.h        Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,39 @@
+/*
+ * QEMU Mixing engine header
+ * 
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * 
+ * 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.
+ */
+#ifndef QEMU_MIXENG_H
+#define QEMU_MIXENG_H
+
+typedef void (t_sample) (void *dst, const void *src, int samples);
+typedef void (f_sample) (void *dst, const void *src, int samples);
+typedef struct { int64_t l; int64_t r; } st_sample_t;
+
+extern t_sample *mixeng_conv[2][2][2];
+extern f_sample *mixeng_clip[2][2][2];
+
+void *st_rate_start (int inrate, int outrate);
+void st_rate_flow (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
+                   int *isamp, int *osamp);
+void st_rate_stop (void *opaque);
+
+#endif  /* mixeng.h */
diff -r 1283d309a603 tools/ioemu/audio/mixeng_template.h
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/mixeng_template.h       Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,111 @@
+/*
+ * QEMU Mixing engine
+ * 
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * 
+ * 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.
+ */
+
+/*
+ * Tusen tack till Mike Nordell
+ * dec++'ified by Dscho
+ */
+
+#ifdef SIGNED
+#define HALFT IN_MAX
+#define HALF IN_MAX
+#else
+#define HALFT ((IN_MAX)>>1)
+#define HALF HALFT
+#endif
+
+static int64_t inline glue(conv_,IN_T) (IN_T v)
+{
+#ifdef SIGNED
+    return (INT_MAX*(int64_t)v)/HALF;
+#else
+    return (INT_MAX*((int64_t)v-HALFT))/HALF;
+#endif
+}
+
+static IN_T inline glue(clip_,IN_T) (int64_t v)
+{
+    if (v >= INT_MAX)
+        return IN_MAX;
+    else if (v < -INT_MAX)
+        return IN_MIN;
+
+#ifdef SIGNED
+    return (IN_T) (v*HALF/INT_MAX);
+#else
+    return (IN_T) (v+INT_MAX/2)*HALF/INT_MAX;
+#endif
+}
+
+static void glue(glue(conv_,IN_T),_to_stereo) (void *dst, const void *src,
+                                               int samples)
+{
+    st_sample_t *out = (st_sample_t *) dst;
+    IN_T *in = (IN_T *) src;
+    while (samples--) {
+        out->l = glue(conv_,IN_T) (*in++);
+        out->r = glue(conv_,IN_T) (*in++);
+        out += 1;
+    }
+}
+
+static void glue(glue(conv_,IN_T),_to_mono) (void *dst, const void *src,
+                                             int samples)
+{
+    st_sample_t *out = (st_sample_t *) dst;
+    IN_T *in = (IN_T *) src;
+    while (samples--) {
+        out->l = glue(conv_,IN_T) (in[0]);
+        out->r = out->l;
+        out += 1;
+        in += 1;
+    }
+}
+
+static void glue(glue(clip_,IN_T),_from_stereo) (void *dst, const void *src,
+                                                 int samples)
+{
+    st_sample_t *in = (st_sample_t *) src;
+    IN_T *out = (IN_T *) dst;
+    while (samples--) {
+        *out++ = glue(clip_,IN_T) (in->l);
+        *out++ = glue(clip_,IN_T) (in->r);
+        in += 1;
+    }
+}
+
+static void glue(glue(clip_,IN_T),_from_mono) (void *dst, const void *src,
+                                               int samples)
+{
+    st_sample_t *in = (st_sample_t *) src;
+    IN_T *out = (IN_T *) dst;
+    while (samples--) {
+        *out++ = glue(clip_,IN_T) (in->l + in->r);
+        in += 1;
+    }
+}
+
+#undef HALF
+#undef HALFT
+
diff -r 1283d309a603 tools/ioemu/audio/noaudio.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/noaudio.c       Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,130 @@
+/*
+ * QEMU NULL audio output driver
+ * 
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * 
+ * 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 "vl.h"
+
+#include "audio/audio_int.h"
+
+typedef struct NoVoice {
+    HWVoice hw;
+    int64_t old_ticks;
+} NoVoice;
+
+#define dolog(...) AUD_log ("noaudio", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+static void no_hw_run (HWVoice *hw)
+{
+    NoVoice *no = (NoVoice *) hw;
+    int rpos, live, decr, samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    int64_t now = qemu_get_clock (vm_clock);
+    int64_t ticks = now - no->old_ticks;
+    int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
+
+    if (bytes > INT_MAX)
+        samples = INT_MAX >> hw->shift;
+    else
+        samples = bytes >> hw->shift;
+
+    live = pcm_hw_get_live (hw);
+    if (live <= 0)
+        return;
+
+    no->old_ticks = now;
+    decr = audio_MIN (live, samples);
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+        src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
+        memset (src, 0, convert_samples * sizeof (st_sample_t));
+
+        rpos = (rpos + convert_samples) % hw->samples;
+        samples -= convert_samples;
+    }
+
+    pcm_hw_dec_live (hw, decr);
+    hw->rpos = rpos;
+}
+
+static int no_hw_write (SWVoice *sw, void *buf, int len)
+{
+    return pcm_hw_write (sw, buf, len);
+}
+
+static int no_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
+{
+    NoVoice *no = (NoVoice *) hw;
+    hw->freq = freq;
+    hw->nchannels = nchannels;
+    hw->fmt = fmt;
+    hw->bufsize = 4096;
+    return 0;
+}
+
+static void no_hw_fini (HWVoice *hw)
+{
+    (void) hw;
+}
+
+static int no_hw_ctl (HWVoice *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+static void *no_audio_init (void)
+{
+    return &no_audio_init;
+}
+
+static void no_audio_fini (void *opaque)
+{
+}
+
+struct pcm_ops no_pcm_ops = {
+    no_hw_init,
+    no_hw_fini,
+    no_hw_run,
+    no_hw_write,
+    no_hw_ctl
+};
+
+struct audio_output_driver no_output_driver = {
+    "none",
+    no_audio_init,
+    no_audio_fini,
+    &no_pcm_ops,
+    1,
+    1,
+    sizeof (NoVoice)
+};
diff -r 1283d309a603 tools/ioemu/audio/ossaudio.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/ossaudio.c      Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,475 @@
+/*
+ * QEMU OSS audio output driver
+ * 
+ * Copyright (c) 2003-2004 Vassili Karpov (malc)
+ * 
+ * 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 <sys/mman.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/soundcard.h>
+#include <assert.h>
+#include "vl.h"
+
+#include "audio/audio_int.h"
+
+typedef struct OSSVoice {
+    HWVoice hw;
+    void *pcm_buf;
+    int fd;
+    int nfrags;
+    int fragsize;
+    int mmapped;
+    int old_optr;
+} OSSVoice;
+
+#define dolog(...) AUD_log ("oss", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define QC_OSS_FRAGSIZE "QEMU_OSS_FRAGSIZE"
+#define QC_OSS_NFRAGS   "QEMU_OSS_NFRAGS"
+#define QC_OSS_MMAP     "QEMU_OSS_MMAP"
+#define QC_OSS_DEV      "QEMU_OSS_DEV"
+
+#define errstr() strerror (errno)
+
+static struct {
+    int try_mmap;
+    int nfrags;
+    int fragsize;
+    const char *dspname;
+} conf = {
+    .try_mmap = 0,
+    .nfrags = 4,
+    .fragsize = 4096,
+    .dspname = "/dev/dsp"
+};
+
+struct oss_params {
+    int freq;
+    audfmt_e fmt;
+    int nchannels;
+    int nfrags;
+    int fragsize;
+};
+
+static int oss_hw_write (SWVoice *sw, void *buf, int len)
+{
+    return pcm_hw_write (sw, buf, len);
+}
+
+static int AUD_to_ossfmt (audfmt_e fmt)
+{
+    switch (fmt) {
+    case AUD_FMT_S8: return AFMT_S8;
+    case AUD_FMT_U8: return AFMT_U8;
+    case AUD_FMT_S16: return AFMT_S16_LE;
+    case AUD_FMT_U16: return AFMT_U16_LE;
+    default:
+        dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
+        exit (EXIT_FAILURE);
+    }
+}
+
+static int oss_to_audfmt (int fmt)
+{
+    switch (fmt) {
+    case AFMT_S8: return AUD_FMT_S8;
+    case AFMT_U8: return AUD_FMT_U8;
+    case AFMT_S16_LE: return AUD_FMT_S16;
+    case AFMT_U16_LE: return AUD_FMT_U16;
+    default:
+        dolog ("Internal logic error: Unrecognized OSS audio format %d\n"
+               "Aborting\n",
+               fmt);
+        exit (EXIT_FAILURE);
+    }
+}
+
+#ifdef DEBUG_PCM
+static void oss_dump_pcm_info (struct oss_params *req, struct oss_params *obt)
+{
+    dolog ("parameter | requested value | obtained value\n");
+    dolog ("format    |      %10d |     %10d\n", req->fmt, obt->fmt);
+    dolog ("channels  |      %10d |     %10d\n", req->nchannels, 
obt->nchannels);
+    dolog ("frequency |      %10d |     %10d\n", req->freq, obt->freq);
+    dolog ("nfrags    |      %10d |     %10d\n", req->nfrags, obt->nfrags);
+    dolog ("fragsize  |      %10d |     %10d\n", req->fragsize, obt->fragsize);
+}
+#endif
+
+static int oss_open (struct oss_params *req, struct oss_params *obt, int *pfd)
+{
+    int fd;
+    int mmmmssss;
+    audio_buf_info abinfo;
+    int fmt, freq, nchannels;
+    const char *dspname = conf.dspname;
+
+    fd = open (dspname, O_RDWR | O_NONBLOCK);
+    if (-1 == fd) {
+        dolog ("Could not initialize audio hardware. Failed to open `%s':\n"
+               "Reason:%s\n",
+               dspname,
+               errstr ());
+        return -1;
+    }
+
+    freq = req->freq;
+    nchannels = req->nchannels;
+    fmt = req->fmt;
+
+    if (ioctl (fd, SNDCTL_DSP_SAMPLESIZE, &fmt)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to set sample size\n"
+               "Reason: %s\n",
+               errstr ());
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_CHANNELS, &nchannels)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to set number of channels\n"
+               "Reason: %s\n",
+               errstr ());
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_SPEED, &freq)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to set frequency\n"
+               "Reason: %s\n",
+               errstr ());
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_NONBLOCK)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to set non-blocking mode\n"
+               "Reason: %s\n",
+               errstr ());
+        goto err;
+    }
+
+    mmmmssss = (req->nfrags << 16) | lsbindex (req->fragsize);
+    if (ioctl (fd, SNDCTL_DSP_SETFRAGMENT, &mmmmssss)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to set buffer length (%d, %d)\n"
+               "Reason:%s\n",
+               conf.nfrags, conf.fragsize,
+               errstr ());
+        goto err;
+    }
+
+    if (ioctl (fd, SNDCTL_DSP_GETOSPACE, &abinfo)) {
+        dolog ("Could not initialize audio hardware\n"
+               "Failed to get buffer length\n"
+               "Reason:%s\n",
+               errstr ());
+        goto err;
+    }
+
+    obt->fmt = fmt;
+    obt->nchannels = nchannels;
+    obt->freq = freq;
+    obt->nfrags = abinfo.fragstotal;
+    obt->fragsize = abinfo.fragsize;
+    *pfd = fd;
+
+    if ((req->fmt != obt->fmt) ||
+        (req->nchannels != obt->nchannels) ||
+        (req->freq != obt->freq) ||
+        (req->fragsize != obt->fragsize) ||
+        (req->nfrags != obt->nfrags)) {
+#ifdef DEBUG_PCM
+        dolog ("Audio parameters mismatch\n");
+        oss_dump_pcm_info (req, obt);
+#endif
+    }
+
+#ifdef DEBUG_PCM
+    oss_dump_pcm_info (req, obt);
+#endif
+    return 0;
+
+err:
+    close (fd);
+    return -1;
+}
+
+static void oss_hw_run (HWVoice *hw)
+{
+    OSSVoice *oss = (OSSVoice *) hw;
+    int err, rpos, live, decr;
+    int samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    struct audio_buf_info abinfo;
+    struct count_info cntinfo;
+
+    live = pcm_hw_get_live (hw);
+    if (live <= 0)
+        return;
+
+    if (oss->mmapped) {
+        int bytes;
+
+        err = ioctl (oss->fd, SNDCTL_DSP_GETOPTR, &cntinfo);
+        if (err < 0) {
+            dolog ("SNDCTL_DSP_GETOPTR failed\nReason: %s\n", errstr ());
+            return;
+        }
+
+        if (cntinfo.ptr == oss->old_optr) {
+            if (abs (hw->samples - live) < 64)
+                dolog ("overrun\n");
+            return;
+        }
+
+        if (cntinfo.ptr > oss->old_optr) {
+            bytes = cntinfo.ptr - oss->old_optr;
+        }
+        else {
+            bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
+        }
+
+        decr = audio_MIN (bytes >> hw->shift, live);
+    }
+    else {
+        err = ioctl (oss->fd, SNDCTL_DSP_GETOSPACE, &abinfo);
+        if (err < 0) {
+            dolog ("SNDCTL_DSP_GETOSPACE failed\nReason: %s\n", errstr ());
+            return;
+        }
+
+        decr = audio_MIN (abinfo.bytes >> hw->shift, live);
+        if (decr <= 0)
+            return;
+    }
+
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+        src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
+        dst = advance (oss->pcm_buf, rpos << hw->shift);
+
+        hw->clip (dst, src, convert_samples);
+        if (!oss->mmapped) {
+            int written;
+
+            written = write (oss->fd, dst, convert_samples << hw->shift);
+            /* XXX: follow errno recommendations ? */
+            if (written == -1) {
+                dolog ("Failed to write audio\nReason: %s\n", errstr ());
+                continue;
+            }
+
+            if (written != convert_samples << hw->shift) {
+                int wsamples = written >> hw->shift;
+                int wbytes = wsamples << hw->shift;
+                if (wbytes != written) {
+                    dolog ("Unaligned write %d, %d\n", wbytes, written);
+                }
+                memset (src, 0, wbytes);
+                decr -= samples;
+                rpos = (rpos + wsamples) % hw->samples;
+                break;
+            }
+        }
+        memset (src, 0, convert_samples * sizeof (st_sample_t));
+
+        rpos = (rpos + convert_samples) % hw->samples;
+        samples -= convert_samples;
+    }
+    if (oss->mmapped) {
+        oss->old_optr = cntinfo.ptr;
+    }
+
+    pcm_hw_dec_live (hw, decr);
+    hw->rpos = rpos;
+}
+
+static void oss_hw_fini (HWVoice *hw)
+{
+    int err;
+    OSSVoice *oss = (OSSVoice *) hw;
+
+    ldebug ("oss_hw_fini\n");
+    err = close (oss->fd);
+    if (err) {
+        dolog ("Failed to close OSS descriptor\nReason: %s\n", errstr ());
+    }
+    oss->fd = -1;
+
+    if (oss->pcm_buf) {
+        if (oss->mmapped) {
+            err = munmap (oss->pcm_buf, hw->bufsize);
+            if (err) {
+                dolog ("Failed to unmap OSS buffer\nReason: %s\n",
+                       errstr ());
+            }
+        }
+        else {
+            qemu_free (oss->pcm_buf);
+        }
+        oss->pcm_buf = NULL;
+    }
+}
+
+static int oss_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
+{
+    OSSVoice *oss = (OSSVoice *) hw;
+    struct oss_params req, obt;
+
+    assert (!oss->fd);
+    req.fmt = AUD_to_ossfmt (fmt);
+    req.freq = freq;
+    req.nchannels = nchannels;
+    req.fragsize = conf.fragsize;
+    req.nfrags = conf.nfrags;
+
+    if (oss_open (&req, &obt, &oss->fd))
+        return -1;
+
+    hw->freq = obt.freq;
+    hw->fmt = oss_to_audfmt (obt.fmt);
+    hw->nchannels = obt.nchannels;
+
+    oss->nfrags = obt.nfrags;
+    oss->fragsize = obt.fragsize;
+    hw->bufsize = obt.nfrags * obt.fragsize;
+
+    oss->mmapped = 0;
+    if (conf.try_mmap) {
+        oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
+                             MAP_SHARED, oss->fd, 0);
+        if (oss->pcm_buf == MAP_FAILED) {
+            dolog ("Failed to mmap OSS device\nReason: %s\n",
+                   errstr ());
+        } else {
+            int err;
+            int trig = 0;
+            if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+                dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
+                       errstr ());
+            }
+            else {
+                trig = PCM_ENABLE_OUTPUT;
+                if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+                    dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+                           "Reason: %s\n", errstr ());
+                }
+                else {
+                    oss->mmapped = 1;
+                }
+            }
+
+            if (!oss->mmapped) {
+                err = munmap (oss->pcm_buf, hw->bufsize);
+                if (err) {
+                    dolog ("Failed to unmap OSS device\nReason: %s\n",
+                           errstr ());
+                }
+            }
+        }
+    }
+
+    if (!oss->mmapped) {
+        oss->pcm_buf = qemu_mallocz (hw->bufsize);
+        if (!oss->pcm_buf) {
+            close (oss->fd);
+            oss->fd = -1;
+            return -1;
+        }
+    }
+
+    return 0;
+}
+
+static int oss_hw_ctl (HWVoice *hw, int cmd, ...)
+{
+    int trig;
+    OSSVoice *oss = (OSSVoice *) hw;
+
+    if (!oss->mmapped)
+        return 0;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        ldebug ("enabling voice\n");
+        pcm_hw_clear (hw, oss->pcm_buf, hw->samples);
+        trig = PCM_ENABLE_OUTPUT;
+        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+            dolog ("SNDCTL_DSP_SETTRIGGER PCM_ENABLE_OUTPUT failed\n"
+                   "Reason: %s\n", errstr ());
+            return -1;
+        }
+        break;
+
+    case VOICE_DISABLE:
+        ldebug ("disabling voice\n");
+        trig = 0;
+        if (ioctl (oss->fd, SNDCTL_DSP_SETTRIGGER, &trig) < 0) {
+            dolog ("SNDCTL_DSP_SETTRIGGER 0 failed\nReason: %s\n",
+                   errstr ());
+            return -1;
+        }
+        break;
+    }
+    return 0;
+}
+
+static void *oss_audio_init (void)
+{
+    conf.fragsize = audio_get_conf_int (QC_OSS_FRAGSIZE, conf.fragsize);
+    conf.nfrags = audio_get_conf_int (QC_OSS_NFRAGS, conf.nfrags);
+    conf.try_mmap = audio_get_conf_int (QC_OSS_MMAP, conf.try_mmap);
+    conf.dspname = audio_get_conf_str (QC_OSS_DEV, conf.dspname);
+    return &conf;
+}
+
+static void oss_audio_fini (void *opaque)
+{
+}
+
+struct pcm_ops oss_pcm_ops = {
+    oss_hw_init,
+    oss_hw_fini,
+    oss_hw_run,
+    oss_hw_write,
+    oss_hw_ctl
+};
+
+struct audio_output_driver oss_output_driver = {
+    "oss",
+    oss_audio_init,
+    oss_audio_fini,
+    &oss_pcm_ops,
+    1,
+    INT_MAX,
+    sizeof (OSSVoice)
+};
diff -r 1283d309a603 tools/ioemu/audio/sdlaudio.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/sdlaudio.c      Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,332 @@
+/*
+ * QEMU SDL audio output driver
+ * 
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * 
+ * 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 <SDL.h>
+#include <SDL_thread.h>
+#include "vl.h"
+
+#include "audio/audio_int.h"
+
+typedef struct SDLVoice {
+    HWVoice hw;
+} SDLVoice;
+
+#define dolog(...) AUD_log ("sdl", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+#define QC_SDL_SAMPLES "QEMU_SDL_SAMPLES"
+
+#define errstr() SDL_GetError ()
+
+static struct {
+    int nb_samples;
+} conf = {
+    1024
+};
+
+struct SDLAudioState {
+    int exit;
+    SDL_mutex *mutex;
+    SDL_sem *sem;
+    int initialized;
+} glob_sdl;
+typedef struct SDLAudioState SDLAudioState;
+
+static void sdl_hw_run (HWVoice *hw)
+{
+    (void) hw;
+}
+
+static int sdl_lock (SDLAudioState *s)
+{
+    if (SDL_LockMutex (s->mutex)) {
+        dolog ("SDL_LockMutex failed\nReason: %s\n", errstr ());
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_unlock (SDLAudioState *s)
+{
+    if (SDL_UnlockMutex (s->mutex)) {
+        dolog ("SDL_UnlockMutex failed\nReason: %s\n", errstr ());
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_post (SDLAudioState *s)
+{
+    if (SDL_SemPost (s->sem)) {
+        dolog ("SDL_SemPost failed\nReason: %s\n", errstr ());
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_wait (SDLAudioState *s)
+{
+    if (SDL_SemWait (s->sem)) {
+        dolog ("SDL_SemWait failed\nReason: %s\n", errstr ());
+        return -1;
+    }
+    return 0;
+}
+
+static int sdl_unlock_and_post (SDLAudioState *s)
+{
+    if (sdl_unlock (s))
+        return -1;
+
+    return sdl_post (s);
+}
+
+static int sdl_hw_write (SWVoice *sw, void *buf, int len)
+{
+    int ret;
+    SDLAudioState *s = &glob_sdl;
+    sdl_lock (s);
+    ret = pcm_hw_write (sw, buf, len);
+    sdl_unlock_and_post (s);
+    return ret;
+}
+
+static int AUD_to_sdlfmt (audfmt_e fmt, int *shift)
+{
+    *shift = 0;
+    switch (fmt) {
+    case AUD_FMT_S8: return AUDIO_S8;
+    case AUD_FMT_U8: return AUDIO_U8;
+    case AUD_FMT_S16: *shift = 1; return AUDIO_S16LSB;
+    case AUD_FMT_U16: *shift = 1; return AUDIO_U16LSB;
+    default:
+        dolog ("Internal logic error: Bad audio format %d\nAborting\n", fmt);
+        exit (EXIT_FAILURE);
+    }
+}
+
+static int sdl_to_audfmt (int fmt)
+{
+    switch (fmt) {
+    case AUDIO_S8: return AUD_FMT_S8;
+    case AUDIO_U8: return AUD_FMT_U8;
+    case AUDIO_S16LSB: return AUD_FMT_S16;
+    case AUDIO_U16LSB: return AUD_FMT_U16;
+    default:
+        dolog ("Internal logic error: Unrecognized SDL audio format %d\n"
+               "Aborting\n", fmt);
+        exit (EXIT_FAILURE);
+    }
+}
+
+static int sdl_open (SDL_AudioSpec *req, SDL_AudioSpec *obt)
+{
+    int status;
+
+    status = SDL_OpenAudio (req, obt);
+    if (status) {
+        dolog ("SDL_OpenAudio failed\nReason: %s\n", errstr ());
+    }
+    return status;
+}
+
+static void sdl_close (SDLAudioState *s)
+{
+    if (s->initialized) {
+        sdl_lock (s);
+        s->exit = 1;
+        sdl_unlock_and_post (s);
+        SDL_PauseAudio (1);
+        SDL_CloseAudio ();
+        s->initialized = 0;
+    }
+}
+
+static void sdl_callback (void *opaque, Uint8 *buf, int len)
+{
+    SDLVoice *sdl = opaque;
+    SDLAudioState *s = &glob_sdl;
+    HWVoice *hw = &sdl->hw;
+    int samples = len >> hw->shift;
+
+    if (s->exit) {
+        return;
+    }
+
+    while (samples) {
+        int to_mix, live, decr;
+
+        /* dolog ("in callback samples=%d\n", samples); */
+        sdl_wait (s);
+        if (s->exit) {
+            return;
+        }
+
+        sdl_lock (s);
+        live = pcm_hw_get_live (hw);
+        if (live <= 0)
+            goto again;
+
+        /* dolog ("in callback live=%d\n", live); */
+        to_mix = audio_MIN (samples, live);
+        decr = to_mix;
+        while (to_mix) {
+            int chunk = audio_MIN (to_mix, hw->samples - hw->rpos);
+            st_sample_t *src = hw->mix_buf + hw->rpos;
+
+            /* dolog ("in callback to_mix %d, chunk %d\n", to_mix, chunk); */
+            hw->clip (buf, src, chunk);
+            memset (src, 0, chunk * sizeof (st_sample_t));
+            hw->rpos = (hw->rpos + chunk) % hw->samples;
+            to_mix -= chunk;
+            buf += chunk << hw->shift;
+        }
+        samples -= decr;
+        pcm_hw_dec_live (hw, decr);
+
+    again:
+        sdl_unlock (s);
+    }
+    /* dolog ("done len=%d\n", len); */
+}
+
+static void sdl_hw_fini (HWVoice *hw)
+{
+    ldebug ("sdl_hw_fini %d fixed=%d\n",
+             glob_sdl.initialized, audio_conf.fixed_format);
+    sdl_close (&glob_sdl);
+}
+
+static int sdl_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
+{
+    SDLVoice *sdl = (SDLVoice *) hw;
+    SDLAudioState *s = &glob_sdl;
+    SDL_AudioSpec req, obt;
+    int shift;
+
+    ldebug ("sdl_hw_init %d freq=%d fixed=%d\n",
+            s->initialized, freq, audio_conf.fixed_format);
+
+    if (nchannels != 2) {
+        dolog ("Bogus channel count %d\n", nchannels);
+        return -1;
+    }
+
+    req.freq = freq;
+    req.format = AUD_to_sdlfmt (fmt, &shift);
+    req.channels = nchannels;
+    req.samples = conf.nb_samples;
+    shift <<= nchannels == 2;
+
+    req.callback = sdl_callback;
+    req.userdata = sdl;
+
+    if (sdl_open (&req, &obt))
+        return -1;
+
+    hw->freq = obt.freq;
+    hw->fmt = sdl_to_audfmt (obt.format);
+    hw->nchannels = obt.channels;
+    hw->bufsize = obt.samples << shift;
+
+    s->initialized = 1;
+    s->exit = 0;
+    SDL_PauseAudio (0);
+    return 0;
+}
+
+static int sdl_hw_ctl (HWVoice *hw, int cmd, ...)
+{
+    (void) hw;
+
+    switch (cmd) {
+    case VOICE_ENABLE:
+        SDL_PauseAudio (0);
+        break;
+
+    case VOICE_DISABLE:
+        SDL_PauseAudio (1);
+        break;
+    }
+    return 0;
+}
+
+static void *sdl_audio_init (void)
+{
+    SDLAudioState *s = &glob_sdl;
+    conf.nb_samples = audio_get_conf_int (QC_SDL_SAMPLES, conf.nb_samples);
+
+    if (SDL_InitSubSystem (SDL_INIT_AUDIO)) {
+        dolog ("SDL failed to initialize audio subsystem\nReason: %s\n",
+               errstr ());
+        return NULL;
+    }
+
+    s->mutex = SDL_CreateMutex ();
+    if (!s->mutex) {
+        dolog ("Failed to create SDL mutex\nReason: %s\n", errstr ());
+        SDL_QuitSubSystem (SDL_INIT_AUDIO);
+        return NULL;
+    }
+
+    s->sem = SDL_CreateSemaphore (0);
+    if (!s->sem) {
+        dolog ("Failed to create SDL semaphore\nReason: %s\n", errstr ());
+        SDL_DestroyMutex (s->mutex);
+        SDL_QuitSubSystem (SDL_INIT_AUDIO);
+        return NULL;
+    }
+
+    return s;
+}
+
+static void sdl_audio_fini (void *opaque)
+{
+    SDLAudioState *s = opaque;
+    sdl_close (s);
+    SDL_DestroySemaphore (s->sem);
+    SDL_DestroyMutex (s->mutex);
+    SDL_QuitSubSystem (SDL_INIT_AUDIO);
+}
+
+struct pcm_ops sdl_pcm_ops = {
+    sdl_hw_init,
+    sdl_hw_fini,
+    sdl_hw_run,
+    sdl_hw_write,
+    sdl_hw_ctl
+};
+
+struct audio_output_driver sdl_output_driver = {
+    "sdl",
+    sdl_audio_init,
+    sdl_audio_fini,
+    &sdl_pcm_ops,
+    1,
+    1,
+    sizeof (SDLVoice)
+};
diff -r 1283d309a603 tools/ioemu/audio/wavaudio.c
--- /dev/null   Sun Dec 18 20:29:43 2005 +0100
+++ b/tools/ioemu/audio/wavaudio.c      Tue Dec 20 16:58:20 2005 +0800
@@ -0,0 +1,217 @@
+/*
+ * QEMU WAV audio output driver
+ * 
+ * Copyright (c) 2004 Vassili Karpov (malc)
+ * 
+ * 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 "vl.h"
+
+#include "audio/audio_int.h"
+
+typedef struct WAVVoice {
+    HWVoice hw;
+    QEMUFile *f;
+    int64_t old_ticks;
+    void *pcm_buf;
+    int total_samples;
+} WAVVoice;
+
+#define dolog(...) AUD_log ("wav", __VA_ARGS__)
+#ifdef DEBUG
+#define ldebug(...) dolog (__VA_ARGS__)
+#else
+#define ldebug(...)
+#endif
+
+static struct {
+    const char *wav_path;
+} conf = {
+    .wav_path = "qemu.wav"
+};
+
+static void wav_hw_run (HWVoice *hw)
+{
+    WAVVoice *wav = (WAVVoice *) hw;
+    int rpos, live, decr, samples;
+    uint8_t *dst;
+    st_sample_t *src;
+    int64_t now = qemu_get_clock (vm_clock);
+    int64_t ticks = now - wav->old_ticks;
+    int64_t bytes = (ticks * hw->bytes_per_second) / ticks_per_sec;
+
+    if (bytes > INT_MAX)
+        samples = INT_MAX >> hw->shift;
+    else
+        samples = bytes >> hw->shift;
+
+    live = pcm_hw_get_live (hw);
+    if (live <= 0)
+        return;
+
+    wav->old_ticks = now;
+    decr = audio_MIN (live, samples);
+    samples = decr;
+    rpos = hw->rpos;
+    while (samples) {
+        int left_till_end_samples = hw->samples - rpos;
+        int convert_samples = audio_MIN (samples, left_till_end_samples);
+
+        src = advance (hw->mix_buf, rpos * sizeof (st_sample_t));
+        dst = advance (wav->pcm_buf, rpos << hw->shift);
+
+        hw->clip (dst, src, convert_samples);
+        qemu_put_buffer (wav->f, dst, convert_samples << hw->shift);
+        memset (src, 0, convert_samples * sizeof (st_sample_t));
+
+        rpos = (rpos + convert_samples) % hw->samples;
+        samples -= convert_samples;
+        wav->total_samples += convert_samples;
+    }
+
+    pcm_hw_dec_live (hw, decr);
+    hw->rpos = rpos;
+}
+
+static int wav_hw_write (SWVoice *sw, void *buf, int len)
+{
+    return pcm_hw_write (sw, buf, len);
+}
+
+/* VICE code: Store number as little endian. */
+static void le_store (uint8_t *buf, uint32_t val, int len)
+{
+    int i;
+    for (i = 0; i < len; i++) {
+        buf[i] = (uint8_t) (val & 0xff);
+        val >>= 8;
+    }
+}
+
+static int wav_hw_init (HWVoice *hw, int freq, int nchannels, audfmt_e fmt)
+{
+    WAVVoice *wav = (WAVVoice *) hw;
+    int bits16 = 0, stereo = audio_state.fixed_channels == 2;
+    uint8_t hdr[] = {
+        0x52, 0x49, 0x46, 0x46, 0x00, 0x00, 0x00, 0x00, 0x57, 0x41, 0x56,
+        0x45, 0x66, 0x6d, 0x74, 0x20, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00,
+        0x02, 0x00, 0x44, 0xac, 0x00, 0x00, 0x10, 0xb1, 0x02, 0x00, 0x04,
+        0x00, 0x10, 0x00, 0x64, 0x61, 0x74, 0x61, 0x00, 0x00, 0x00, 0x00
+    };
+
+    switch (audio_state.fixed_fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+        break;
+
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        bits16 = 1;
+        break;
+    }
+
+    hdr[34] = bits16 ? 0x10 : 0x08;
+    hw->freq = 44100;
+    hw->nchannels = stereo ? 2 : 1;
+    hw->fmt = bits16 ? AUD_FMT_S16 : AUD_FMT_U8;
+    hw->bufsize = 4096;
+    wav->pcm_buf = qemu_mallocz (hw->bufsize);
+    if (!wav->pcm_buf)
+        return -1;
+
+    le_store (hdr + 22, hw->nchannels, 2);
+    le_store (hdr + 24, hw->freq, 4);
+    le_store (hdr + 28, hw->freq << (bits16 + stereo), 4);
+    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
+
+    wav->f = fopen (conf.wav_path, "wb");
+    if (!wav->f) {
+        dolog ("failed to open wave file `%s'\nReason: %s\n",
+               conf.wav_path, strerror (errno));
+        qemu_free (wav->pcm_buf);
+        wav->pcm_buf = NULL;
+        return -1;
+    }
+
+    qemu_put_buffer (wav->f, hdr, sizeof (hdr));
+    return 0;
+}
+
+static void wav_hw_fini (HWVoice *hw)
+{
+    WAVVoice *wav = (WAVVoice *) hw;
+    int stereo = hw->nchannels == 2;
+    uint8_t rlen[4];
+    uint8_t dlen[4];
+    uint32_t rifflen = (wav->total_samples << stereo) + 36;
+    uint32_t datalen = wav->total_samples << stereo;
+
+    if (!wav->f || !hw->active)
+        return;
+
+    le_store (rlen, rifflen, 4);
+    le_store (dlen, datalen, 4);
+
+    qemu_fseek (wav->f, 4, SEEK_SET);
+    qemu_put_buffer (wav->f, rlen, 4);
+
+    qemu_fseek (wav->f, 32, SEEK_CUR);
+    qemu_put_buffer (wav->f, dlen, 4);
+
+    fclose (wav->f);
+    wav->f = NULL;
+
+    qemu_free (wav->pcm_buf);
+    wav->pcm_buf = NULL;
+}
+
+static int wav_hw_ctl (HWVoice *hw, int cmd, ...)
+{
+    (void) hw;
+    (void) cmd;
+    return 0;
+}
+
+static void *wav_audio_init (void)
+{
+    return &conf;
+}
+
+static void wav_audio_fini (void *opaque)
+{
+    ldebug ("wav_fini");
+}
+
+struct pcm_ops wav_pcm_ops = {
+    wav_hw_init,
+    wav_hw_fini,
+    wav_hw_run,
+    wav_hw_write,
+    wav_hw_ctl
+};
+
+struct audio_output_driver wav_output_driver = {
+    "wav",
+    wav_audio_init,
+    wav_audio_fini,
+    &wav_pcm_ops,
+    1,
+    1,
+    sizeof (WAVVoice)
+};


_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxx
http://lists.xensource.com/xen-devel

<Prev in Thread] Current Thread [Next in Thread>
  • [Xen-devel] [PATCH] Add sound blaster support in device model, Edwin Zhai <=