audio merge (malc)


git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1601 c046a42c-6fe2-441c-8c8c-71466251a162
diff --git a/Makefile.target b/Makefile.target
index cefad74..a22b2e0 100644
--- a/Makefile.target
+++ b/Makefile.target
@@ -453,8 +453,8 @@
 include .depend
 endif
 
-ifeq (0, 1)
+ifeq (1, 0)
 audio.o sdlaudio.o dsoundaudio.o ossaudio.o wavaudio.o noaudio.o \
-fmodaudio.o alsaaudio.o mixeng.o: \
+fmodaudio.o alsaaudio.o mixeng.o sb16.o es1370.o gus.o adlib.o: \
 CFLAGS := $(CFLAGS) -Wall -Werror -W -Wsign-compare
 endif
diff --git a/audio/alsaaudio.c b/audio/alsaaudio.c
index 1336905..65a0a0d 100644
--- a/audio/alsaaudio.c
+++ b/audio/alsaaudio.c
@@ -98,7 +98,7 @@
     audfmt_e fmt;
     int nchannels;
     int can_pause;
-    snd_pcm_uframes_t buffer_size;
+    snd_pcm_uframes_t samples;
 };
 
 static void GCC_FMT_ATTR (2, 3) alsa_logerr (int err, const char *fmt, ...)
@@ -121,7 +121,7 @@
 {
     va_list ap;
 
-    AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 
     va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
@@ -209,7 +209,7 @@
     return 0;
 }
 
-#ifdef DEBUG_MISMATCHES
+#if defined DEBUG_MISMATCHES || defined DEBUG
 static void alsa_dump_info (struct alsa_params_req *req,
                             struct alsa_params_obt *obt)
 {
@@ -221,7 +221,7 @@
     dolog ("============================================\n");
     dolog ("requested: buffer size %d period size %d\n",
            req->buffer_size, req->period_size);
-    dolog ("obtained: buffer size %ld\n", obt->buffer_size);
+    dolog ("obtained: samples %ld\n", obt->samples);
 }
 #endif
 
@@ -234,14 +234,14 @@
 
     err = snd_pcm_sw_params_current (handle, sw_params);
     if (err < 0) {
-        dolog ("Can not fully initialize DAC\n");
+        dolog ("Could not fully initialize DAC\n");
         alsa_logerr (err, "Failed to get current software parameters\n");
         return;
     }
 
     err = snd_pcm_sw_params_set_start_threshold (handle, sw_params, threshold);
     if (err < 0) {
-        dolog ("Can not fully initialize DAC\n");
+        dolog ("Could not fully initialize DAC\n");
         alsa_logerr (err, "Failed to set software threshold to %ld\n",
                      threshold);
         return;
@@ -249,7 +249,7 @@
 
     err = snd_pcm_sw_params (handle, sw_params);
     if (err < 0) {
-        dolog ("Can not fully initialize DAC\n");
+        dolog ("Could not fully initialize DAC\n");
         alsa_logerr (err, "Failed to set software parameters\n");
         return;
     }
@@ -344,7 +344,8 @@
                     handle,
                     hw_params,
                     &period_size,
-                    0);
+                    0
+                    );
                 if (err < 0) {
                     alsa_logerr2 (err, typ,
                                   "Failed to set period time %d\n",
@@ -357,7 +358,8 @@
                 handle,
                 hw_params,
                 &buffer_size,
-                0);
+                0
+                );
 
             if (err < 0) {
                 alsa_logerr2 (err, typ,
@@ -382,7 +384,7 @@
                 if (err < 0) {
                     alsa_logerr (
                         err,
-                        "Can not get minmal period size for %s\n",
+                        "Could not get minmal period size for %s\n",
                         typ
                         );
                 }
@@ -419,7 +421,7 @@
                 &minval
                 );
             if (err < 0) {
-                alsa_logerr (err, "Can not get minmal buffer size for %s\n",
+                alsa_logerr (err, "Could not get minmal buffer size for %s\n",
                              typ);
             }
             else {
@@ -451,7 +453,7 @@
         }
     }
     else {
-        dolog ("warning: buffer size is not set\n");
+        dolog ("warning: Buffer size is not set\n");
     }
 
     err = snd_pcm_hw_params (handle, hw_params);
@@ -468,13 +470,13 @@
 
     err = snd_pcm_prepare (handle);
     if (err < 0) {
-        alsa_logerr2 (err, typ, "Can not prepare handle %p\n", handle);
+        alsa_logerr2 (err, typ, "Could not prepare handle %p\n", handle);
         goto err;
     }
 
     obt->can_pause = snd_pcm_hw_params_can_pause (hw_params);
     if (obt->can_pause < 0) {
-        alsa_logerr (err, "Can not get pause capability for %s\n", typ);
+        alsa_logerr (err, "Could not get pause capability for %s\n", typ);
         obt->can_pause = 0;
     }
 
@@ -493,17 +495,17 @@
     obt->fmt = req->fmt;
     obt->nchannels = nchannels;
     obt->freq = freq;
-    obt->buffer_size = snd_pcm_frames_to_bytes (handle, obt_buffer_size);
+    obt->samples = obt_buffer_size;
     *handlep = handle;
 
+#if defined DEBUG_MISMATCHES || defined DEBUG
     if (obt->fmt != req->fmt ||
         obt->nchannels != req->nchannels ||
         obt->freq != req->freq) {
-#ifdef DEBUG_MISMATCHES
         dolog ("Audio paramters mismatch for %s\n", typ);
         alsa_dump_info (req, obt);
-#endif
     }
+#endif
 
 #ifdef DEBUG
     alsa_dump_info (req, obt);
@@ -550,7 +552,7 @@
             }
         }
 
-        alsa_logerr (avail, "Can not get amount free space\n");
+        alsa_logerr (avail, "Could not get amount free space\n");
         return 0;
     }
 
@@ -618,7 +620,7 @@
     }
 }
 
-static int alsa_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
+static int alsa_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     ALSAVoiceOut *alsa = (ALSAVoiceOut *) hw;
     struct alsa_params_req req;
@@ -627,10 +629,11 @@
     int endianness;
     int err;
     snd_pcm_t *handle;
+    audsettings_t obt_as;
 
-    req.fmt = aud_to_alsafmt (fmt);
-    req.freq = freq;
-    req.nchannels = nchannels;
+    req.fmt = aud_to_alsafmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
     req.period_size = conf.period_size_out;
     req.buffer_size = conf.buffer_size_out;
 
@@ -644,18 +647,22 @@
         return -1;
     }
 
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+
     audio_pcm_init_info (
         &hw->info,
-        obt.freq,
-        obt.nchannels,
-        effective_fmt,
+        &obt_as,
         audio_need_to_swap_endian (endianness)
         );
     alsa->can_pause = obt.can_pause;
-    hw->bufsize = obt.buffer_size;
+    hw->samples = obt.samples;
 
-    alsa->pcm_buf = qemu_mallocz (hw->bufsize);
+    alsa->pcm_buf = audio_calloc (AUDIO_FUNC, obt.samples, 1 << hw->info.shift);
     if (!alsa->pcm_buf) {
+        dolog ("Could not allocate DAC buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
         alsa_anal_close (&handle);
         return -1;
     }
@@ -703,8 +710,7 @@
     return 0;
 }
 
-static int alsa_init_in (HWVoiceIn *hw,
-                        int freq, int nchannels, audfmt_e fmt)
+static int alsa_init_in (HWVoiceIn *hw, audsettings_t *as)
 {
     ALSAVoiceIn *alsa = (ALSAVoiceIn *) hw;
     struct alsa_params_req req;
@@ -713,10 +719,11 @@
     int err;
     audfmt_e effective_fmt;
     snd_pcm_t *handle;
+    audsettings_t obt_as;
 
-    req.fmt = aud_to_alsafmt (fmt);
-    req.freq = freq;
-    req.nchannels = nchannels;
+    req.fmt = aud_to_alsafmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
     req.period_size = conf.period_size_in;
     req.buffer_size = conf.buffer_size_in;
 
@@ -730,17 +737,22 @@
         return -1;
     }
 
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+
     audio_pcm_init_info (
         &hw->info,
-        obt.freq,
-        obt.nchannels,
-        effective_fmt,
+        &obt_as,
         audio_need_to_swap_endian (endianness)
         );
     alsa->can_pause = obt.can_pause;
-    hw->bufsize = obt.buffer_size;
-    alsa->pcm_buf = qemu_mallocz (hw->bufsize);
+    hw->samples = obt.samples;
+
+    alsa->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     if (!alsa->pcm_buf) {
+        dolog ("Could not allocate ADC buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
         alsa_anal_close (&handle);
         return -1;
     }
diff --git a/audio/audio.c b/audio/audio.c
index 1a3925d..961654b 100644
--- a/audio/audio.c
+++ b/audio/audio.c
@@ -26,16 +26,12 @@
 #define AUDIO_CAP "audio"
 #include "audio_int.h"
 
-static void audio_pcm_hw_fini_in (HWVoiceIn *hw);
-static void audio_pcm_hw_fini_out (HWVoiceOut *hw);
-
-static LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
-static LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
-
 /* #define DEBUG_PLIVE */
 /* #define DEBUG_LIVE */
 /* #define DEBUG_OUT */
 
+#define SW_NAME(sw) (sw)->name ? (sw)->name : "unknown"
+
 static struct audio_driver *drvtab[] = {
 #ifdef CONFIG_OSS
     &oss_audio_driver,
@@ -59,31 +55,50 @@
     &wav_audio_driver
 };
 
-AudioState audio_state = {
-    /* Out */
-    1,                          /* use fixed settings */
-    44100,                      /* fixed frequency */
-    2,                          /* fixed channels */
-    AUD_FMT_S16,                /* fixed format */
-    1,                          /* number of hw voices */
-    1,                          /* greedy */
+struct fixed_settings {
+    int enabled;
+    int nb_voices;
+    int greedy;
+    audsettings_t settings;
+};
 
-    /* In */
-    1,                          /* use fixed settings */
-    44100,                      /* fixed frequency */
-    2,                          /* fixed channels */
-    AUD_FMT_S16,                /* fixed format */
-    1,                          /* number of hw voices */
-    1,                          /* greedy */
+static struct {
+    struct fixed_settings fixed_out;
+    struct fixed_settings fixed_in;
+    union {
+        int hz;
+        int64_t ticks;
+    } period;
+    int plive;
+} conf = {
+    {                           /* DAC fixed settings */
+        1,                      /* enabled */
+        1,                      /* nb_voices */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16         /* fmt */
+        }
+    },
 
-    NULL,                       /* driver opaque */
-    NULL,                       /* driver */
+    {                           /* ADC fixed settings */
+        1,                      /* enabled */
+        1,                      /* nb_voices */
+        1,                      /* greedy */
+        {
+            44100,              /* freq */
+            2,                  /* nchannels */
+            AUD_FMT_S16         /* fmt */
+        }
+    },
 
-    NULL,                       /* timer handle */
     { 0 },                      /* period */
     0                           /* plive */
 };
 
+static AudioState glob_audio_state;
+
 volume_t nominal_volume = {
     0,
 #ifdef FLOAT_MIXENG
@@ -148,6 +163,26 @@
 }
 #endif
 
+void *audio_calloc (const char *funcname, int nmemb, size_t size)
+{
+    int cond;
+    size_t len;
+
+    len = nmemb * size;
+    cond = !nmemb || !size;
+    cond |= nmemb < 0;
+    cond |= len < size;
+
+    if (audio_bug ("audio_calloc", cond)) {
+        AUD_log (NULL, "%s passed invalid arguments to audio_calloc\n",
+                 funcname);
+        AUD_log (NULL, "nmemb=%d size=%d (len=%d)\n", nmemb, size, len);
+        return NULL;
+    }
+
+    return qemu_mallocz (len);
+}
+
 static char *audio_alloc_prefix (const char *s)
 {
     const char qemu_prefix[] = "QEMU_";
@@ -386,14 +421,19 @@
         }
 
         len = strlen (opt->name);
+        /* len of opt->name + len of prefix + size of qemu_prefix
+         * (includes trailing zero) + zero + underscore (on behalf of
+         * sizeof) */
         optname = qemu_malloc (len + preflen + sizeof (qemu_prefix) + 1);
         if (!optname) {
-            dolog ("Can not allocate memory for option name `%s'\n",
+            dolog ("Could not allocate memory for option name `%s'\n",
                    opt->name);
             continue;
         }
 
         strcpy (optname, qemu_prefix);
+
+        /* copy while upper-casing, including trailing zero */
         for (i = 0; i <= preflen; ++i) {
             optname[i + sizeof (qemu_prefix) - 1] = toupper (prefix[i]);
         }
@@ -438,12 +478,60 @@
     }
 }
 
-static int audio_pcm_info_eq (struct audio_pcm_info *info, int freq,
-                              int nchannels, audfmt_e fmt)
+static void audio_print_settings (audsettings_t *as)
+{
+    dolog ("frequency=%d nchannels=%d fmt=", as->freq, as->nchannels);
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+        AUD_log (NULL, "S8");
+        break;
+    case AUD_FMT_U8:
+        AUD_log (NULL, "U8");
+        break;
+    case AUD_FMT_S16:
+        AUD_log (NULL, "S16");
+        break;
+    case AUD_FMT_U16:
+        AUD_log (NULL, "U16");
+        break;
+    default:
+        AUD_log (NULL, "invalid(%d)", as->fmt);
+        break;
+    }
+    AUD_log (NULL, "\n");
+}
+
+static int audio_validate_settigs (audsettings_t *as)
+{
+    int invalid;
+
+    invalid = as->nchannels != 1 && as->nchannels != 2;
+
+    switch (as->fmt) {
+    case AUD_FMT_S8:
+    case AUD_FMT_U8:
+    case AUD_FMT_S16:
+    case AUD_FMT_U16:
+        break;
+    default:
+        invalid = 1;
+        break;
+    }
+
+    invalid |= as->freq <= 0;
+
+    if (invalid) {
+        return -1;
+    }
+    return 0;
+}
+
+static int audio_pcm_info_eq (struct audio_pcm_info *info, audsettings_t *as)
 {
     int bits = 8, sign = 0;
 
-    switch (fmt) {
+    switch (as->fmt) {
     case AUD_FMT_S8:
         sign = 1;
     case AUD_FMT_U8:
@@ -455,18 +543,21 @@
         bits = 16;
         break;
     }
-    return info->freq == freq
-        && info->nchannels == nchannels
+    return info->freq == as->freq
+        && info->nchannels == as->nchannels
         && info->sign == sign
         && info->bits == bits;
 }
 
-void audio_pcm_init_info (struct audio_pcm_info *info, int freq,
-                          int nchannels, audfmt_e fmt, int swap_endian)
+void audio_pcm_init_info (
+    struct audio_pcm_info *info,
+    audsettings_t *as,
+    int swap_endian
+    )
 {
     int bits = 8, sign = 0;
 
-    switch (fmt) {
+    switch (as->fmt) {
     case AUD_FMT_S8:
         sign = 1;
     case AUD_FMT_U8:
@@ -479,11 +570,11 @@
         break;
     }
 
-    info->freq = freq;
+    info->freq = as->freq;
     info->bits = bits;
     info->sign = sign;
-    info->nchannels = nchannels;
-    info->shift = (nchannels == 2) + (bits == 16);
+    info->nchannels = as->nchannels;
+    info->shift = (as->nchannels == 2) + (bits == 16);
     info->align = (1 << info->shift) - 1;
     info->bytes_per_second = info->freq << info->shift;
     info->swap_endian = swap_endian;
@@ -532,38 +623,16 @@
 
 static int audio_pcm_hw_alloc_resources_in (HWVoiceIn *hw)
 {
-    hw->conv_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t));
+    hw->conv_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
     if (!hw->conv_buf) {
+        dolog ("Could not allocate ADC conversion buffer (%d bytes)\n",
+               hw->samples * sizeof (st_sample_t));
         return -1;
     }
     return 0;
 }
 
-static int audio_pcm_hw_init_in (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt)
-{
-    audio_pcm_hw_fini_in (hw);
-
-    if (hw->pcm_ops->init_in (hw, freq, nchannels, fmt)) {
-        memset (hw, 0, audio_state.drv->voice_size_in);
-        return -1;
-    }
-    LIST_INIT (&hw->sw_head);
-    hw->active = 1;
-    hw->samples = hw->bufsize >> hw->info.shift;
-    hw->conv =
-        mixeng_conv
-        [nchannels == 2]
-        [hw->info.sign]
-        [hw->info.swap_endian]
-        [hw->info.bits == 16];
-    if (audio_pcm_hw_alloc_resources_in (hw)) {
-        audio_pcm_hw_free_resources_in (hw);
-        return -1;
-    }
-    return 0;
-}
-
-static uint64_t audio_pcm_hw_find_min_in (HWVoiceIn *hw)
+static int audio_pcm_hw_find_min_in (HWVoiceIn *hw)
 {
     SWVoiceIn *sw;
     int m = hw->total_samples_captured;
@@ -606,8 +675,10 @@
 static int audio_pcm_sw_alloc_resources_in (SWVoiceIn *sw)
 {
     int samples = ((int64_t) sw->hw->samples << 32) / sw->ratio;
-    sw->conv_buf = qemu_mallocz (samples * sizeof (st_sample_t));
+    sw->conv_buf = audio_calloc (AUDIO_FUNC, samples, sizeof (st_sample_t));
     if (!sw->conv_buf) {
+        dolog ("Could not allocate buffer for `%s' (%d bytes)\n",
+               SW_NAME (sw), samples * sizeof (st_sample_t));
         return -1;
     }
 
@@ -620,19 +691,22 @@
     return 0;
 }
 
-static int audio_pcm_sw_init_in (SWVoiceIn *sw, HWVoiceIn *hw, const char *name,
-                           int freq, int nchannels, audfmt_e fmt)
+static int audio_pcm_sw_init_in (
+    SWVoiceIn *sw,
+    HWVoiceIn *hw,
+    const char *name,
+    audsettings_t *as
+    )
 {
-    audio_pcm_init_info (&sw->info, freq, nchannels, fmt,
-                         /* None of the cards emulated by QEMU are big-endian
-                            hence following shortcut */
-                         audio_need_to_swap_endian (0));
+    /* None of the cards emulated by QEMU are big-endian
+       hence following shortcut */
+    audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0));
     sw->hw = hw;
     sw->ratio = ((int64_t) sw->info.freq << 32) / sw->hw->info.freq;
 
     sw->clip =
         mixeng_clip
-        [nchannels == 2]
+        [sw->info.nchannels == 2]
         [sw->info.sign]
         [sw->info.swap_endian]
         [sw->info.bits == 16];
@@ -699,6 +773,7 @@
 
         if (audio_bug (AUDIO_FUNC, osamp < 0)) {
             dolog ("osamp=%d\n", osamp);
+            return 0;
         }
 
         st_rate_flow (sw->rate, src, dst, &isamp, &osamp);
@@ -717,6 +792,27 @@
 /*
  * Hard voice (playback)
  */
+static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw)
+{
+    if (hw->mix_buf) {
+        qemu_free (hw->mix_buf);
+    }
+
+    hw->mix_buf = NULL;
+}
+
+static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw)
+{
+    hw->mix_buf = audio_calloc (AUDIO_FUNC, hw->samples, sizeof (st_sample_t));
+    if (!hw->mix_buf) {
+        dolog ("Could not allocate DAC mixing buffer (%d bytes)\n",
+               hw->samples * sizeof (st_sample_t));
+        return -1;
+    }
+
+    return 0;
+}
+
 static int audio_pcm_hw_find_min_out (HWVoiceOut *hw, int *nb_livep)
 {
     SWVoiceOut *sw;
@@ -734,50 +830,6 @@
     return m;
 }
 
-static void audio_pcm_hw_free_resources_out (HWVoiceOut *hw)
-{
-    if (hw->mix_buf) {
-        qemu_free (hw->mix_buf);
-    }
-
-    hw->mix_buf = NULL;
-}
-
-static int audio_pcm_hw_alloc_resources_out (HWVoiceOut *hw)
-{
-    hw->mix_buf = qemu_mallocz (hw->samples * sizeof (st_sample_t));
-    if (!hw->mix_buf) {
-        return -1;
-    }
-
-    return 0;
-}
-
-static int audio_pcm_hw_init_out (HWVoiceOut *hw, int freq,
-                            int nchannels, audfmt_e fmt)
-{
-    audio_pcm_hw_fini_out (hw);
-    if (hw->pcm_ops->init_out (hw, freq, nchannels, fmt)) {
-        memset (hw, 0, audio_state.drv->voice_size_out);
-        return -1;
-    }
-
-    LIST_INIT (&hw->sw_head);
-    hw->active = 1;
-    hw->samples = hw->bufsize >> hw->info.shift;
-    hw->clip =
-        mixeng_clip
-        [nchannels == 2]
-        [hw->info.sign]
-        [hw->info.swap_endian]
-        [hw->info.bits == 16];
-    if (audio_pcm_hw_alloc_resources_out (hw)) {
-        audio_pcm_hw_fini_out (hw);
-        return -1;
-    }
-    return 0;
-}
-
 int audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live)
 {
     int smin;
@@ -830,8 +882,10 @@
 
 static int audio_pcm_sw_alloc_resources_out (SWVoiceOut *sw)
 {
-    sw->buf = qemu_mallocz (sw->hw->samples * sizeof (st_sample_t));
+    sw->buf = audio_calloc (AUDIO_FUNC, sw->hw->samples, sizeof (st_sample_t));
     if (!sw->buf) {
+        dolog ("Could not allocate buffer for `%s' (%d bytes)\n",
+               SW_NAME (sw), sw->hw->samples * sizeof (st_sample_t));
         return -1;
     }
 
@@ -844,14 +898,16 @@
     return 0;
 }
 
-static int audio_pcm_sw_init_out (SWVoiceOut *sw, HWVoiceOut *hw,
-                            const char *name, int freq,
-                            int nchannels, audfmt_e fmt)
+static int audio_pcm_sw_init_out (
+    SWVoiceOut *sw,
+    HWVoiceOut *hw,
+    const char *name,
+    audsettings_t *as
+    )
 {
-    audio_pcm_init_info (&sw->info, freq, nchannels, fmt,
-                         /* None of the cards emulated by QEMU are big-endian
-                            hence following shortcut */
-                         audio_need_to_swap_endian (0));
+    /* None of the cards emulated by QEMU are big-endian
+       hence following shortcut */
+    audio_pcm_init_info (&sw->info, as, audio_need_to_swap_endian (0));
     sw->hw = hw;
     sw->empty = 1;
     sw->active = 0;
@@ -860,7 +916,7 @@
 
     sw->conv =
         mixeng_conv
-        [nchannels == 2]
+        [sw->info.nchannels == 2]
         [sw->info.sign]
         [sw->info.swap_endian]
         [sw->info.bits == 16];
@@ -930,12 +986,11 @@
 
 #ifdef DEBUG_OUT
     dolog (
-        "%s: write size %d ret %d total sw %d, hw %d\n",
-        sw->name,
+        "%s: write size %d ret %d total sw %d\n",
+        SW_NAME (sw),
         size >> sw->info.shift,
         ret,
-        sw->total_hw_samples_mixed,
-        sw->hw->total_samples_played
+        sw->total_hw_samples_mixed
         );
 #endif
 
@@ -965,7 +1020,7 @@
     }
 
     if (!sw->hw->enabled) {
-        dolog ("Writing to disabled voice %s\n", sw->name);
+        dolog ("Writing to disabled voice %s\n", SW_NAME (sw));
         return 0;
     }
 
@@ -983,7 +1038,7 @@
     }
 
     if (!sw->hw->enabled) {
-        dolog ("Reading from disabled voice %s\n", sw->name);
+        dolog ("Reading from disabled voice %s\n", SW_NAME (sw));
         return 0;
     }
 
@@ -993,7 +1048,7 @@
 
 int AUD_get_buffer_size_out (SWVoiceOut *sw)
 {
-    return sw->hw->bufsize;
+    return sw->hw->samples << sw->hw->info.shift;
 }
 
 void AUD_set_active_out (SWVoiceOut *sw, int on)
@@ -1091,7 +1146,7 @@
 
     ldebug (
         "%s: get_avail live %d ret %lld\n",
-        sw->name,
+        SW_NAME (sw),
         live, (((int64_t) live << 32) / sw->ratio) << sw->info.shift
         );
 
@@ -1110,34 +1165,37 @@
 
     if (audio_bug (AUDIO_FUNC, live < 0 || live > sw->hw->samples)) {
         dolog ("live=%d sw->hw->samples=%d\n", live, sw->hw->samples);
+        return 0;
     }
 
     dead = sw->hw->samples - live;
 
 #ifdef DEBUG_OUT
     dolog ("%s: get_free live %d dead %d ret %lld\n",
-           sw->name,
+           SW_NAME (sw),
            live, dead, (((int64_t) dead << 32) / sw->ratio) << sw->info.shift);
 #endif
 
     return (((int64_t) dead << 32) / sw->ratio) << sw->info.shift;
 }
 
-static void audio_run_out (void)
+static void audio_run_out (AudioState *s)
 {
     HWVoiceOut *hw = NULL;
     SWVoiceOut *sw;
 
-    while ((hw = audio_pcm_hw_find_any_active_enabled_out (hw))) {
+    while ((hw = audio_pcm_hw_find_any_enabled_out (s, hw))) {
         int played;
-        int live, free, nb_live;
+        int live, free, nb_live, cleanup_required;
 
         live = audio_pcm_hw_get_live_out2 (hw, &nb_live);
         if (!nb_live) {
             live = 0;
         }
+
         if (audio_bug (AUDIO_FUNC, live < 0 || live > hw->samples)) {
             dolog ("live=%d hw->samples=%d\n", live, hw->samples);
+            continue;
         }
 
         if (hw->pending_disable && !nb_live) {
@@ -1170,15 +1228,15 @@
         }
 
 #ifdef DEBUG_OUT
-        dolog ("played = %d total %d\n", played, hw->total_samples_played);
+        dolog ("played=%d\n", played);
 #endif
 
         if (played) {
             hw->ts_helper += played;
         }
 
+        cleanup_required = 0;
         for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
-        again:
             if (!sw->active && sw->empty) {
                 continue;
             }
@@ -1193,22 +1251,7 @@
 
             if (!sw->total_hw_samples_mixed) {
                 sw->empty = 1;
-
-                if (!sw->active && !sw->callback.fn) {
-                    SWVoiceOut *temp = sw->entries.le_next;
-
-#ifdef DEBUG_PLIVE
-                    dolog ("Finishing with old voice\n");
-#endif
-                    AUD_close_out (sw);
-                    sw = temp;
-                    if (sw) {
-                        goto again;
-                    }
-                    else {
-                        break;
-                    }
-                }
+                cleanup_required |= !sw->active && !sw->callback.fn;
             }
 
             if (sw->active) {
@@ -1218,14 +1261,27 @@
                 }
             }
         }
+
+        if (cleanup_required) {
+        restart:
+            for (sw = hw->sw_head.lh_first; sw; sw = sw->entries.le_next) {
+                if (!sw->active && !sw->callback.fn) {
+#ifdef DEBUG_PLIVE
+                    dolog ("Finishing with old voice\n");
+#endif
+                    audio_close_out (s, sw);
+                    goto restart; /* play it safe */
+                }
+            }
+        }
     }
 }
 
-static void audio_run_in (void)
+static void audio_run_in (AudioState *s)
 {
     HWVoiceIn *hw = NULL;
 
-    while ((hw = audio_pcm_hw_find_any_active_enabled_in (hw))) {
+    while ((hw = audio_pcm_hw_find_any_enabled_in (s, hw))) {
         SWVoiceIn *sw;
         int captured, min;
 
@@ -1252,42 +1308,42 @@
 
 static struct audio_option audio_options[] = {
     /* DAC */
-    {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out,
+    {"DAC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_out.enabled,
      "Use fixed settings for host DAC", NULL, 0},
 
-    {"DAC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out,
+    {"DAC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_out.settings.freq,
      "Frequency for fixed host DAC", NULL, 0},
 
-    {"DAC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out,
+    {"DAC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_out.settings.fmt,
      "Format for fixed host DAC", NULL, 0},
 
-    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_out,
+    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_out.settings.nchannels,
      "Number of channels for fixed DAC (1 - mono, 2 - stereo)", NULL, 0},
 
-    {"DAC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out,
+    {"DAC_VOICES", AUD_OPT_INT, &conf.fixed_out.nb_voices,
      "Number of voices for DAC", NULL, 0},
 
     /* ADC */
-    {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &audio_state.fixed_settings_out,
+    {"ADC_FIXED_SETTINGS", AUD_OPT_BOOL, &conf.fixed_in.enabled,
      "Use fixed settings for host ADC", NULL, 0},
 
-    {"ADC_FIXED_FREQ", AUD_OPT_INT, &audio_state.fixed_freq_out,
-     "Frequency for fixed ADC", NULL, 0},
+    {"ADC_FIXED_FREQ", AUD_OPT_INT, &conf.fixed_in.settings.freq,
+     "Frequency for fixed host ADC", NULL, 0},
 
-    {"ADC_FIXED_FMT", AUD_OPT_FMT, &audio_state.fixed_fmt_out,
-     "Format for fixed ADC", NULL, 0},
+    {"ADC_FIXED_FMT", AUD_OPT_FMT, &conf.fixed_in.settings.fmt,
+     "Format for fixed host ADC", NULL, 0},
 
-    {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &audio_state.fixed_channels_in,
+    {"ADC_FIXED_CHANNELS", AUD_OPT_INT, &conf.fixed_in.settings.nchannels,
      "Number of channels for fixed ADC (1 - mono, 2 - stereo)", NULL, 0},
 
-    {"ADC_VOICES", AUD_OPT_INT, &audio_state.nb_hw_voices_out,
+    {"ADC_VOICES", AUD_OPT_INT, &conf.fixed_in.nb_voices,
      "Number of voices for ADC", NULL, 0},
 
     /* Misc */
-    {"TIMER_PERIOD", AUD_OPT_INT, &audio_state.period.usec,
-     "Timer period in microseconds (0 - try lowest possible)", NULL, 0},
+    {"TIMER_PERIOD", AUD_OPT_INT, &conf.period.hz,
+     "Timer period in HZ (0 - use lowest possible)", NULL, 0},
 
-    {"PLIVE", AUD_OPT_BOOL, &audio_state.plive,
+    {"PLIVE", AUD_OPT_BOOL, &conf.plive,
      "(undocumented)", NULL, 0},
 
     {NULL, 0, NULL, NULL, NULL, 0}
@@ -1378,25 +1434,21 @@
 {
     AudioState *s = opaque;
 
-    audio_run_out ();
-    audio_run_in ();
+    audio_run_out (s);
+    audio_run_in (s);
 
-    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks);
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
 }
 
-static int audio_driver_init (struct audio_driver *drv)
+static int audio_driver_init (AudioState *s, struct audio_driver *drv)
 {
     if (drv->options) {
         audio_process_options (drv->name, drv->options);
     }
-    audio_state.opaque = drv->init ();
+    s->drv_opaque = drv->init ();
 
-    if (audio_state.opaque) {
-        int i;
-        HWVoiceOut *hwo;
-        HWVoiceIn *hwi;
-
-        if (audio_state.nb_hw_voices_out > drv->max_voices_out) {
+    if (s->drv_opaque) {
+        if (s->nb_hw_voices_out > drv->max_voices_out) {
             if (!drv->max_voices_out) {
                 dolog ("`%s' does not support DAC\n", drv->name);
             }
@@ -1405,30 +1457,13 @@
                     "`%s' does not support %d multiple DAC voicess\n"
                     "Resetting to %d\n",
                     drv->name,
-                    audio_state.nb_hw_voices_out,
+                    s->nb_hw_voices_out,
                     drv->max_voices_out
                     );
             }
-            audio_state.nb_hw_voices_out = drv->max_voices_out;
+            s->nb_hw_voices_out = drv->max_voices_out;
         }
 
-        LIST_INIT (&hw_head_out);
-        hwo = qemu_mallocz (audio_state.nb_hw_voices_out * drv->voice_size_out);
-        if (!hwo) {
-            dolog (
-                "Not enough memory for %d `%s' DAC voices (each %d bytes)\n",
-                audio_state.nb_hw_voices_out,
-                drv->name,
-                drv->voice_size_out
-                );
-            drv->fini (audio_state.opaque);
-            return -1;
-        }
-
-        for (i = 0; i < audio_state.nb_hw_voices_out; ++i) {
-            LIST_INSERT_HEAD (&hw_head_out, hwo, entries);
-            hwo = advance (hwo, drv->voice_size_out);
-        }
 
         if (!drv->voice_size_in && drv->max_voices_in) {
             ldebug ("warning: No ADC voice size defined for `%s'\n",
@@ -1442,16 +1477,16 @@
         }
 
         if (drv->voice_size_in && !drv->max_voices_in) {
-            ldebug ("warning: ADC voice size is %d for ADC less driver `%s'\n",
-                    drv->voice_size_out, drv->name);
+            ldebug ("warning: `%s' ADC voice size %d, zero voices \n",
+                    drv->name, drv->voice_size_out);
         }
 
         if (drv->voice_size_out && !drv->max_voices_out) {
-            ldebug ("warning: DAC voice size is %d for DAC less driver `%s'\n",
-                    drv->voice_size_in, drv->name);
+            ldebug ("warning: `%s' DAC voice size %d, zero voices \n",
+                    drv->name, drv->voice_size_in);
         }
 
-        if (audio_state.nb_hw_voices_in > drv->max_voices_in) {
+        if (s->nb_hw_voices_in > drv->max_voices_in) {
             if (!drv->max_voices_in) {
                 ldebug ("`%s' does not support ADC\n", drv->name);
             }
@@ -1460,33 +1495,16 @@
                     "`%s' does not support %d multiple ADC voices\n"
                     "Resetting to %d\n",
                     drv->name,
-                    audio_state.nb_hw_voices_in,
+                    s->nb_hw_voices_in,
                     drv->max_voices_in
                     );
             }
-            audio_state.nb_hw_voices_in = drv->max_voices_in;
+            s->nb_hw_voices_in = drv->max_voices_in;
         }
 
-        LIST_INIT (&hw_head_in);
-        hwi = qemu_mallocz (audio_state.nb_hw_voices_in * drv->voice_size_in);
-        if (!hwi) {
-            dolog (
-                "Not enough memory for %d `%s' ADC voices (each %d bytes)\n",
-                audio_state.nb_hw_voices_in,
-                drv->name,
-                drv->voice_size_in
-                );
-            qemu_free (hwo);
-            drv->fini (audio_state.opaque);
-            return -1;
-        }
-
-        for (i = 0; i < audio_state.nb_hw_voices_in; ++i) {
-            LIST_INSERT_HEAD (&hw_head_in, hwi, entries);
-            hwi = advance (hwi, drv->voice_size_in);
-        }
-
-        audio_state.drv = drv;
+        LIST_INIT (&s->hw_head_out);
+        LIST_INIT (&s->hw_head_in);
+        s->drv = drv;
         return 0;
     }
     else {
@@ -1497,12 +1515,12 @@
 
 static void audio_vm_stop_handler (void *opaque, int reason)
 {
+    AudioState *s = opaque;
     HWVoiceOut *hwo = NULL;
     HWVoiceIn *hwi = NULL;
     int op = reason ? VOICE_ENABLE : VOICE_DISABLE;
 
-    (void) opaque;
-    while ((hwo = audio_pcm_hw_find_any_out (hwo))) {
+    while ((hwo = audio_pcm_hw_find_any_out (s, hwo))) {
         if (!hwo->pcm_ops) {
             continue;
         }
@@ -1512,7 +1530,7 @@
         }
     }
 
-    while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
+    while ((hwi = audio_pcm_hw_find_any_in (s, hwi))) {
         if (!hwi->pcm_ops) {
             continue;
         }
@@ -1525,10 +1543,11 @@
 
 static void audio_atexit (void)
 {
+    AudioState *s = &glob_audio_state;
     HWVoiceOut *hwo = NULL;
     HWVoiceIn *hwi = NULL;
 
-    while ((hwo = audio_pcm_hw_find_any_out (hwo))) {
+    while ((hwo = audio_pcm_hw_find_any_out (s, hwo))) {
         if (!hwo->pcm_ops) {
             continue;
         }
@@ -1539,7 +1558,7 @@
         hwo->pcm_ops->fini_out (hwo);
     }
 
-    while ((hwi = audio_pcm_hw_find_any_in (hwi))) {
+    while ((hwi = audio_pcm_hw_find_any_in (s, hwi))) {
         if (!hwi->pcm_ops) {
             continue;
         }
@@ -1549,7 +1568,10 @@
         }
         hwi->pcm_ops->fini_in (hwi);
     }
-    audio_state.drv->fini (audio_state.opaque);
+
+    if (s->drv) {
+        s->drv->fini (s->drv_opaque);
+    }
 }
 
 static void audio_save (QEMUFile *f, void *opaque)
@@ -1570,15 +1592,33 @@
     return 0;
 }
 
-void AUD_init (void)
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card)
+{
+    card->audio = s;
+    card->name = qemu_strdup (name);
+    memset (&card->entries, 0, sizeof (card->entries));
+    LIST_INSERT_HEAD (&s->card_head, card, entries);
+}
+
+void AUD_remove_card (QEMUSoundCard *card)
+{
+    LIST_REMOVE (card, entries);
+    card->audio = NULL;
+    qemu_free (card->name);
+}
+
+AudioState *AUD_init (void)
 {
     size_t i;
     int done = 0;
     const char *drvname;
-    AudioState *s = &audio_state;
+    AudioState *s = &glob_audio_state;
 
     audio_process_options ("AUDIO", audio_options);
 
+    s->nb_hw_voices_out = conf.fixed_out.nb_voices;
+    s->nb_hw_voices_in = conf.fixed_in.nb_voices;
+
     if (s->nb_hw_voices_out <= 0) {
         dolog ("Bogus number of DAC voices %d\n",
                s->nb_hw_voices_out);
@@ -1598,8 +1638,8 @@
 
     s->ts = qemu_new_timer (vm_clock, audio_timer, s);
     if (!s->ts) {
-        dolog ("Can not create audio timer\n");
-        return;
+        dolog ("Could not create audio timer\n");
+        return NULL;
     }
 
     if (drvname) {
@@ -1607,7 +1647,7 @@
 
         for (i = 0; i < sizeof (drvtab) / sizeof (drvtab[0]); i++) {
             if (!strcmp (drvname, drvtab[i]->name)) {
-                done = !audio_driver_init (drvtab[i]);
+                done = !audio_driver_init (s, drvtab[i]);
                 found = 1;
                 break;
             }
@@ -1619,37 +1659,47 @@
         }
     }
 
-    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 = !audio_driver_init (drvtab[i]);
+                done = !audio_driver_init (s, drvtab[i]);
             }
         }
     }
 
-    register_savevm ("audio", 0, 1, audio_save, audio_load, NULL);
     if (!done) {
-        if (audio_driver_init (&no_audio_driver)) {
-            dolog ("Can not initialize audio subsystem\n");
+        done = !audio_driver_init (s, &no_audio_driver);
+        if (!done) {
+            dolog ("Could not initialize audio subsystem\n");
         }
         else {
-            dolog ("warning: using timer based audio emulation\n");
+            dolog ("warning: Using timer based audio emulation\n");
         }
     }
 
-    if (s->period.usec <= 0) {
-        if (s->period.usec < 0) {
-            dolog ("warning: timer period is negative - %d treating as zero\n",
-                   s->period.usec);
+    if (done) {
+        if (conf.period.hz <= 0) {
+            if (conf.period.hz < 0) {
+                dolog ("warning: Timer period is negative - %d "
+                       "treating as zero\n",
+                       conf.period.hz);
+            }
+            conf.period.ticks = 1;
         }
-        s->period.ticks = 1;
+        else {
+            conf.period.ticks = ticks_per_sec / conf.period.hz;
+        }
+
+        qemu_add_vm_stop_handler (audio_vm_stop_handler, NULL);
     }
     else {
-        s->period.ticks = (ticks_per_sec * s->period.usec) / 1000000;
+        qemu_del_timer (s->ts);
+        return NULL;
     }
 
-    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + s->period.ticks);
+    LIST_INIT (&s->card_head);
+    register_savevm ("audio", 0, 1, audio_save, audio_load, s);
+    atexit (audio_atexit);
+    qemu_mod_timer (s->ts, qemu_get_clock (vm_clock) + conf.period.ticks);
+    return s;
 }
diff --git a/audio/audio.h b/audio/audio.h
index 6dd2fd2..682d0e0 100644
--- a/audio/audio.h
+++ b/audio/audio.h
@@ -24,18 +24,33 @@
 #ifndef QEMU_AUDIO_H
 #define QEMU_AUDIO_H
 
+#include "sys-queue.h"
+
 typedef void (*audio_callback_fn_t) (void *opaque, int avail);
 
 typedef enum {
-  AUD_FMT_U8,
-  AUD_FMT_S8,
-  AUD_FMT_U16,
-  AUD_FMT_S16
+    AUD_FMT_U8,
+    AUD_FMT_S8,
+    AUD_FMT_U16,
+    AUD_FMT_S16
 } audfmt_e;
 
+typedef struct {
+    int freq;
+    int nchannels;
+    audfmt_e fmt;
+} audsettings_t;
+
+typedef struct AudioState AudioState;
 typedef struct SWVoiceOut SWVoiceOut;
 typedef struct SWVoiceIn SWVoiceIn;
 
+typedef struct QEMUSoundCard {
+    AudioState *audio;
+    char *name;
+    LIST_ENTRY (QEMUSoundCard) entries;
+} QEMUSoundCard;
+
 typedef struct QEMUAudioTimeStamp {
     uint64_t old_ts;
 } QEMUAudioTimeStamp;
@@ -47,46 +62,45 @@
 #endif
     ;
 
-void AUD_init (void);
+AudioState *AUD_init (void);
 void AUD_help (void);
+void AUD_register_card (AudioState *s, const char *name, QEMUSoundCard *card);
+void AUD_remove_card (QEMUSoundCard *card);
 
-SWVoiceOut  *AUD_open_out (
+SWVoiceOut *AUD_open_out (
+    QEMUSoundCard *card,
     SWVoiceOut *sw,
     const char *name,
     void *callback_opaque,
     audio_callback_fn_t callback_fn,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
+    audsettings_t *settings
     );
-void         AUD_close_out (SWVoiceOut *sw);
-int          AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
-int          AUD_get_buffer_size_out (SWVoiceOut *sw);
-void         AUD_set_active_out (SWVoiceOut *sw, int on);
-int          AUD_is_active_out (SWVoiceOut *sw);
-void         AUD_init_time_stamp_out (SWVoiceOut *sw,
-                                      QEMUAudioTimeStamp *ts);
-uint64_t     AUD_time_stamp_get_elapsed_usec_out (SWVoiceOut *sw,
-                                                  QEMUAudioTimeStamp *ts);
 
-SWVoiceIn   *AUD_open_in (
+void AUD_close_out (QEMUSoundCard *card, SWVoiceOut *sw);
+int  AUD_write (SWVoiceOut *sw, void *pcm_buf, int size);
+int  AUD_get_buffer_size_out (SWVoiceOut *sw);
+void AUD_set_active_out (SWVoiceOut *sw, int on);
+int  AUD_is_active_out (SWVoiceOut *sw);
+
+void     AUD_init_time_stamp_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_out (SWVoiceOut *sw, QEMUAudioTimeStamp *ts);
+
+SWVoiceIn *AUD_open_in (
+    QEMUSoundCard *card,
     SWVoiceIn *sw,
     const char *name,
     void *callback_opaque,
     audio_callback_fn_t callback_fn,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
+    audsettings_t *settings
     );
-void         AUD_close_in (SWVoiceIn *sw);
-int          AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
-void         AUD_adjust_in (SWVoiceIn *sw, int leftover);
-void         AUD_set_active_in (SWVoiceIn *sw, int on);
-int          AUD_is_active_in (SWVoiceIn *sw);
-void         AUD_init_time_stamp_in (SWVoiceIn *sw,
-                                     QEMUAudioTimeStamp *ts);
-uint64_t     AUD_time_stamp_get_elapsed_usec_in (SWVoiceIn *sw,
-                                                 QEMUAudioTimeStamp *ts);
+
+void AUD_close_in (QEMUSoundCard *card, SWVoiceIn *sw);
+int  AUD_read (SWVoiceIn *sw, void *pcm_buf, int size);
+void AUD_set_active_in (SWVoiceIn *sw, int on);
+int  AUD_is_active_in (SWVoiceIn *sw);
+
+void     AUD_init_time_stamp_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
+uint64_t AUD_get_elapsed_usec_in (SWVoiceIn *sw, QEMUAudioTimeStamp *ts);
 
 static inline void *advance (void *p, int incr)
 {
diff --git a/audio/audio_int.h b/audio/audio_int.h
index 9d28829..6d4c32b 100644
--- a/audio/audio_int.h
+++ b/audio/audio_int.h
@@ -24,16 +24,12 @@
 #ifndef QEMU_AUDIO_INT_H
 #define QEMU_AUDIO_INT_H
 
-#include "sys-queue.h"
-
 #ifdef CONFIG_COREAUDIO
 #define FLOAT_MIXENG
 /* #define RECIPROCAL */
 #endif
 #include "mixeng.h"
 
-int audio_bug (const char *funcname, int cond);
-
 struct audio_pcm_ops;
 
 typedef enum {
@@ -69,7 +65,6 @@
 };
 
 typedef struct HWVoiceOut {
-    int active;
     int enabled;
     int pending_disable;
     int valid;
@@ -78,7 +73,6 @@
     f_sample *clip;
 
     int rpos;
-    int bufsize;
     uint64_t ts_helper;
 
     st_sample_t *mix_buf;
@@ -91,13 +85,11 @@
 
 typedef struct HWVoiceIn {
     int enabled;
-    int active;
     struct audio_pcm_info info;
 
     t_sample *conv;
 
     int wpos;
-    int bufsize;
     int total_samples_captured;
     uint64_t ts_helper;
 
@@ -109,58 +101,6 @@
     LIST_ENTRY (HWVoiceIn) entries;
 } HWVoiceIn;
 
-extern struct audio_driver no_audio_driver;
-extern struct audio_driver oss_audio_driver;
-extern struct audio_driver sdl_audio_driver;
-extern struct audio_driver wav_audio_driver;
-extern struct audio_driver fmod_audio_driver;
-extern struct audio_driver alsa_audio_driver;
-extern struct audio_driver coreaudio_audio_driver;
-extern struct audio_driver dsound_audio_driver;
-extern volume_t nominal_volume;
-
-struct audio_driver {
-    const char *name;
-    const char *descr;
-    struct audio_option *options;
-    void *(*init) (void);
-    void (*fini) (void *);
-    struct audio_pcm_ops *pcm_ops;
-    int can_be_default;
-    int max_voices_out;
-    int max_voices_in;
-    int voice_size_out;
-    int voice_size_in;
-};
-
-typedef struct AudioState {
-    int fixed_settings_out;
-    int fixed_freq_out;
-    int fixed_channels_out;
-    int fixed_fmt_out;
-    int nb_hw_voices_out;
-    int greedy_out;
-
-    int fixed_settings_in;
-    int fixed_freq_in;
-    int fixed_channels_in;
-    int fixed_fmt_in;
-    int nb_hw_voices_in;
-    int greedy_in;
-
-    void *opaque;
-    struct audio_driver *drv;
-
-    QEMUTimer *ts;
-    union {
-        int usec;
-        int64_t ticks;
-    } period;
-
-    int plive;
-} AudioState;
-extern AudioState audio_state;
-
 struct SWVoiceOut {
     struct audio_pcm_info info;
     t_sample *conv;
@@ -192,22 +132,58 @@
     LIST_ENTRY (SWVoiceIn) entries;
 };
 
+struct audio_driver {
+    const char *name;
+    const char *descr;
+    struct audio_option *options;
+    void *(*init) (void);
+    void (*fini) (void *);
+    struct audio_pcm_ops *pcm_ops;
+    int can_be_default;
+    int max_voices_out;
+    int max_voices_in;
+    int voice_size_out;
+    int voice_size_in;
+};
+
 struct audio_pcm_ops {
-    int  (*init_out)(HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt);
+    int  (*init_out)(HWVoiceOut *hw, audsettings_t *as);
     void (*fini_out)(HWVoiceOut *hw);
     int  (*run_out) (HWVoiceOut *hw);
     int  (*write)   (SWVoiceOut *sw, void *buf, int size);
     int  (*ctl_out) (HWVoiceOut *hw, int cmd, ...);
 
-    int  (*init_in) (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt);
+    int  (*init_in) (HWVoiceIn *hw, audsettings_t *as);
     void (*fini_in) (HWVoiceIn *hw);
     int  (*run_in)  (HWVoiceIn *hw);
     int  (*read)    (SWVoiceIn *sw, void *buf, int size);
     int  (*ctl_in)  (HWVoiceIn *hw, int cmd, ...);
 };
 
-void audio_pcm_init_info (struct audio_pcm_info *info, int freq,
-                          int nchannels, audfmt_e fmt, int swap_endian);
+struct AudioState {
+    struct audio_driver *drv;
+    void *drv_opaque;
+
+    QEMUTimer *ts;
+    LIST_HEAD (card_head, QEMUSoundCard) card_head;
+    LIST_HEAD (hw_in_listhead, HWVoiceIn) hw_head_in;
+    LIST_HEAD (hw_out_listhead, HWVoiceOut) hw_head_out;
+    int nb_hw_voices_out;
+    int nb_hw_voices_in;
+};
+
+extern struct audio_driver no_audio_driver;
+extern struct audio_driver oss_audio_driver;
+extern struct audio_driver sdl_audio_driver;
+extern struct audio_driver wav_audio_driver;
+extern struct audio_driver fmod_audio_driver;
+extern struct audio_driver alsa_audio_driver;
+extern struct audio_driver coreaudio_audio_driver;
+extern struct audio_driver dsound_audio_driver;
+extern volume_t nominal_volume;
+
+void audio_pcm_init_info (struct audio_pcm_info *info, audsettings_t *as,
+                          int swap_endian);
 void audio_pcm_info_clear_buf (struct audio_pcm_info *info, void *buf, int len);
 
 int  audio_pcm_sw_write (SWVoiceOut *sw, void *buf, int len);
@@ -217,6 +193,9 @@
 int  audio_pcm_hw_get_live_out (HWVoiceOut *hw);
 int  audio_pcm_hw_get_live_out2 (HWVoiceOut *hw, int *nb_live);
 
+int audio_bug (const char *funcname, int cond);
+void *audio_calloc (const char *funcname, int nmemb, size_t size);
+
 #define VOICE_ENABLE 1
 #define VOICE_DISABLE 2
 
diff --git a/audio/audio_template.h b/audio/audio_template.h
index 25ea72f..d985c2e 100644
--- a/audio/audio_template.h
+++ b/audio/audio_template.h
@@ -32,6 +32,43 @@
 #define SW glue (SWVoice, In)
 #endif
 
+static int glue (audio_pcm_hw_init_, TYPE) (
+    HW *hw,
+    audsettings_t *as
+    )
+{
+    glue (audio_pcm_hw_free_resources_, TYPE) (hw);
+
+    if (glue (hw->pcm_ops->init_, TYPE) (hw, as)) {
+        return -1;
+    }
+
+    if (audio_bug (AUDIO_FUNC, hw->samples <= 0)) {
+        dolog ("hw->samples=%d\n", hw->samples);
+        return -1;
+    }
+
+    LIST_INIT (&hw->sw_head);
+#ifdef DAC
+    hw->clip =
+        mixeng_clip
+#else
+    hw->conv =
+        mixeng_conv
+#endif
+        [hw->info.nchannels == 2]
+        [hw->info.sign]
+        [hw->info.swap_endian]
+        [hw->info.bits == 16];
+
+    if (glue (audio_pcm_hw_alloc_resources_, TYPE) (hw)) {
+        glue (hw->pcm_ops->fini_, TYPE) (hw);
+        return -1;
+    }
+
+    return 0;
+}
+
 static void glue (audio_pcm_sw_fini_, TYPE) (SW *sw)
 {
     glue (audio_pcm_sw_free_resources_, TYPE) (sw);
@@ -51,89 +88,86 @@
     LIST_REMOVE (sw, entries);
 }
 
-static void glue (audio_pcm_hw_fini_, TYPE) (HW *hw)
+static void glue (audio_pcm_hw_gc_, TYPE) (AudioState *s, HW **hwp)
 {
-    if (hw->active) {
+    HW *hw = *hwp;
+
+    if (!hw->sw_head.lh_first) {
+        LIST_REMOVE (hw, entries);
+        glue (s->nb_hw_voices_, TYPE) += 1;
         glue (audio_pcm_hw_free_resources_ ,TYPE) (hw);
         glue (hw->pcm_ops->fini_, TYPE) (hw);
-        memset (hw, 0, glue (audio_state.drv->voice_size_, TYPE));
+        qemu_free (hw);
+        *hwp = NULL;
     }
 }
 
-static void glue (audio_pcm_hw_gc_, TYPE) (HW *hw)
+static HW *glue (audio_pcm_hw_find_any_, TYPE) (AudioState *s, HW *hw)
 {
-    if (!hw->sw_head.lh_first) {
-        glue (audio_pcm_hw_fini_, TYPE) (hw);
-    }
+    return hw ? hw->entries.le_next : s->glue (hw_head_, TYPE).lh_first;
 }
 
-static HW *glue (audio_pcm_hw_find_any_, TYPE) (HW *hw)
+static HW *glue (audio_pcm_hw_find_any_enabled_, TYPE) (AudioState *s, HW *hw)
 {
-    return hw ? hw->entries.le_next : glue (hw_head_, TYPE).lh_first;
-}
-
-static HW *glue (audio_pcm_hw_find_any_active_, TYPE) (HW *hw)
-{
-    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
-        if (hw->active) {
+    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+        if (hw->enabled) {
             return hw;
         }
     }
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_find_any_active_enabled_, TYPE) (HW *hw)
+static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (AudioState *s)
 {
-    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
-        if (hw->active && hw->enabled) {
-            return hw;
-        }
-    }
-    return NULL;
-}
+    if (glue (s->nb_hw_voices_, TYPE)) {
+        struct audio_driver *drv = s->drv;
 
-static HW *glue (audio_pcm_hw_find_any_passive_, TYPE) (HW *hw)
-{
-    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (hw))) {
-        if (!hw->active) {
-            return hw;
+        if (audio_bug (AUDIO_FUNC, !drv)) {
+            dolog ("No host audio driver\n");
+            return NULL;
         }
+
+        HW *hw = audio_calloc (AUDIO_FUNC, 1, glue (drv->voice_size_, TYPE));
+        if (!hw) {
+            dolog ("Can not allocate voice `%s' size %d\n",
+                   drv->name, glue (drv->voice_size_, TYPE));
+        }
+
+        LIST_INSERT_HEAD (&s->glue (hw_head_, TYPE), hw, entries);
+        glue (s->nb_hw_voices_, TYPE) -= 1;
+        return hw;
     }
+
     return NULL;
 }
 
 static HW *glue (audio_pcm_hw_find_specific_, TYPE) (
+    AudioState *s,
     HW *hw,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
+    audsettings_t *as
     )
 {
-    while ((hw = glue (audio_pcm_hw_find_any_active_, TYPE) (hw))) {
-        if (audio_pcm_info_eq (&hw->info, freq, nchannels, fmt)) {
+    while ((hw = glue (audio_pcm_hw_find_any_, TYPE) (s, hw))) {
+        if (audio_pcm_info_eq (&hw->info, as)) {
             return hw;
         }
     }
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_add_new_, TYPE) (
-    int freq,
-    int nchannels,
-    audfmt_e fmt
-    )
+static HW *glue (audio_pcm_hw_add_new_, TYPE) (AudioState *s, audsettings_t *as)
 {
     HW *hw;
 
-    hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (NULL);
+    hw = glue (audio_pcm_hw_find_any_passive_, TYPE) (s);
     if (hw) {
-        hw->pcm_ops = audio_state.drv->pcm_ops;
+        hw->pcm_ops = s->drv->pcm_ops;
         if (!hw->pcm_ops) {
             return NULL;
         }
 
-        if (glue (audio_pcm_hw_init_, TYPE) (hw, freq, nchannels, fmt)) {
-            glue (audio_pcm_hw_gc_, TYPE) (hw);
+        if (glue (audio_pcm_hw_init_, TYPE) (hw, as)) {
+            glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
             return NULL;
         }
         else {
@@ -144,66 +178,62 @@
     return NULL;
 }
 
-static HW *glue (audio_pcm_hw_add_, TYPE) (
-    int freq,
-    int nchannels,
-    audfmt_e fmt
-    )
+static HW *glue (audio_pcm_hw_add_, TYPE) (AudioState *s, audsettings_t *as)
 {
     HW *hw;
 
-    if (glue (audio_state.greedy_, TYPE)) {
-        hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
+    if (glue (conf.fixed_, TYPE).enabled && glue (conf.fixed_, TYPE).greedy) {
+        hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
         if (hw) {
             return hw;
         }
     }
 
-    hw = glue (audio_pcm_hw_find_specific_, TYPE) (NULL, freq, nchannels, fmt);
+    hw = glue (audio_pcm_hw_find_specific_, TYPE) (s, NULL, as);
     if (hw) {
         return hw;
     }
 
-    hw = glue (audio_pcm_hw_add_new_, TYPE) (freq, nchannels, fmt);
+    hw = glue (audio_pcm_hw_add_new_, TYPE) (s, as);
     if (hw) {
         return hw;
     }
 
-    return glue (audio_pcm_hw_find_any_active_, TYPE) (NULL);
+    return glue (audio_pcm_hw_find_any_, TYPE) (s, NULL);
 }
 
 static SW *glue (audio_pcm_create_voice_pair_, TYPE) (
-    const char *name,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
+    AudioState *s,
+    const char *sw_name,
+    audsettings_t *as
     )
 {
     SW *sw;
     HW *hw;
-    int hw_freq = freq;
-    int hw_nchannels = nchannels;
-    int hw_fmt = fmt;
+    audsettings_t hw_as;
 
-    if (glue (audio_state.fixed_settings_, TYPE)) {
-        hw_freq = glue (audio_state.fixed_freq_, TYPE);
-        hw_nchannels = glue (audio_state.fixed_channels_, TYPE);
-        hw_fmt = glue (audio_state.fixed_fmt_, TYPE);
+    if (glue (conf.fixed_, TYPE).enabled) {
+        hw_as = glue (conf.fixed_, TYPE).settings;
+    }
+    else {
+        hw_as = *as;
     }
 
-    sw = qemu_mallocz (sizeof (*sw));
+    sw = audio_calloc (AUDIO_FUNC, 1, sizeof (*sw));
     if (!sw) {
+        dolog ("Could not allocate soft voice `%s' (%d bytes)\n",
+               sw_name ? sw_name : "unknown", sizeof (*sw));
         goto err1;
     }
 
-    hw = glue (audio_pcm_hw_add_, TYPE) (hw_freq, hw_nchannels, hw_fmt);
+    hw = glue (audio_pcm_hw_add_, TYPE) (s, &hw_as);
     if (!hw) {
         goto err2;
     }
 
     glue (audio_pcm_hw_add_sw_, TYPE) (hw, sw);
 
-    if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, freq, nchannels, fmt)) {
+    if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, sw_name, as)) {
         goto err3;
     }
 
@@ -211,67 +241,86 @@
 
 err3:
     glue (audio_pcm_hw_del_sw_, TYPE) (sw);
-    glue (audio_pcm_hw_gc_, TYPE) (hw);
+    glue (audio_pcm_hw_gc_, TYPE) (s, &hw);
 err2:
     qemu_free (sw);
 err1:
     return NULL;
 }
 
-void glue (AUD_close_, TYPE) (SW *sw)
+static void glue (audio_close_, TYPE) (AudioState *s, SW *sw)
+{
+    glue (audio_pcm_sw_fini_, TYPE) (sw);
+    glue (audio_pcm_hw_del_sw_, TYPE) (sw);
+    glue (audio_pcm_hw_gc_, TYPE) (s, &sw->hw);
+    qemu_free (sw);
+}
+void glue (AUD_close_, TYPE) (QEMUSoundCard *card, SW *sw)
 {
     if (sw) {
-        glue (audio_pcm_sw_fini_, TYPE) (sw);
-        glue (audio_pcm_hw_del_sw_, TYPE) (sw);
-        glue (audio_pcm_hw_gc_, TYPE) (sw->hw);
-        qemu_free (sw);
+        if (audio_bug (AUDIO_FUNC, !card || !card->audio)) {
+            dolog ("card=%p card->audio=%p\n",
+                   card, card ? card->audio : NULL);
+            return;
+        }
+
+        glue (audio_close_, TYPE) (card->audio, sw);
     }
 }
 
 SW *glue (AUD_open_, TYPE) (
+    QEMUSoundCard *card,
     SW *sw,
     const char *name,
     void *callback_opaque ,
     audio_callback_fn_t callback_fn,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
+    audsettings_t *as
     )
 {
+    AudioState *s;
 #ifdef DAC
     int live = 0;
     SW *old_sw = NULL;
 #endif
 
-    if (!callback_fn) {
-        dolog ("No callback specifed for voice `%s'\n", name);
+    ldebug ("open %s, freq %d, nchannels %d, fmt %d\n",
+            name, as->freq, as->nchannels, as->fmt);
+
+    if (audio_bug (AUDIO_FUNC,
+                   !card || !card->audio || !name || !callback_fn || !as)) {
+        dolog ("card=%p card->audio=%p name=%p callback_fn=%p as=%p\n",
+               card, card ? card->audio : NULL, name, callback_fn, as);
         goto fail;
     }
 
-    if (nchannels != 1 && nchannels != 2) {
-        dolog ("Bogus channel count %d for voice `%s'\n", nchannels, name);
+    s = card->audio;
+
+    if (audio_bug (AUDIO_FUNC, audio_validate_settigs (as))) {
+        audio_print_settings (as);
         goto fail;
     }
 
-    if (!audio_state.drv) {
-        dolog ("No audio driver defined\n");
+    if (audio_bug (AUDIO_FUNC, !s->drv)) {
+        dolog ("Can not open `%s' (no host audio driver)\n", name);
         goto fail;
     }
 
-    if (sw && audio_pcm_info_eq (&sw->info, freq, nchannels, fmt)) {
+    if (sw && audio_pcm_info_eq (&sw->info, as)) {
         return sw;
     }
 
 #ifdef DAC
-    if (audio_state.plive && sw && (!sw->active && !sw->empty)) {
+    if (conf.plive && sw && (!sw->active && !sw->empty)) {
         live = sw->total_hw_samples_mixed;
 
 #ifdef DEBUG_PLIVE
-        dolog ("Replacing voice %s with %d live samples\n", sw->name, live);
+        dolog ("Replacing voice %s with %d live samples\n", SW_NAME (sw), live);
         dolog ("Old %s freq %d, bits %d, channels %d\n",
-               sw->name, sw->info.freq, sw->info.bits, sw->info.nchannels);
+               SW_NAME (sw), sw->info.freq, sw->info.bits, sw->info.nchannels);
         dolog ("New %s freq %d, bits %d, channels %d\n",
-               name, freq, (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
+               name,
+               freq,
+               (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) ? 16 : 8,
                nchannels);
 #endif
 
@@ -283,8 +332,8 @@
     }
 #endif
 
-    if (!glue (audio_state.fixed_settings_, TYPE) && sw) {
-        glue (AUD_close_, TYPE) (sw);
+    if (!glue (conf.fixed_, TYPE).enabled && sw) {
+        glue (AUD_close_, TYPE) (card, sw);
         sw = NULL;
     }
 
@@ -292,30 +341,19 @@
         HW *hw = sw->hw;
 
         if (!hw) {
-            dolog ("Internal logic error voice %s has no hardware store\n",
-                   name);
+            dolog ("Internal logic error voice `%s' has no hardware store\n",
+                   SW_NAME (sw));
             goto fail;
         }
 
-        if (glue (audio_pcm_sw_init_, TYPE) (
-                sw,
-                hw,
-                name,
-                freq,
-                nchannels,
-                fmt
-                )) {
+        if (glue (audio_pcm_sw_init_, TYPE) (sw, hw, name, as)) {
             goto fail;
         }
     }
     else {
-        sw = glue (audio_pcm_create_voice_pair_, TYPE) (
-            name,
-            freq,
-            nchannels,
-            fmt);
+        sw = glue (audio_pcm_create_voice_pair_, TYPE) (s, name, as);
         if (!sw) {
-            dolog ("Failed to create voice %s\n", name);
+            dolog ("Failed to create voice `%s'\n", name);
             goto fail;
         }
     }
@@ -349,7 +387,7 @@
     return sw;
 
  fail:
-    glue (AUD_close_, TYPE) (sw);
+    glue (AUD_close_, TYPE) (card, sw);
     return NULL;
 }
 
@@ -367,10 +405,7 @@
     ts->old_ts = sw->hw->ts_helper;
 }
 
-uint64_t glue (AUD_time_stamp_get_elapsed_usec_, TYPE) (
-    SW *sw,
-    QEMUAudioTimeStamp *ts
-    )
+uint64_t glue (AUD_get_elapsed_usec_, TYPE) (SW *sw, QEMUAudioTimeStamp *ts)
 {
     uint64_t delta, cur_ts, old_ts;
 
diff --git a/audio/coreaudio.c b/audio/coreaudio.c
index eee1238..8551938 100644
--- a/audio/coreaudio.c
+++ b/audio/coreaudio.c
@@ -31,8 +31,6 @@
 #define AUDIO_CAP "coreaudio"
 #include "audio_int.h"
 
-#define DEVICE_BUFFER_FRAMES  (512)
-
 struct {
     int buffer_frames;
 } conf = {
@@ -132,7 +130,7 @@
 {
     va_list ap;
 
-    AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 
     va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
@@ -147,7 +145,7 @@
 
     err = pthread_mutex_lock (&core->mutex);
     if (err) {
-        dolog ("Can not lock voice for %s\nReason: %s\n",
+        dolog ("Could not lock voice for %s\nReason: %s\n",
                fn_name, strerror (err));
         return -1;
     }
@@ -160,7 +158,7 @@
 
     err = pthread_mutex_unlock (&core->mutex);
     if (err) {
-        dolog ("Can not unlock voice for %s\nReason: %s\n",
+        dolog ("Could not unlock voice for %s\nReason: %s\n",
                fn_name, strerror (err));
         return -1;
     }
@@ -268,8 +266,7 @@
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int coreaudio_init_out (HWVoiceOut *hw, int freq,
-                               int nchannels, audfmt_e fmt)
+static int coreaudio_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     OSStatus status;
     coreaudioVoiceOut *core = (coreaudioVoiceOut *) hw;
@@ -282,25 +279,22 @@
     /* create mutex */
     err = pthread_mutex_init(&core->mutex, NULL);
     if (err) {
-        dolog("Can not create mutex\nReason: %s\n", strerror (err));
+        dolog("Could not create mutex\nReason: %s\n", strerror (err));
         return -1;
     }
 
-    if (fmt == AUD_FMT_S16 || fmt == AUD_FMT_U16) {
+    if (as->fmt == AUD_FMT_S16 || as->fmt == AUD_FMT_U16) {
         bits = 16;
         endianess = 1;
     }
 
     audio_pcm_init_info (
         &hw->info,
-        freq,
-        nchannels,
-        fmt,
+        as,
         /* Following is irrelevant actually since we do not use
            mixengs clipping routines */
         audio_need_to_swap_endian (endianess)
         );
-    hw->bufsize = 4 * conf.buffer_frames * nchannels * bits;
 
     /* open default output device */
     propertySize = sizeof(core->outputDeviceID);
@@ -310,18 +304,18 @@
         &core->outputDeviceID);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Can not get default output Device\n");
+                           "Could not get default output Device\n");
         return -1;
     }
     if (core->outputDeviceID == kAudioDeviceUnknown) {
-        dolog ("Can not initialize %s - Unknown Audiodevice\n", typ);
+        dolog ("Could not initialize %s - Unknown Audiodevice\n", typ);
         return -1;
     }
 
     /* set Buffersize to conf.buffer_frames frames */
     propertySize = sizeof(core->audioDevicePropertyBufferSize);
     core->audioDevicePropertyBufferSize =
-        conf.buffer_frames * sizeof(float) * 2;
+        conf.buffer_frames * sizeof(float) << (as->nchannels == 2);
     status = AudioDeviceSetProperty(
         core->outputDeviceID,
         NULL,
@@ -332,7 +326,7 @@
         &core->audioDevicePropertyBufferSize);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Can not set device buffer size %d\n",
+                           "Could not set device buffer size %d\n",
                            kAudioDevicePropertyBufferSize);
         return -1;
     }
@@ -347,9 +341,11 @@
         &propertySize,
         &core->audioDevicePropertyBufferSize);
     if (status != kAudioHardwareNoError) {
-        coreaudio_logerr2 (status, typ, "Can not get device buffer size\n");
+        coreaudio_logerr2 (status, typ, "Could not get device buffer size\n");
         return -1;
     }
+    hw->samples = (core->audioDevicePropertyBufferSize / sizeof (float))
+        >> (as->nchannels == 2);
 
     /* get StreamFormat */
     propertySize = sizeof(core->outputStreamBasicDescription);
@@ -362,13 +358,13 @@
         &core->outputStreamBasicDescription);
     if (status != kAudioHardwareNoError) {
         coreaudio_logerr2 (status, typ,
-                           "Can not get Device Stream properties\n");
+                           "Could not get Device Stream properties\n");
         core->outputDeviceID = kAudioDeviceUnknown;
         return -1;
     }
 
     /* set Samplerate */
-    core->outputStreamBasicDescription.mSampleRate = (Float64)freq;
+    core->outputStreamBasicDescription.mSampleRate = (Float64)as->freq;
     propertySize = sizeof(core->outputStreamBasicDescription);
     status = AudioDeviceSetProperty(
         core->outputDeviceID,
@@ -379,7 +375,7 @@
         propertySize,
         &core->outputStreamBasicDescription);
     if (status != kAudioHardwareNoError) {
-        coreaudio_logerr2 (status, typ, "Can not set samplerate %d\n", freq);
+        coreaudio_logerr2 (status, typ, "Could not set samplerate %d\n", freq);
         core->outputDeviceID = kAudioDeviceUnknown;
         return -1;
     }
@@ -387,7 +383,7 @@
     /* set Callback */
     status = AudioDeviceAddIOProc(core->outputDeviceID, audioDeviceIOProc, hw);
     if (status != kAudioHardwareNoError) {
-        coreaudio_logerr2 (status, typ, "Can not set IOProc\n");
+        coreaudio_logerr2 (status, typ, "Could not set IOProc\n");
         core->outputDeviceID = kAudioDeviceUnknown;
         return -1;
     }
@@ -396,7 +392,7 @@
     if (!core->isPlaying) {
         status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
         if (status != kAudioHardwareNoError) {
-            coreaudio_logerr2 (status, typ, "Can not start playback\n");
+            coreaudio_logerr2 (status, typ, "Could not start playback\n");
             AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
             core->outputDeviceID = kAudioDeviceUnknown;
             return -1;
@@ -417,7 +413,7 @@
     if (core->isPlaying) {
         status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
         if (status != kAudioHardwareNoError) {
-            coreaudio_logerr (status, "Can not stop playback\n");
+            coreaudio_logerr (status, "Could not stop playback\n");
         }
         core->isPlaying = 0;
     }
@@ -425,14 +421,14 @@
     /* remove callback */
     status = AudioDeviceRemoveIOProc(core->outputDeviceID, audioDeviceIOProc);
     if (status != kAudioHardwareNoError) {
-        coreaudio_logerr (status, "Can not remove IOProc\n");
+        coreaudio_logerr (status, "Could not remove IOProc\n");
     }
     core->outputDeviceID = kAudioDeviceUnknown;
 
     /* destroy mutex */
     err = pthread_mutex_destroy(&core->mutex);
     if (err) {
-        dolog("Can not destroy mutex\nReason: %s\n", strerror (err));
+        dolog("Could not destroy mutex\nReason: %s\n", strerror (err));
     }
 }
 
@@ -447,7 +443,7 @@
         if (!core->isPlaying) {
             status = AudioDeviceStart(core->outputDeviceID, audioDeviceIOProc);
             if (status != kAudioHardwareNoError) {
-                coreaudio_logerr (status, "Can not unpause playback\n");
+                coreaudio_logerr (status, "Could not unpause playback\n");
             }
             core->isPlaying = 1;
         }
@@ -458,7 +454,7 @@
         if (core->isPlaying) {
             status = AudioDeviceStop(core->outputDeviceID, audioDeviceIOProc);
             if (status != kAudioHardwareNoError) {
-                coreaudio_logerr (status, "Can not pause playback\n");
+                coreaudio_logerr (status, "Could not pause playback\n");
             }
             core->isPlaying = 0;
         }
diff --git a/audio/dsound_template.h b/audio/dsound_template.h
index a04806e..38ba5b9 100644
--- a/audio/dsound_template.h
+++ b/audio/dsound_template.h
@@ -47,7 +47,7 @@
 
     hr = glue (IFACE, _Unlock) (buf, p1, blen1, p2, blen2);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not unlock " NAME "\n");
+        dsound_logerr (hr, "Could not unlock " NAME "\n");
         return -1;
     }
 
@@ -93,13 +93,13 @@
 #ifndef DSBTYPE_IN
             if (hr == DSERR_BUFFERLOST) {
                 if (glue (dsound_restore_, TYPE) (buf)) {
-                    dsound_logerr (hr, "Can not lock " NAME "\n");
+                    dsound_logerr (hr, "Could not lock " NAME "\n");
                     goto fail;
                 }
                 continue;
             }
 #endif
-            dsound_logerr (hr, "Can not lock " NAME "\n");
+            dsound_logerr (hr, "Could not lock " NAME "\n");
             goto fail;
         }
 
@@ -158,38 +158,28 @@
     if (ds->FIELD) {
         hr = glue (IFACE, _Stop) (ds->FIELD);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not stop " NAME "\n");
+            dsound_logerr (hr, "Could not stop " NAME "\n");
         }
 
         hr = glue (IFACE, _Release) (ds->FIELD);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not release " NAME "\n");
+            dsound_logerr (hr, "Could not release " NAME "\n");
         }
         ds->FIELD = NULL;
     }
 }
 
 #ifdef DSBTYPE_IN
-static int dsound_init_in (
-    HWVoiceIn *hw,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
-    )
+static int dsound_init_in (HWVoiceIn *hw, audsettings_t *as)
 #else
-static int dsound_init_out (
-    HWVoiceOut *hw,
-    int freq,
-    int nchannels,
-    audfmt_e fmt
-    )
+static int dsound_init_out (HWVoiceOut *hw, audsettings_t *as)
 #endif
 {
     int err;
     HRESULT hr;
     dsound *s = &glob_dsound;
     WAVEFORMATEX wfx;
-    struct full_fmt full_fmt;
+    audsettings_t obt_as;
 #ifdef DSBTYPE_IN
     const char *typ = "ADC";
     DSoundVoiceIn *ds = (DSoundVoiceIn *) hw;
@@ -202,10 +192,7 @@
     DSBCAPS bc;
 #endif
 
-    full_fmt.freq = freq;
-    full_fmt.nchannels = nchannels;
-    full_fmt.fmt = fmt;
-    err = waveformat_from_full_fmt (&wfx, &full_fmt);
+    err = waveformat_from_audio_settings (&wfx, as);
     if (err) {
         return -1;
     }
@@ -233,18 +220,13 @@
 #endif
 
     if (FAILED (hr)) {
-        dsound_logerr2 (hr, typ, "Can not create " NAME "\n");
+        dsound_logerr2 (hr, typ, "Could not create " NAME "\n");
         return -1;
     }
 
-    hr = glue (IFACE, _GetFormat) (
-        ds->FIELD,
-        &wfx,
-        sizeof (wfx),
-        NULL
-        );
+    hr = glue (IFACE, _GetFormat) (ds->FIELD, &wfx, sizeof (wfx), NULL);
     if (FAILED (hr)) {
-        dsound_logerr2 (hr, typ, "Can not get " NAME " format\n");
+        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
         goto fail0;
     }
 
@@ -258,31 +240,33 @@
 
     hr = glue (IFACE, _GetCaps) (ds->FIELD, &bc);
     if (FAILED (hr)) {
-        dsound_logerr2 (hr, typ, "Can not get " NAME " format\n");
+        dsound_logerr2 (hr, typ, "Could not get " NAME " format\n");
         goto fail0;
     }
 
-    err = waveformat_to_full_fmt (&wfx, &full_fmt);
+    err = waveformat_to_audio_settings (&wfx, &obt_as);
     if (err) {
         goto fail0;
     }
 
     ds->first_time = 1;
-    hw->bufsize = bc.dwBufferBytes;
-    audio_pcm_init_info (
-        &hw->info,
-        full_fmt.freq,
-        full_fmt.nchannels,
-        full_fmt.fmt,
-        audio_need_to_swap_endian (0)
-        );
+
+    audio_pcm_init_info (&hw->info, &obt_as, audio_need_to_swap_endian (0));
+
+    if (bc.dwBufferBytes & hw->info.align) {
+        dolog (
+            "GetCaps returned misaligned buffer size %ld, alignment %d\n",
+            bc.dwBufferBytes, hw->info.align + 1
+            );
+    }
+    hw->samples = bc.dwBufferBytes >> hw->info.shift;
 
 #ifdef DEBUG_DSOUND
     dolog ("caps %ld, desc %ld\n",
            bc.dwBufferBytes, bd.dwBufferBytes);
 
     dolog ("bufsize %d, freq %d, chan %d, fmt %d\n",
-           hw->bufsize, full_fmt.freq, full_fmt.nchannels, full_fmt.fmt);
+           hw->bufsize, settings.freq, settings.nchannels, settings.fmt);
 #endif
     return 0;
 
diff --git a/audio/dsoundaudio.c b/audio/dsoundaudio.c
index 64b8417..63c5a50 100644
--- a/audio/dsoundaudio.c
+++ b/audio/dsoundaudio.c
@@ -37,12 +37,6 @@
 
 /* #define DEBUG_DSOUND */
 
-struct full_fmt {
-    int freq;
-    int nchannels;
-    audfmt_e fmt;
-};
-
 static struct {
     int lock_retries;
     int restore_retries;
@@ -50,7 +44,7 @@
     int set_primary;
     int bufsize_in;
     int bufsize_out;
-    struct full_fmt full_fmt;
+    audsettings_t settings;
     int latency_millis;
 } conf = {
     1,
@@ -71,7 +65,7 @@
     LPDIRECTSOUND dsound;
     LPDIRECTSOUNDCAPTURE dsound_capture;
     LPDIRECTSOUNDBUFFER dsound_primary_buffer;
-    struct full_fmt fmt;
+    audsettings_t settings;
 } dsound;
 
 static dsound glob_dsound;
@@ -259,7 +253,7 @@
 {
     va_list ap;
 
-    AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
     va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
     va_end (ap);
@@ -301,7 +295,7 @@
             continue;
 
         default:
-            dsound_logerr (hr, "Can not restore playback buffer\n");
+            dsound_logerr (hr, "Could not restore playback buffer\n");
             return -1;
         }
     }
@@ -310,19 +304,18 @@
     return -1;
 }
 
-static int waveformat_from_full_fmt (WAVEFORMATEX *wfx,
-                                     struct full_fmt *full_fmt)
+static int waveformat_from_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
 {
     memset (wfx, 0, sizeof (*wfx));
 
     wfx->wFormatTag = WAVE_FORMAT_PCM;
-    wfx->nChannels = full_fmt->nchannels;
-    wfx->nSamplesPerSec = full_fmt->freq;
-    wfx->nAvgBytesPerSec = full_fmt->freq << (full_fmt->nchannels == 2);
-    wfx->nBlockAlign = 1 << (full_fmt->nchannels == 2);
+    wfx->nChannels = as->nchannels;
+    wfx->nSamplesPerSec = as->freq;
+    wfx->nAvgBytesPerSec = as->freq << (as->nchannels == 2);
+    wfx->nBlockAlign = 1 << (as->nchannels == 2);
     wfx->cbSize = 0;
 
-    switch (full_fmt->fmt) {
+    switch (as->fmt) {
     case AUD_FMT_S8:
         wfx->wBitsPerSample = 8;
         break;
@@ -344,16 +337,14 @@
         break;
 
     default:
-        dolog ("Internal logic error: Bad audio format %d\n",
-               full_fmt->freq);
+        dolog ("Internal logic error: Bad audio format %d\n", as->freq);
         return -1;
     }
 
     return 0;
 }
 
-static int waveformat_to_full_fmt (WAVEFORMATEX *wfx,
-                                   struct full_fmt *full_fmt)
+static int waveformat_to_audio_settings (WAVEFORMATEX *wfx, audsettings_t *as)
 {
     if (wfx->wFormatTag != WAVE_FORMAT_PCM) {
         dolog ("Invalid wave format, tag is not PCM, but %d\n",
@@ -365,15 +356,15 @@
         dolog ("Invalid wave format, frequency is zero\n");
         return -1;
     }
-    full_fmt->freq = wfx->nSamplesPerSec;
+    as->freq = wfx->nSamplesPerSec;
 
     switch (wfx->nChannels) {
     case 1:
-        full_fmt->nchannels = 1;
+        as->nchannels = 1;
         break;
 
     case 2:
-        full_fmt->nchannels = 2;
+        as->nchannels = 2;
         break;
 
     default:
@@ -386,11 +377,11 @@
 
     switch (wfx->wBitsPerSample) {
     case 8:
-        full_fmt->fmt = AUD_FMT_U8;
+        as->fmt = AUD_FMT_U8;
         break;
 
     case 16:
-        full_fmt->fmt = AUD_FMT_S16;
+        as->fmt = AUD_FMT_S16;
         break;
 
     default:
@@ -415,7 +406,7 @@
     for (i = 0; i < conf.getstatus_retries; ++i) {
         hr = IDirectSoundBuffer_GetStatus (dsb, statusp);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not get playback buffer status\n");
+            dsound_logerr (hr, "Could not get playback buffer status\n");
             return -1;
         }
 
@@ -438,7 +429,7 @@
 
     hr = IDirectSoundCaptureBuffer_GetStatus (dscb, statusp);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not get capture buffer status\n");
+        dsound_logerr (hr, "Could not get capture buffer status\n");
         return -1;
     }
 
@@ -520,7 +511,7 @@
     if (s->dsound_primary_buffer) {
         hr = IDirectSoundBuffer_Release (s->dsound_primary_buffer);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not release primary buffer\n");
+            dsound_logerr (hr, "Could not release primary buffer\n");
         }
         s->dsound_primary_buffer = NULL;
     }
@@ -542,7 +533,7 @@
         );
 
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not set cooperative level for window %p\n",
+        dsound_logerr (hr, "Could not set cooperative level for window %p\n",
                        hwnd);
         return -1;
     }
@@ -551,7 +542,7 @@
         return 0;
     }
 
-    err = waveformat_from_full_fmt (&wfx, &conf.full_fmt);
+    err = waveformat_from_audio_settings (&wfx, &conf.settings);
     if (err) {
         return -1;
     }
@@ -569,13 +560,13 @@
         NULL
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not create primary playback buffer\n");
+        dsound_logerr (hr, "Could not create primary playback buffer\n");
         return -1;
     }
 
     hr = IDirectSoundBuffer_SetFormat (s->dsound_primary_buffer, &wfx);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not set primary playback buffer format\n");
+        dsound_logerr (hr, "Could not set primary playback buffer format\n");
     }
 
     hr = IDirectSoundBuffer_GetFormat (
@@ -585,7 +576,7 @@
         NULL
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not get primary playback buffer format\n");
+        dsound_logerr (hr, "Could not get primary playback buffer format\n");
         goto fail0;
     }
 
@@ -594,7 +585,7 @@
     print_wave_format (&wfx);
 #endif
 
-    err = waveformat_to_full_fmt (&wfx, &s->fmt);
+    err = waveformat_to_audio_settings (&wfx, &s->settings);
     if (err) {
         goto fail0;
     }
@@ -625,7 +616,7 @@
         }
 
         if (status & DSBSTATUS_PLAYING) {
-            dolog ("warning: voice is already playing\n");
+            dolog ("warning: Voice is already playing\n");
             return 0;
         }
 
@@ -633,7 +624,7 @@
 
         hr = IDirectSoundBuffer_Play (dsb, 0, 0, DSBPLAY_LOOPING);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not start playing buffer\n");
+            dsound_logerr (hr, "Could not start playing buffer\n");
             return -1;
         }
         break;
@@ -646,12 +637,12 @@
         if (status & DSBSTATUS_PLAYING) {
             hr = IDirectSoundBuffer_Stop (dsb);
             if (FAILED (hr)) {
-                dsound_logerr (hr, "Can not stop playing buffer\n");
+                dsound_logerr (hr, "Could not stop playing buffer\n");
                 return -1;
             }
         }
         else {
-            dolog ("warning: voice is not playing\n");
+            dolog ("warning: Voice is not playing\n");
         }
         break;
     }
@@ -675,6 +666,7 @@
     DWORD decr;
     DWORD wpos, ppos, old_pos;
     LPVOID p1, p2;
+    int bufsize;
 
     if (!dsb) {
         dolog ("Attempt to run empty with playback buffer\n");
@@ -682,6 +674,7 @@
     }
 
     hwshift = hw->info.shift;
+    bufsize = hw->samples << hwshift;
 
     live = audio_pcm_hw_get_live_out (hw);
 
@@ -691,7 +684,7 @@
         ds->first_time ? &wpos : NULL
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not get playback buffer position\n");
+        dsound_logerr (hr, "Could not get playback buffer position\n");
         return 0;
     }
 
@@ -699,13 +692,14 @@
 
     if (ds->first_time) {
         if (conf.latency_millis) {
-            DWORD cur_blat = audio_ring_dist (wpos, ppos, hw->bufsize);
+            DWORD cur_blat;
 
+            cur_blat = audio_ring_dist (wpos, ppos, bufsize);
             ds->first_time = 0;
             old_pos = wpos;
             old_pos +=
                 millis_to_bytes (&hw->info, conf.latency_millis) - cur_blat;
-            old_pos %= hw->bufsize;
+            old_pos %= bufsize;
             old_pos &= ~hw->info.align;
         }
         else {
@@ -734,14 +728,14 @@
         len = ppos - old_pos;
     }
     else {
-        if ((old_pos > ppos) && ((old_pos + len) > (ppos + hw->bufsize))) {
-            len = hw->bufsize - old_pos + ppos;
+        if ((old_pos > ppos) && ((old_pos + len) > (ppos + bufsize))) {
+            len = bufsize - old_pos + ppos;
         }
     }
 
-    if (audio_bug (AUDIO_FUNC, len < 0 || len > hw->bufsize)) {
-        dolog ("len=%d hw->bufsize=%d old_pos=%ld ppos=%ld\n",
-               len, hw->bufsize, old_pos, ppos);
+    if (audio_bug (AUDIO_FUNC, len < 0 || len > bufsize)) {
+        dolog ("len=%d bufsize=%d old_pos=%ld ppos=%ld\n",
+               len, bufsize, old_pos, ppos);
         return 0;
     }
 
@@ -779,7 +773,7 @@
     }
 
     dsound_unlock_out (dsb, p1, p2, blen1, blen2);
-    ds->old_pos = (old_pos + (decr << hwshift)) % hw->bufsize;
+    ds->old_pos = (old_pos + (decr << hwshift)) % bufsize;
 
 #ifdef DEBUG_DSOUND
     ds->mixed += decr << hwshift;
@@ -812,7 +806,7 @@
         }
 
         if (status & DSCBSTATUS_CAPTURING) {
-            dolog ("warning: voice is already capturing\n");
+            dolog ("warning: Voice is already capturing\n");
             return 0;
         }
 
@@ -820,7 +814,7 @@
 
         hr = IDirectSoundCaptureBuffer_Start (dscb, DSCBSTART_LOOPING);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not start capturing\n");
+            dsound_logerr (hr, "Could not start capturing\n");
             return -1;
         }
         break;
@@ -833,12 +827,12 @@
         if (status & DSCBSTATUS_CAPTURING) {
             hr = IDirectSoundCaptureBuffer_Stop (dscb);
             if (FAILED (hr)) {
-                dsound_logerr (hr, "Can not stop capturing\n");
+                dsound_logerr (hr, "Could not stop capturing\n");
                 return -1;
             }
         }
         else {
-            dolog ("warning: voice is not capturing\n");
+            dolog ("warning: Voice is not capturing\n");
         }
         break;
     }
@@ -883,21 +877,21 @@
         ds->first_time ? &rpos : NULL
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not get capture buffer position\n");
+        dsound_logerr (hr, "Could not get capture buffer position\n");
         return 0;
     }
 
     if (ds->first_time) {
         ds->first_time = 0;
         if (rpos & hw->info.align) {
-            ldebug ("warning: misaligned capture read position %ld(%d)\n",
+            ldebug ("warning: Misaligned capture read position %ld(%d)\n",
                     rpos, hw->info.align);
         }
         hw->wpos = rpos >> hwshift;
     }
 
     if (cpos & hw->info.align) {
-        ldebug ("warning: misaligned capture position %ld(%d)\n",
+        ldebug ("warning: Misaligned capture position %ld(%d)\n",
                 cpos, hw->info.align);
     }
     cpos >>= hwshift;
@@ -951,7 +945,7 @@
 
     hr = IDirectSound_Release (s->dsound);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not release DirectSound\n");
+        dsound_logerr (hr, "Could not release DirectSound\n");
     }
     s->dsound = NULL;
 
@@ -961,7 +955,7 @@
 
     hr = IDirectSoundCapture_Release (s->dsound_capture);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not release DirectSoundCapture\n");
+        dsound_logerr (hr, "Could not release DirectSoundCapture\n");
     }
     s->dsound_capture = NULL;
 }
@@ -974,7 +968,7 @@
 
     hr = CoInitialize (NULL);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not initialize COM\n");
+        dsound_logerr (hr, "Could not initialize COM\n");
         return NULL;
     }
 
@@ -986,13 +980,13 @@
         (void **) &s->dsound
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not create DirectSound instance\n");
+        dsound_logerr (hr, "Could not create DirectSound instance\n");
         return NULL;
     }
 
     hr = IDirectSound_Initialize (s->dsound, NULL);
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not initialize DirectSound\n");
+        dsound_logerr (hr, "Could not initialize DirectSound\n");
         return NULL;
     }
 
@@ -1004,16 +998,16 @@
         (void **) &s->dsound_capture
         );
     if (FAILED (hr)) {
-        dsound_logerr (hr, "Can not create DirectSoundCapture instance\n");
+        dsound_logerr (hr, "Could not create DirectSoundCapture instance\n");
     }
     else {
         hr = IDirectSoundCapture_Initialize (s->dsound_capture, NULL);
         if (FAILED (hr)) {
-            dsound_logerr (hr, "Can not initialize DirectSoundCapture\n");
+            dsound_logerr (hr, "Could not initialize DirectSoundCapture\n");
 
             hr = IDirectSoundCapture_Release (s->dsound_capture);
             if (FAILED (hr)) {
-                dsound_logerr (hr, "Can not release DirectSoundCapture\n");
+                dsound_logerr (hr, "Could not release DirectSoundCapture\n");
             }
             s->dsound_capture = NULL;
         }
@@ -1039,11 +1033,11 @@
      "Set the parameters of primary buffer", NULL, 0},
     {"LATENCY_MILLIS", AUD_OPT_INT, &conf.latency_millis,
      "(undocumented)", NULL, 0},
-    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.full_fmt.freq,
+    {"PRIMARY_FREQ", AUD_OPT_INT, &conf.settings.freq,
      "Primary buffer frequency", NULL, 0},
-    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.full_fmt.nchannels,
+    {"PRIMARY_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
      "Primary buffer number of channels (1 - mono, 2 - stereo)", NULL, 0},
-    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.full_fmt.fmt,
+    {"PRIMARY_FMT", AUD_OPT_FMT, &conf.settings.fmt,
      "Primary buffer format", NULL, 0},
     {"BUFSIZE_OUT", AUD_OPT_INT, &conf.bufsize_out,
      "(undocumented)", NULL, 0},
diff --git a/audio/fmodaudio.c b/audio/fmodaudio.c
index 36b8d47..072d8a8 100644
--- a/audio/fmodaudio.c
+++ b/audio/fmodaudio.c
@@ -78,7 +78,7 @@
 {
     va_list ap;
 
-    AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 
     va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
@@ -356,17 +356,17 @@
     }
 }
 
-static int fmod_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
+static int fmod_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     int bits16, mode, channel;
     FMODVoiceOut *fmd = (FMODVoiceOut *) hw;
 
-    mode = aud_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
+    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
     fmd->fmod_sample = FSOUND_Sample_Alloc (
         FSOUND_FREE,            /* index */
         conf.nb_samples,        /* length */
         mode,                   /* mode */
-        freq,                   /* freq */
+        as->freq,               /* freq */
         255,                    /* volume */
         128,                    /* pan */
         255                     /* priority */
@@ -386,10 +386,9 @@
     fmd->channel = channel;
 
     /* FMOD always operates on little endian frames? */
-    audio_pcm_init_info (&hw->info, freq, nchannels, fmt,
-                         audio_need_to_swap_endian (0));
+    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
     bits16 = (mode & FSOUND_16BITS) != 0;
-    hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
+    hw->samples = conf.nb_samples;
     return 0;
 }
 
@@ -417,7 +416,7 @@
     return 0;
 }
 
-static int fmod_init_in (HWVoiceIn *hw, int freq, int nchannels, audfmt_e fmt)
+static int fmod_init_in (HWVoiceIn *hw, audsettings_t *as)
 {
     int bits16, mode;
     FMODVoiceIn *fmd = (FMODVoiceIn *) hw;
@@ -426,12 +425,12 @@
         return -1;
     }
 
-    mode = aud_to_fmodfmt (fmt, nchannels == 2 ? 1 : 0);
+    mode = aud_to_fmodfmt (as->fmt, as->nchannels == 2 ? 1 : 0);
     fmd->fmod_sample = FSOUND_Sample_Alloc (
         FSOUND_FREE,            /* index */
         conf.nb_samples,        /* length */
         mode,                   /* mode */
-        freq,                   /* freq */
+        as->freq,               /* freq */
         255,                    /* volume */
         128,                    /* pan */
         255                     /* priority */
@@ -443,10 +442,9 @@
     }
 
     /* FMOD always operates on little endian frames? */
-    audio_pcm_init_info (&hw->info, freq, nchannels, fmt,
-                         audio_need_to_swap_endian (0));
+    audio_pcm_init_info (&hw->info, as, audio_need_to_swap_endian (0));
     bits16 = (mode & FSOUND_16BITS) != 0;
-    hw->bufsize = conf.nb_samples << (nchannels == 2) << bits16;
+    hw->samples = conf.nb_samples;
     return 0;
 }
 
@@ -479,7 +477,7 @@
 
     new_pos = FSOUND_Record_GetPosition ();
     if (new_pos < 0) {
-        fmod_logerr ("Can not get recording position\n");
+        fmod_logerr ("Could not get recording position\n");
         return 0;
     }
 
diff --git a/audio/mixeng.c b/audio/mixeng.c
index d43c5e5..14e37ae 100644
--- a/audio/mixeng.c
+++ b/audio/mixeng.c
@@ -228,21 +228,22 @@
  */
 
 /* Private data */
-typedef struct ratestuff {
+struct rate {
     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));
+    struct rate *rate = audio_calloc (AUDIO_FUNC, 1, sizeof (*rate));
 
     if (!rate) {
+        dolog ("Could not allocate resampler (%d bytes)\n", sizeof (*rate));
         return NULL;
     }
 
diff --git a/audio/noaudio.c b/audio/noaudio.c
index e7936cc..aa35811 100644
--- a/audio/noaudio.c
+++ b/audio/noaudio.c
@@ -67,11 +67,10 @@
     return audio_pcm_sw_write (sw, buf, len);
 }
 
-static int no_init_out (HWVoiceOut *hw, int freq,
-                        int nchannels, audfmt_e fmt)
+static int no_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
-    audio_pcm_init_info (&hw->info, freq, nchannels, fmt, 0);
-    hw->bufsize = 4096;
+    audio_pcm_init_info (&hw->info, as, 0);
+    hw->samples = 1024;
     return 0;
 }
 
@@ -87,11 +86,10 @@
     return 0;
 }
 
-static int no_init_in (HWVoiceIn *hw, int freq,
-                       int nchannels, audfmt_e fmt)
+static int no_init_in (HWVoiceIn *hw, audsettings_t *as)
 {
-    audio_pcm_init_info (&hw->info, freq, nchannels, fmt, 0);
-    hw->bufsize = 4096;
+    audio_pcm_init_info (&hw->info, as, 0);
+    hw->samples = 1024;
     return 0;
 }
 
diff --git a/audio/ossaudio.c b/audio/ossaudio.c
index ff1a034..5072742 100644
--- a/audio/ossaudio.c
+++ b/audio/ossaudio.c
@@ -91,7 +91,7 @@
 {
     va_list ap;
 
-    AUD_log (AUDIO_CAP, "Can not initialize %s\n", typ);
+    AUD_log (AUDIO_CAP, "Could not initialize %s\n", typ);
 
     va_start (ap, fmt);
     AUD_vlog (AUDIO_CAP, fmt, ap);
@@ -179,7 +179,7 @@
     return 0;
 }
 
-#ifdef DEBUG_MISMATCHES
+#if defined DEBUG_MISMATCHES || defined DEBUG
 static void oss_dump_info (struct oss_params *req, struct oss_params *obt)
 {
     dolog ("parameter | requested value | obtained value\n");
@@ -253,16 +253,16 @@
     obt->fragsize = abinfo.fragsize;
     *pfd = fd;
 
+#ifdef DEBUG_MISMATCHES
     if ((req->fmt != obt->fmt) ||
         (req->nchannels != obt->nchannels) ||
         (req->freq != obt->freq) ||
         (req->fragsize != obt->fragsize) ||
         (req->nfrags != obt->nfrags)) {
-#ifdef DEBUG_MISMATCHES
         dolog ("Audio parameters mismatch\n");
         oss_dump_info (req, obt);
-#endif
     }
+#endif
 
 #ifdef DEBUG
     oss_dump_info (req, obt);
@@ -283,12 +283,15 @@
     st_sample_t *src;
     struct audio_buf_info abinfo;
     struct count_info cntinfo;
+    int bufsize;
 
     live = audio_pcm_hw_get_live_out (hw);
     if (!live) {
         return 0;
     }
 
+    bufsize = hw->samples << hw->info.shift;
+
     if (oss->mmapped) {
         int bytes;
 
@@ -300,7 +303,7 @@
 
         if (cntinfo.ptr == oss->old_optr) {
             if (abs (hw->samples - live) < 64) {
-                dolog ("warning: overrun\n");
+                dolog ("warning: Overrun\n");
             }
             return 0;
         }
@@ -309,7 +312,7 @@
             bytes = cntinfo.ptr - oss->old_optr;
         }
         else {
-            bytes = hw->bufsize + cntinfo.ptr - oss->old_optr;
+            bytes = bufsize + cntinfo.ptr - oss->old_optr;
         }
 
         decr = audio_MIN (bytes >> hw->info.shift, live);
@@ -321,9 +324,9 @@
             return 0;
         }
 
-        if (abinfo.bytes < 0 || abinfo.bytes > hw->bufsize) {
-            ldebug ("warning: invalid available size, size=%d bufsize=%d\n",
-                    abinfo.bytes, hw->bufsize);
+        if (abinfo.bytes < 0 || abinfo.bytes > bufsize) {
+            ldebug ("warning: Invalid available size, size=%d bufsize=%d\n",
+                    abinfo.bytes, bufsize);
             return 0;
         }
 
@@ -362,7 +365,7 @@
                 int wsamples = written >> hw->info.shift;
                 int wbytes = wsamples << hw->info.shift;
                 if (wbytes != written) {
-                    dolog ("warning: misaligned write %d (requested %d), "
+                    dolog ("warning: Misaligned write %d (requested %d), "
                            "alignment %d\n",
                            wbytes, written, hw->info.align + 1);
                 }
@@ -396,10 +399,10 @@
 
     if (oss->pcm_buf) {
         if (oss->mmapped) {
-            err = munmap (oss->pcm_buf, hw->bufsize);
+            err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
             if (err) {
                 oss_logerr (errno, "Failed to unmap buffer %p, size %d\n",
-                            oss->pcm_buf, hw->bufsize);
+                            oss->pcm_buf, hw->samples << hw->info.shift);
             }
         }
         else {
@@ -409,7 +412,7 @@
     }
 }
 
-static int oss_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
+static int oss_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     OSSVoiceOut *oss = (OSSVoiceOut *) hw;
     struct oss_params req, obt;
@@ -417,10 +420,11 @@
     int err;
     int fd;
     audfmt_e effective_fmt;
+    audsettings_t obt_as;
 
-    req.fmt = aud_to_ossfmt (fmt);
-    req.freq = freq;
-    req.nchannels = nchannels;
+    req.fmt = aud_to_ossfmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
     req.fragsize = conf.fragsize;
     req.nfrags = conf.nfrags;
 
@@ -434,24 +438,38 @@
         return -1;
     }
 
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+
     audio_pcm_init_info (
         &hw->info,
-        obt.freq,
-        obt.nchannels,
-        effective_fmt,
+        &obt_as,
         audio_need_to_swap_endian (endianness)
         );
     oss->nfrags = obt.nfrags;
     oss->fragsize = obt.fragsize;
-    hw->bufsize = obt.nfrags * obt.fragsize;
+
+    if (obt.nfrags * obt.fragsize & hw->info.align) {
+        dolog ("warning: Misaligned DAC buffer, size %d, alignment %d\n",
+               obt.nfrags * obt.fragsize, hw->info.align + 1);
+    }
+
+    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
 
     oss->mmapped = 0;
     if (conf.try_mmap) {
-        oss->pcm_buf = mmap (0, hw->bufsize, PROT_READ | PROT_WRITE,
-                             MAP_SHARED, fd, 0);
+        oss->pcm_buf = mmap (
+            0,
+            hw->samples << hw->info.shift,
+            PROT_READ | PROT_WRITE,
+            MAP_SHARED,
+            fd,
+            0
+            );
         if (oss->pcm_buf == MAP_FAILED) {
             oss_logerr (errno, "Failed to map %d bytes of DAC\n",
-                        hw->bufsize);
+                        hw->samples << hw->info.shift);
         } else {
             int err;
             int trig = 0;
@@ -472,18 +490,24 @@
             }
 
             if (!oss->mmapped) {
-                err = munmap (oss->pcm_buf, hw->bufsize);
+                err = munmap (oss->pcm_buf, hw->samples << hw->info.shift);
                 if (err) {
                     oss_logerr (errno, "Failed to unmap buffer %p size %d\n",
-                                oss->pcm_buf, hw->bufsize);
+                                oss->pcm_buf, hw->samples << hw->info.shift);
                 }
             }
         }
     }
 
     if (!oss->mmapped) {
-        oss->pcm_buf = qemu_mallocz (hw->bufsize);
+        oss->pcm_buf = audio_calloc (
+            AUDIO_FUNC,
+            hw->samples,
+            1 << hw->info.shift
+            );
         if (!oss->pcm_buf) {
+            dolog ("Could not allocate DAC buffer (%d bytes)\n",
+                   hw->samples << hw->info.shift);
             oss_anal_close (&fd);
             return -1;
         }
@@ -528,8 +552,7 @@
     return 0;
 }
 
-static int oss_init_in (HWVoiceIn *hw,
-                        int freq, int nchannels, audfmt_e fmt)
+static int oss_init_in (HWVoiceIn *hw, audsettings_t *as)
 {
     OSSVoiceIn *oss = (OSSVoiceIn *) hw;
     struct oss_params req, obt;
@@ -537,10 +560,11 @@
     int err;
     int fd;
     audfmt_e effective_fmt;
+    audsettings_t obt_as;
 
-    req.fmt = aud_to_ossfmt (fmt);
-    req.freq = freq;
-    req.nchannels = nchannels;
+    req.fmt = aud_to_ossfmt (as->fmt);
+    req.freq = as->freq;
+    req.nchannels = as->nchannels;
     req.fragsize = conf.fragsize;
     req.nfrags = conf.nfrags;
     if (oss_open (1, &req, &obt, &fd)) {
@@ -553,18 +577,28 @@
         return -1;
     }
 
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.nchannels;
+    obt_as.fmt = effective_fmt;
+
     audio_pcm_init_info (
         &hw->info,
-        obt.freq,
-        obt.nchannels,
-        effective_fmt,
+        &obt_as,
         audio_need_to_swap_endian (endianness)
         );
     oss->nfrags = obt.nfrags;
     oss->fragsize = obt.fragsize;
-    hw->bufsize = obt.nfrags * obt.fragsize;
-    oss->pcm_buf = qemu_mallocz (hw->bufsize);
+
+    if (obt.nfrags * obt.fragsize & hw->info.align) {
+        dolog ("warning: Misaligned ADC buffer, size %d, alignment %d\n",
+               obt.nfrags * obt.fragsize, hw->info.align + 1);
+    }
+
+    hw->samples = (obt.nfrags * obt.fragsize) >> hw->info.shift;
+    oss->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     if (!oss->pcm_buf) {
+        dolog ("Could not allocate ADC buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
         oss_anal_close (&fd);
         return -1;
     }
@@ -623,7 +657,7 @@
 
             if (nread > 0) {
                 if (nread & hw->info.align) {
-                    dolog ("warning: misaligned read %d (requested %d), "
+                    dolog ("warning: Misaligned read %d (requested %d), "
                            "alignment %d\n", nread, bufs[i].add << hwshift,
                            hw->info.align + 1);
                 }
diff --git a/audio/rate_template.h b/audio/rate_template.h
index 5cc95c8..3e0e77c 100644
--- a/audio/rate_template.h
+++ b/audio/rate_template.h
@@ -30,7 +30,7 @@
 void NAME (void *opaque, st_sample_t *ibuf, st_sample_t *obuf,
            int *isamp, int *osamp)
 {
-    rate_t rate = (rate_t) opaque;
+    struct rate *rate = opaque;
     st_sample_t *istart, *iend;
     st_sample_t *ostart, *oend;
     st_sample_t ilast, icur, out;
diff --git a/audio/sdlaudio.c b/audio/sdlaudio.c
index 673e2a1..713c784 100644
--- a/audio/sdlaudio.c
+++ b/audio/sdlaudio.c
@@ -303,7 +303,7 @@
     sdl_close (&glob_sdl);
 }
 
-static int sdl_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
+static int sdl_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     SDLVoiceOut *sdl = (SDLVoiceOut *) hw;
     SDLAudioState *s = &glob_sdl;
@@ -312,18 +312,14 @@
     int endianess;
     int err;
     audfmt_e effective_fmt;
+    audsettings_t obt_as;
 
-    if (nchannels != 2) {
-        dolog ("Can not init DAC. Bogus channel count %d\n", nchannels);
-        return -1;
-    }
+    shift <<= as->nchannels == 2;
 
-    req.freq = freq;
-    req.format = aud_to_sdlfmt (fmt, &shift);
-    req.channels = nchannels;
+    req.freq = as->freq;
+    req.format = aud_to_sdlfmt (as->fmt, &shift);
+    req.channels = as->nchannels;
     req.samples = conf.nb_samples;
-    shift <<= nchannels == 2;
-
     req.callback = sdl_callback;
     req.userdata = sdl;
 
@@ -337,14 +333,16 @@
         return -1;
     }
 
+    obt_as.freq = obt.freq;
+    obt_as.nchannels = obt.channels;
+    obt_as.fmt = effective_fmt;
+
     audio_pcm_init_info (
         &hw->info,
-        obt.freq,
-        obt.channels,
-        effective_fmt,
+        &obt_as,
         audio_need_to_swap_endian (endianess)
         );
-    hw->bufsize = obt.samples << shift;
+    hw->samples = obt.samples;
 
     s->initialized = 1;
     s->exit = 0;
diff --git a/audio/wavaudio.c b/audio/wavaudio.c
index e9bd878..4cc9ca7 100644
--- a/audio/wavaudio.c
+++ b/audio/wavaudio.c
@@ -35,9 +35,15 @@
 } WAVVoiceOut;
 
 static struct {
+    audsettings_t settings;
     const char *wav_path;
 } conf = {
-    .wav_path = "qemu.wav"
+    {
+        44100,
+        2,
+        AUD_FMT_S16
+    },
+    "qemu.wav"
 };
 
 static int wav_run_out (HWVoiceOut *hw)
@@ -101,22 +107,22 @@
     }
 }
 
-static int wav_init_out (HWVoiceOut *hw, int freq, int nchannels, audfmt_e fmt)
+static int wav_init_out (HWVoiceOut *hw, audsettings_t *as)
 {
     WAVVoiceOut *wav = (WAVVoiceOut *) hw;
-    int bits16;
+    int bits16 = 0, stereo = 0;
     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
     };
+    audsettings_t wav_as = conf.settings;
 
-    freq = audio_state.fixed_freq_out;
-    fmt = audio_state.fixed_fmt_out;
-    nchannels = audio_state.fixed_channels_out;
+    (void) as;
 
-    switch (fmt) {
+    stereo = wav_as.nchannels == 2;
+    switch (wav_as.fmt) {
     case AUD_FMT_S8:
     case AUD_FMT_U8:
         bits16 = 0;
@@ -126,32 +132,24 @@
     case AUD_FMT_U16:
         bits16 = 1;
         break;
-
-    default:
-        dolog ("Internal logic error bad format %d\n", fmt);
-        return -1;
     }
 
     hdr[34] = bits16 ? 0x10 : 0x08;
-    audio_pcm_init_info (
-        &hw->info,
-        freq,
-        nchannels,
-        bits16 ? AUD_FMT_S16 : AUD_FMT_U8,
-        audio_need_to_swap_endian (0)
-        );
-    hw->bufsize = 4096;
-    wav->pcm_buf = qemu_mallocz (hw->bufsize);
+
+    audio_pcm_init_info (&hw->info, &wav_as, audio_need_to_swap_endian (0));
+
+    hw->samples = 1024;
+    wav->pcm_buf = audio_calloc (AUDIO_FUNC, hw->samples, 1 << hw->info.shift);
     if (!wav->pcm_buf) {
-        dolog ("Can not initialize WAV buffer of %d bytes\n",
-               hw->bufsize);
+        dolog ("Could not allocate buffer (%d bytes)\n",
+               hw->samples << hw->info.shift);
         return -1;
     }
 
     le_store (hdr + 22, hw->info.nchannels, 2);
     le_store (hdr + 24, hw->info.freq, 4);
-    le_store (hdr + 28, hw->info.freq << (bits16 + (nchannels == 2)), 4);
-    le_store (hdr + 32, 1 << (bits16 + (nchannels == 2)), 2);
+    le_store (hdr + 28, hw->info.freq << (bits16 + stereo), 4);
+    le_store (hdr + 32, 1 << (bits16 + stereo), 2);
 
     wav->f = fopen (conf.wav_path, "wb");
     if (!wav->f) {
@@ -175,7 +173,7 @@
     uint32_t rifflen = (wav->total_samples << stereo) + 36;
     uint32_t datalen = wav->total_samples << stereo;
 
-    if (!wav->f || !hw->active) {
+    if (!wav->f) {
         return;
     }
 
@@ -214,6 +212,15 @@
 }
 
 struct audio_option wav_options[] = {
+    {"FREQUENCY", AUD_OPT_INT, &conf.settings.freq,
+     "Frequency", NULL, 0},
+
+    {"FORMAT", AUD_OPT_FMT, &conf.settings.fmt,
+     "Format", NULL, 0},
+
+    {"DAC_FIXED_CHANNELS", AUD_OPT_INT, &conf.settings.nchannels,
+     "Number of channels (1 - mono, 2 - stereo)", NULL, 0},
+
     {"PATH", AUD_OPT_STR, &conf.wav_path,
      "Path to wave file", NULL, 0},
     {NULL, 0, NULL, NULL, NULL, 0}
diff --git a/hw/adlib.c b/hw/adlib.c
index 70de4ff..fa2a03d 100644
--- a/hw/adlib.c
+++ b/hw/adlib.c
@@ -53,6 +53,7 @@
 } conf = {0x220, 44100};
 
 typedef struct {
+    QEMUSoundCard card;
     int ticking[2];
     int enabled;
     int active;
@@ -70,7 +71,7 @@
 #endif
 } AdlibState;
 
-static AdlibState adlib;
+static AdlibState glob_adlib;
 
 static void adlib_stop_opl_timer (AdlibState *s, size_t n)
 {
@@ -90,7 +91,7 @@
         if (s->ticking[i]) {
             uint64_t delta;
 
-            delta = AUD_time_stamp_get_elapsed_usec_out (s->voice, &s->ats);
+            delta = AUD_get_elapsed_usec_out (s->voice, &s->ats);
             ldebug (
                 "delta = %f dexp = %f expired => %d\n",
                 delta / 1000000.0,
@@ -141,10 +142,11 @@
 
 static void timer_handler (int c, double interval_Sec)
 {
-    AdlibState *s = &adlib;
+    AdlibState *s = &glob_adlib;
     unsigned n = c & 1;
 #ifdef DEBUG
     double interval;
+    int64_t exp;
 #endif
 
     if (interval_Sec == 0.0) {
@@ -262,16 +264,23 @@
 
     s->active = 0;
     s->enabled = 0;
+    AUD_remove_card (&s->card);
 }
 
-void Adlib_init (void)
+int Adlib_init (AudioState *audio)
 {
-    AdlibState *s = &adlib;
+    AdlibState *s = &glob_adlib;
+    audsettings_t as;
+
+    if (!audio) {
+        dolog ("No audio state\n");
+        return -1;
+    }
 
 #ifdef HAS_YMF262
     if (YMF262Init (1, 14318180, conf.freq)) {
         dolog ("YMF262Init %d failed\n", conf.freq);
-        return;
+        return -1;
     }
     else {
         YMF262SetTimerHandler (0, timer_handler, 0);
@@ -281,7 +290,7 @@
     s->opl = OPLCreate (OPL_TYPE_YM3812, 3579545, conf.freq);
     if (!s->opl) {
         dolog ("OPLCreate %d failed\n", conf.freq);
-        return;
+        return -1;
     }
     else {
         OPLSetTimerHandler (s->opl, timer_handler, 0);
@@ -289,18 +298,23 @@
     }
 #endif
 
+    as.freq = conf.freq;
+    as.nchannels = SHIFT;
+    as.fmt = AUD_FMT_S16;
+
+    AUD_register_card (audio, "adlib", &s->card);
+
     s->voice = AUD_open_out (
+        &s->card,
         s->voice,
         "adlib",
         s,
         adlib_callback,
-        conf.freq,
-        SHIFT,
-        AUD_FMT_S16
+        &as
         );
     if (!s->voice) {
         Adlib_fini (s);
-        return;
+        return -1;
     }
 
     s->samples = AUD_get_buffer_size_out (s->voice) >> SHIFT;
@@ -310,7 +324,7 @@
         dolog ("not enough memory for adlib mixing buffer (%d)\n",
                s->samples << SHIFT);
         Adlib_fini (s);
-        return;
+        return -1;
     }
 
     register_ioport_read (0x388, 4, 1, adlib_read, s);
@@ -321,4 +335,6 @@
 
     register_ioport_read (conf.port + 8, 2, 1, adlib_read, s);
     register_ioport_write (conf.port + 8, 2, 1, adlib_write, s);
+
+    return 0;
 }
diff --git a/hw/es1370.c b/hw/es1370.c
index 0191f5f..fc7ac0a 100644
--- a/hw/es1370.c
+++ b/hw/es1370.c
@@ -265,6 +265,7 @@
 typedef struct ES1370State {
     PCIDevice *pci_dev;
 
+    QEMUSoundCard card;
     struct chan chan[NB_CHANNELS];
     SWVoiceOut *dac_voice[2];
     SWVoiceIn *adc_voice;
@@ -341,11 +342,11 @@
         d->scount = 0;
         d->leftover = 0;
         if (i == ADC_CHANNEL) {
-            AUD_close_in (s->adc_voice);
+            AUD_close_in (&s->card, s->adc_voice);
             s->adc_voice = NULL;
         }
         else {
-            AUD_close_out (s->dac_voice[i]);
+            AUD_close_out (&s->card, s->dac_voice[i]);
             s->dac_voice[i] = NULL;
         }
     }
@@ -417,28 +418,32 @@
                     (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8,
                     d->shift);
             if (new_freq) {
+                audsettings_t as;
+
+                as.freq = new_freq;
+                as.nchannels = 1 << (new_fmt & 1);
+                as.fmt = (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8;
+
                 if (i == ADC_CHANNEL) {
                     s->adc_voice =
                         AUD_open_in (
+                            &s->card,
                             s->adc_voice,
                             "es1370.adc",
                             s,
                             es1370_adc_callback,
-                            new_freq,
-                            1 << (new_fmt & 1),
-                            (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8
+                            &as
                             );
                 }
                 else {
                     s->dac_voice[i] =
                         AUD_open_out (
+                            &s->card,
                             s->dac_voice[i],
                             i ? "es1370.dac2" : "es1370.dac1",
                             s,
                             i ? es1370_dac2_callback : es1370_dac1_callback,
-                            new_freq,
-                            1 << (new_fmt & 1),
-                            (new_fmt & 2) ? AUD_FMT_S16 : AUD_FMT_U8
+                            &as
                             );
                 }
             }
@@ -761,7 +766,7 @@
         while (temp) {
             int acquired, to_copy;
 
-            to_copy = audio_MIN (temp, sizeof (tmpbuf));
+            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
             acquired = AUD_read (s->adc_voice, tmpbuf, to_copy);
             if (!acquired)
                 break;
@@ -779,7 +784,7 @@
         while (temp) {
             int copied, to_copy;
 
-            to_copy = audio_MIN (temp, sizeof (tmpbuf));
+            to_copy = audio_MIN ((size_t) temp, sizeof (tmpbuf));
             cpu_physical_memory_read (addr, tmpbuf, to_copy);
             copied = AUD_write (voice, tmpbuf, to_copy);
             if (!copied)
@@ -812,7 +817,7 @@
     else {
         d->frame_cnt = size;
 
-        if (cnt <= d->frame_cnt)
+        if ((uint32_t) cnt <= d->frame_cnt)
             d->frame_cnt |= cnt << 16;
     }
 
@@ -876,6 +881,10 @@
     PCIES1370State *d = (PCIES1370State *) pci_dev;
     ES1370State *s = &d->es1370;
 
+    (void) region_num;
+    (void) size;
+    (void) type;
+
     register_ioport_write (addr, 0x40 * 4, 1, es1370_writeb, s);
     register_ioport_write (addr, 0x40 * 2, 2, es1370_writew, s);
     register_ioport_write (addr, 0x40, 4, es1370_writel, s);
@@ -923,13 +932,13 @@
         qemu_get_be32s (f, &d->frame_cnt);
         if (i == ADC_CHANNEL) {
             if (s->adc_voice) {
-                AUD_close_in (s->adc_voice);
+                AUD_close_in (&s->card, s->adc_voice);
                 s->adc_voice = NULL;
             }
         }
         else {
             if (s->dac_voice[i]) {
-                AUD_close_out (s->dac_voice[i]);
+                AUD_close_out (&s->card, s->dac_voice[i]);
                 s->dac_voice[i] = NULL;
             }
         }
@@ -953,12 +962,22 @@
     es1370_reset (s);
 }
 
-int es1370_init (PCIBus *bus)
+int es1370_init (PCIBus *bus, AudioState *audio)
 {
     PCIES1370State *d;
     ES1370State *s;
     uint8_t *c;
 
+    if (!bus) {
+        dolog ("No PCI bus\n");
+        return -1;
+    }
+
+    if (!audio) {
+        dolog ("No audio state\n");
+        return -1;
+    }
+
     d = (PCIES1370State *) pci_register_device (bus, "ES1370",
                                                 sizeof (PCIES1370State),
                                                 -1, NULL, NULL);
@@ -1002,6 +1021,8 @@
     pci_register_io_region (&d->dev, 0, 256, PCI_ADDRESS_SPACE_IO, es1370_map);
     register_savevm ("es1370", 0, 1, es1370_save, es1370_load, s);
     qemu_register_reset (es1370_on_reset, s);
+
+    AUD_register_card (audio, "es1370", &s->card);
     es1370_reset (s);
     return 0;
 }
diff --git a/hw/pc.c b/hw/pc.c
index 90a0e48..324f536 100644
--- a/hw/pc.c
+++ b/hw/pc.c
@@ -601,19 +601,23 @@
     DMA_init(0);
 
     if (audio_enabled) {
-        AUD_init();
-        if (sb16_enabled)
-            SB16_init ();
+        AudioState *audio;
+
+        audio = AUD_init();
+        if (audio) {
+            if (sb16_enabled)
+                SB16_init (audio);
 #ifdef CONFIG_ADLIB
-        if (adlib_enabled)
-            Adlib_init ();
+            if (adlib_enabled)
+                Adlib_init (audio);
 #endif
 #ifdef CONFIG_GUS
-        if (gus_enabled)
-            GUS_init ();
+            if (gus_enabled)
+                GUS_init (audio);
 #endif
-        if (pci_enabled && es1370_enabled)
-            es1370_init (pci_bus);
+            if (pci_enabled && es1370_enabled)
+                es1370_init (pci_bus, audio);
+        }
     }
 
     floppy_controller = fdctrl_init(6, 2, 0, 0x3f0, fd_table);
diff --git a/hw/sb16.c b/hw/sb16.c
index e79d192..4414af3 100644
--- a/hw/sb16.c
+++ b/hw/sb16.c
@@ -53,6 +53,7 @@
 } conf = {5, 4, 5, 1, 5, 0x220};
 
 typedef struct SB16State {
+    QEMUSoundCard card;
     int irq;
     int dma;
     int hdma;
@@ -108,9 +109,6 @@
     uint8_t mixer_regs[256];
 } SB16State;
 
-/* XXX: suppress that and use a context */
-static struct SB16State dsp;
-
 static void SB_audio_callback (void *opaque, int free);
 
 static int magic_of_irq (int irq)
@@ -242,15 +240,21 @@
             s->block_size, s->dma_auto, s->fifo, s->highspeed);
 
     if (s->freq) {
+        audsettings_t as;
+
         s->audio_free = 0;
+
+        as.freq = s->freq;
+        as.nchannels = 1 << s->fmt_stereo;
+        as.fmt = s->fmt;
+
         s->voice = AUD_open_out (
+            &s->card,
             s->voice,
             "sb16",
             s,
             SB_audio_callback,
-            s->freq,
-            1 << s->fmt_stereo,
-            s->fmt
+            &as
             );
     }
 
@@ -330,15 +334,21 @@
     }
 
     if (s->freq) {
+        audsettings_t as;
+
         s->audio_free = 0;
+
+        as.freq = s->freq;
+        as.nchannels = 1 << s->fmt_stereo;
+        as.fmt = s->fmt;
+
         s->voice = AUD_open_out (
+            &s->card,
             s->voice,
             "sb16",
             s,
             SB_audio_callback,
-            s->freq,
-            1 << s->fmt_stereo,
-            s->fmt
+            &as
             );
     }
 
@@ -349,7 +359,7 @@
 static inline void dsp_out_data (SB16State *s, uint8_t val)
 {
     ldebug ("outdata %#x\n", val);
-    if (s->out_data_len < sizeof (s->out_data)) {
+    if ((size_t) s->out_data_len < sizeof (s->out_data)) {
         s->out_data[s->out_data_len++] = val;
     }
 }
@@ -1018,6 +1028,7 @@
 static IO_WRITE_PROTO(mixer_write_indexb)
 {
     SB16State *s = opaque;
+    (void) nport;
     s->mixer_nreg = val;
 }
 
@@ -1025,10 +1036,8 @@
 {
     SB16State *s = opaque;
 
+    (void) nport;
     ldebug ("mixer_write [%#x] <- %#x\n", s->mixer_nreg, val);
-    if (s->mixer_nreg > sizeof (s->mixer_regs)) {
-        return;
-    }
 
     switch (s->mixer_nreg) {
     case 0x00:
@@ -1088,6 +1097,8 @@
 static IO_READ_PROTO(mixer_read)
 {
     SB16State *s = opaque;
+
+    (void) nport;
 #ifndef DEBUG_SB16_MOST
     if (s->mixer_nreg != 0x82) {
         ldebug ("mixer_read[%#x] -> %#x\n",
@@ -1111,11 +1122,12 @@
 
     while (temp) {
         int left = dma_len - dma_pos;
-        int to_copy, copied;
+        int copied;
+        size_t to_copy;
 
         to_copy = audio_MIN (temp, left);
-        if (to_copy > sizeof(tmpbuf)) {
-            to_copy = sizeof(tmpbuf);
+        if (to_copy > sizeof (tmpbuf)) {
+            to_copy = sizeof (tmpbuf);
         }
 
         copied = DMA_read_memory (nchan, tmpbuf, dma_pos, to_copy);
@@ -1308,21 +1320,27 @@
     qemu_get_buffer (f, s->mixer_regs, 256);
 
     if (s->voice) {
-        AUD_close_out (s->voice);
+        AUD_close_out (&s->card, s->voice);
         s->voice = NULL;
     }
 
     if (s->dma_running) {
         if (s->freq) {
+            audsettings_t as;
+
             s->audio_free = 0;
+
+            as.freq = s->freq;
+            as.nchannels = 1 << s->fmt_stereo;
+            as.fmt = s->fmt;
+
             s->voice = AUD_open_out (
+                &s->card,
                 s->voice,
                 "sb16",
                 s,
                 SB_audio_callback,
-                s->freq,
-                1 << s->fmt_stereo,
-                s->fmt
+                &as
                 );
         }
 
@@ -1332,13 +1350,25 @@
     return 0;
 }
 
-void SB16_init (void)
+int SB16_init (AudioState *audio)
 {
-    SB16State *s = &dsp;
+    SB16State *s;
     int i;
     static const uint8_t dsp_write_ports[] = {0x6, 0xc};
     static const uint8_t dsp_read_ports[] = {0x6, 0xa, 0xc, 0xd, 0xe, 0xf};
 
+    if (!audio) {
+        dolog ("No audio state\n");
+        return -1;
+    }
+
+    s = qemu_mallocz (sizeof (*s));
+    if (!s) {
+        dolog ("Could not allocate memory for SB16 (%d bytes)\n",
+               sizeof (*s));
+        return -1;
+    }
+
     s->cmd = -1;
     s->irq = conf.irq;
     s->dma = conf.dma;
@@ -1356,7 +1386,7 @@
     reset_mixer (s);
     s->aux_ts = qemu_new_timer (vm_clock, aux_timer, s);
     if (!s->aux_ts) {
-        dolog ("Can not create auxiliary timer\n");
+        dolog ("warning: Could not create auxiliary timer\n");
     }
 
     for (i = 0; i < LENOFA (dsp_write_ports); i++) {
@@ -1377,4 +1407,6 @@
     s->can_write = 1;
 
     register_savevm ("sb16", 0, 1, SB_save, SB_load, s);
+    AUD_register_card (audio, "sb16", &s->card);
+    return 0;
 }
diff --git a/qemu-doc.texi b/qemu-doc.texi
index 51875d7..1c5b5b7 100644
--- a/qemu-doc.texi
+++ b/qemu-doc.texi
@@ -95,12 +95,21 @@
 @item
 Serial ports
 @item
-Soundblaster 16 card
+Creative SoundBlaster 16 sound card
+@item
+ENSONIQ AudioPCI ES1370 sound card
+@item
+Adlib(OPL2) - Yamaha YM3812 compatible chip
 @end itemize
 
+Note that adlib is only available when QEMU was configured with
+-enable-adlib
+
 QEMU uses the PC BIOS from the Bochs project and the Plex86/Bochs LGPL
 VGA BIOS.
 
+QEMU uses YM3812 emulation by Tatsuyuki Satoh.
+
 @c man end
 
 @section Quick Start
diff --git a/vl.c b/vl.c
index d551a4e..810db81 100644
--- a/vl.c
+++ b/vl.c
@@ -2842,10 +2842,11 @@
 	   "-k language     use keyboard layout (for example \"fr\" for French)\n"
 #endif
 #ifdef HAS_AUDIO
-           "-enable-audio   enable audio support\n"
+           "-enable-audio   enable audio support, and all the sound cars\n"
            "-audio-help     print list of audio drivers and their options\n"
-           "-soundhw c1,... comma separated list of sound card names\n"
-           "                use -soundhw ? to get the list of supported sound cards\n"
+           "-soundhw c1,... enable audio support\n"
+           "                and only specified sound cards (comma separated list)\n"
+           "                use -soundhw ? to get the list of supported cards\n"
 #endif
            "-localtime      set the real time clock to local time [default=utc]\n"
            "-full-screen    start in full screen\n"
@@ -3145,9 +3146,9 @@
         printf ("sb16       Creative Sound Blaster 16\n");
 #ifdef CONFIG_ADLIB
 #ifdef HAS_YMF262
-        printf ("adlib      Ymaha YMF262 (OPL3)\n");
+        printf ("adlib      Yamaha YMF262 (OPL3)\n");
 #else
-        printf ("adlib      Ymaha YM3812 (OPL2)\n");
+        printf ("adlib      Yamaha YM3812 (OPL2)\n");
 #endif
 #endif
 #ifdef CONFIG_GUS
diff --git a/vl.h b/vl.h
index e1a2ec3..72d4f87 100644
--- a/vl.h
+++ b/vl.h
@@ -631,16 +631,16 @@
                    SetIRQFunc *set_irq, void *irq_opaque, int irq);
 
 /* es1370.c */
-int es1370_init (PCIBus *bus);
+int es1370_init (PCIBus *bus, AudioState *s);
 
 /* sb16.c */
-void SB16_init (void);
+int SB16_init (AudioState *s);
 
 /* adlib.c */
-void Adlib_init (void);
+int Adlib_init (AudioState *s);
 
 /* gus.c */
-void GUS_init (void);
+int GUS_init (AudioState *s);
 
 /* dma.c */
 typedef int (*DMA_transfer_handler) (void *opaque, int nchan, int pos, int size);